mmap系统调用(功能)

void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset)

内存映射函数mmap , 负责把文件内容映射到进程的虚拟内存空间,通过对这段内存的读取和修改,来实现对文件的读取和修改,而不需要再调用read, write等操作。

addr:        指定映射的起始地址,通常设为NULL, 由系统指定。

len:          映射到内存的文件长度

prot:      映射区的保护方式, 可以是:

PROT_EXEC:  映射区可被执行

PROT_READ:  映射区可被读取

PROT_WRITE: 映射区可被写入

flags:        映射区的特性,可以是:

MAP_SHARED:

写入映射区的数据会复制回文件,且允许其他映射该文件的进程共享

MAP_PRIVATE:

对映射区的写入操作会产生一个映射区的复制(copy-on-write), 对此区域所做的修改不会写回原文件。

fd:            由open返回的文件描述符,代表要映射的文件

offset:      以文件开始处的偏移量,必须是分页大小的整数倍,通常为0,表示从文件开头映射

解除映射

int munmap(void *start, size_t length)

功能:

取消参数start所指向的映射内存,参数length表示欲取消的内存大小

返回值:

解除成功返回0,否则返回-1,错误原因存于errno中。

注意:

mmap      不影响原文件的长度,如果写入的长度超出了原文件的长度,那么就只能写入原文件的长度大小的数据内容

代码:

#include <stdio.h>

#include <string.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <unistd.h>

#include <sys/mman.h>

int main(void)

-{

|    int fd;

|    char *start=NULL;

|    char buf[100];

|

|    fd = open("t.txt", O_RDWR | O_CREAT);

|    start = mmap(NULL, 100, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);

|

|    strcpy(buf, start);

|    printf("buf is %s\r\n", buf);

|

|    strcpy(start, "sky is hello");

|    munmap(start, 100);

|    close(fd);

|

|    return 0;

|}

虚拟内存区域

虚拟内存区域是进程的虚拟地址空间中的一个同质区间,即具有同样特性的连续地址

范围。一个进程的内存映像由下面几部分组成:程序代码、数据、BSS和栈区域,以及内存

映射的区域。

一个进程的内存区域可以通过 /proc/pid/maps来查看

例如:cat   /proc/6/maps

每一行的域为:

Start_end perm offset major:minor inode

Start: 该区域起始虚拟地址

End:该区域结束虚拟地址

Perm:读、写和执行权限;表示对这个区域,允许进程做什么。这个域的最后一个字符要么是p表示私有的,要么是s表示共享的

Offset: 被映射部分在文件中的起始地址

Major、minor: 主次设备号

Inode: 索引结点

Linux内核使用结构vm_area_struct

(<linux/mm_types.h>)来描述虚拟内存区域,其中几个主要成员如下:

unsigned long vm_start   虚拟内存区域起始地址

unsigned long vm_end    虚拟内存区域结束地址

unsigned long vm_flags    该区域的标记。如:VM_IO和VM_RESERVED。

VM_IO将该VMA标记为内存映射的IO区域,VM_IO会阻止系统将该区域包含在进程的存放转存(core dump)中, VM_RESERVED 标志内存区域不能被换出。

mmap设备操作

映射一个设备是指把用户空间的一段地址关联到设备内存上。当程序读写这段用户空间的地址时,它实际上是在访问设备。

内存管理单元去关联的,通过页式管理关联的,具体如何关联的去了解linux内存管理吧。

mmap设备方法需要完成什么功能?

Mmap方法是file_oprations结构的成员,在mmap系统调用发出时被调用。在此之前,内核已经完成了很多工作。Mmap设备方法所需要做的就是建立虚拟地址到物理地址的页表。

int  (*mmap) (struct file*, struct vm_area_struct *)

mmap如何完成页表的建立?

方法有二:

1、  使用remap_pfn_range一次建立所有页表;

2、  使用nopage  VMA方法每次建立一个页表

构造页表的工作可由remap_pfn_range函数完成,原型如下:

int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr, unsigned long pfn, unsigned long size, pgprot_t prot)

vma:                         虚拟内存区域指针

virt_addr:        虚拟地址的起始值

pfn:                          要映射的物理地址所在的物理页帧号,可将物理地址>>PAGE_SHIFT得到,这个宏定义是12,就是除以4KB

size:              要映射的区域的大小

prot:              VMA的保护属性

例子:

int memdev_mmap(struct file *filp, struct vm_area_struct *vma)

{

//设置保护属性

vma->vm_flags |= VM_IO;

vma->vm_flags |= VM_RESERVED;

if (remap_pfm_range(vma, vma->vma-start, virt_to_phys(dev->data)>>PAGE_SHIFT,

size, vma->vm_page_prot))

return –EAGAIN;

return 0;

}

Dev-data是虚拟地址,就要用virt_to_phys转换为物理地址,如果是物理地址,那就不用转化了。

这样就实现了mmap的设备操作

欢迎交流
如有转载请注明出处

新浪博客:http://blog.sina.com.cn/u/2049150530
博客园:http://www.cnblogs.com/sky-heaven/
知乎:http://www.zhihu.com/people/zhang-bing-hua

Linux内核驱动--mmap设备方法【原创】的更多相关文章

  1. linux内核驱动模型

    linux内核驱动模型,以2.6.32内核为例.(一边写一边看的,有点乱.) 1.以内核对象为基础.用kobject表示,相当于其它对象的基类,是构建linux驱动模型的关键.具有相同类型的内核对象构 ...

  2. linux 内核驱动--Platform Device和Platform_driver注册过程

    linux 内核驱动--Platform Device和Platform_driver注册过程 从 Linux 2.6 起引入了一套新的驱动管理和注册机制 :Platform_device 和 Pla ...

  3. 【引用】Linux 内核驱动--多点触摸接口

    本文转载自James<Linux 内核驱动--多点触摸接口>   译自:linux-2.6.31.14\Documentation\input\multi-touch-protocol.t ...

  4. Linux内核驱动开发之KGDB原理介绍及kgdboe方式配置

    接博文<Linux内核驱动开发之KGDB单步调试内核(kgdboc方式)>.上篇博文中,仅简单介绍使用串口的Kgbd的流程(kgdboc方式),本文将重点介绍KGDB调试Linux内核的原 ...

  5. 嵌入式C语言自我修养 02:Linux 内核驱动中的指定初始化

    2.1 什么是指定初始化 在标准 C 中,当我们定义并初始化一个数组时,常用方法如下: ] = {,,,,,,,,}; 按照这种固定的顺序,我们可以依次给 a[0] 和 a[8] 赋值.因为没有对 a ...

  6. Linux内核源码分析方法_转

    Linux内核源码分析方法 转自:http://www.cnblogs.com/fanzhidongyzby/archive/2013/03/20/2970624.html 一.内核源码之我见 Lin ...

  7. Linux内核驱动学习(八)GPIO驱动模拟输出PWM

    文章目录 前言 原理图 IO模拟输出PWM 设备树 驱动端 调试信息 实验结果 附录 前言 上一篇的学习中介绍了如何在用户空间直接操作GPIO,并写了一个脚本可以产生PWM.本篇的学习会将写一个驱动操 ...

  8. Linux内核驱动学习(六)GPIO之概览

    文章目录 前言 功能 如何使用 设备树 API 总结 前言 GPIO(General Purpose Input/Output)通用输入/输出接口,是十分灵活软件可编程的接口,功能强大,十分常用,SO ...

  9. linux内核中创建线程方法

    1.头文件 #include <linux/sched.h> //wake_up_process() #include <linux/kthread.h> //kthread_ ...

随机推荐

  1. 自学Linux Shell12.6-嵌套循环for命令

    点击返回 自学Linux命令行与Shell脚本之路 12.6-嵌套循环for命令 嵌套循环就是在一个循环中还有一个循环. 内部循环在外部循环体中,在外部循环的每次执行过程中都会触发内部循环,直到内部循 ...

  2. 【转】stm32CubeMx上移植自己的printf()和scanf()函数

    要想printf()和scanf() 函数工作,我们需要把printf()和scanf() 重新定向到串口中.重定向是指用户可以自己重写C 的库函数,当连接器检查到用户编写了与C 库函数相同名字的函数 ...

  3. cf1000C Covered Points Count (差分+map)

    考虑如果数字范围没有这么大的话,直接做一个差分数组就可以了 但现在变大了 所以要用一个map来维护 #include<bits/stdc++.h> #define pa pair<i ...

  4. luogu1377 树的序 (线段树)

    题意:给你一个1~N的排列,然后让你按顺序把它们插到一个二叉搜索树里,然后问能插出同样的二叉搜索树的 字典序最小的排列是什么 本来可以直接模拟建树然后dfs一下输出结果...然而有可能会退化成链,最差 ...

  5. luogu1972 HH的项链(树状数组)

    无修改.询问区间种类数的问题可以很容易地用树状数组解决 我们先给询问按右端点排序,然后推着做,每次让a[i]++,表示i处新增了一个种类 但是这样会和前面的有重复,我们只要记下每个种类上次在哪里出现过 ...

  6. UVALive - 6439(思维题)

    题目链接:https://vjudge.net/contest/241341#problem/F 题目大意:给你一个字符串,你可以用任意单个字符代替其中的一个子串,使它形成一个回文串,要求形成的回文串 ...

  7. linux动态库与静态库混合连接

      1, 在应用程序需要连接外部库的情况下,linux默认对库的连接是使用动态库,在找不到动态库的情况下再选择静态库.使用方式为: gcc test.cpp -L. -ltestlib 如果当前目录有 ...

  8. ioi2018集训队自选题:最短路练习题

    题意:链接 定义pos[i]表示i这个值在数组里的下标. 我们先用单调栈找到每个元素左边和右边第一个比它大的元素$l_i$和$r_i$,然后建一棵二叉树,我们就叫做maxtree吧 (upd:mdzz ...

  9. CF815D Karen and Cards

    CF815D Karen and Cards 固定一维c,然后(a,b)看成坐标,矩形区域求交 1.Segment tree Beats! 2.改成不合法的区域就是求并,c反向枚举,区域只增不减且完全 ...

  10. 初见mobX

    先看如下的代码 const {observable}= mobox; const {observer}=mobxReact; const {Component}=React; const appSta ...