精华内容
下载资源
问答
  • mmap函数
    2021-05-14 12:12:32

    本文主要讲述mmap 函数的使用,与驱动中 mmap 函数的实现

    mmap 怎么使用,怎么实现,为什么 mmap 可以减少额外的拷贝?

    下面简单详情。

    一、 mmap 的使用#include void *mmap(void *addr, size_t len, int prot, int flag, int filedes, off_t off );int munmap(void *addr, size_t length);

    函数属于系统调用级别,负责内存映射。

    形容

    mmap

    把文件或者者设施映射到内存。这个函数在调用进程的虚拟地址空间中创立一块映射区域。

    addr 指定映射区域的首地址。假如 addr 是 NULL ,那么由内核来选择一个地址来创立映射的区域,否则创立的时候会尽可能地使用 addr 的地址。在linux系统中,创立映射的时候应该是在下一个页面的边界创立, addr 是 NULL 的时候,程序的可移植性最好。

    len 指定文件被映射的长度, 或者者映射区域的长度。

    offset 指定从文件的哪个偏移位置开始映射。=offset= 必需是页面大小的整数倍页面的大小可以由 sysconf(_SC_PAGE_SIZE) 来返回.

    prot 指定内存的保护模式(具体参见 man ), flags 指定区域在不同进程之间的共享方式,以及区域能否同步到相应的文件等等(具体参见 man ).

    这个函数返回新创立的页面的地址。

    munmap

    取消 address 指定地址范围的映射。以后再引用取消的映射的时候就会导致非法内存的访问。

    这里 address 应该是页面的整数倍, length 指定取消映射的地址长度。

    成功的时候这个函数返回0, 失败的时候,两者都返回-1.

    举例//hello hello hello/*程序功能: * 1)主要测试mmap和munmap的2)简单的write * 具体为: * 在命令中分别指定文件名,映射的长度,映射的起始地址. * 将文件映射到内存中 * 把映射到内存中的内容用write写到标准输出。 * 注意,这里没有对越界进行检测。 * */#include //mmap#include //sysconf#include //file open#include //printfint main(int argc, char *argv[]){ if(argc != 4) { write(STDOUT_FILENO,"hello\n",6); printf("usage:%s \n",argv[0]); return 1; } char *filename = argv[1];//1)指定文件 printf("the file to be mapped is:%s\n",filename); int fd = open(filename,O_RDONLY); int offset = atoi(argv[2]);//2)指定映射起始地址(页面的整数倍) printf("start offset of file to be mapped is:%d\n",offset); printf("page size is:%ld\n",sysconf(_SC_PAGE_SIZE)); int realOffset = offset & ~(sysconf(_SC_PAGE_SIZE) - 1);//转换成页面的整数倍 printf("real start offset of file to be mapped is:%d\n",realOffset); int length = atoi(argv[3]);//3)指定映射长度 printf("the length to be map is:%d\n",length); int realLen = length+offset-realOffset;//实际写入的字节数 printf("the real length to be map is:%d\n",realLen); //mmap的参数分别是: //NULL,让内核自己选择映射的地址;realLen指定映射的长度; //PROT_READ只读;MAP_PRIVATE不和其余的进程之间共享映射区域,数据也不写入对应的文件中; //realOffset映射文件的起始地址(页面的整数倍)。 char *addr = mmap(NULL, realLen,PROT_READ,MAP_PRIVATE,fd,realOffset);//4)开始映射 //关闭打开的文件,实际程序退出的时候会自动关闭。 //关闭文件之后,相应的映射内存依旧存在,映射的内存用munmap关闭。 close(fd); //write的参数分别是: //STDOUT_FILENO:文件形容符号(这里是标准输出) //addr,将要写入文件的内容的地址 //realLen,写入的长度,长度以addr作为起始地址 write(STDOUT_FILENO,addr,realLen);//将映射的内容写到标准输出 munmap(addr,realLen);//5)关闭映射的内存 //write(STDOUT_FILENO,addr,realLen);//不能使用了 printf("\n");}

    二、 mmap 实现

    驱动中有对 mmap 的具体实现。客户调用 mmap 系统调用函数之后,最终会调用到驱动中的 mmap 函数接口。下面是一个例子:static int commdrv_mmap(struct file* file, struct vm_area_struct* vma){ long phy_addr; unsigned long offset; unsigned long size; vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); vma->vm_flags |= VM_LOCKED; offset = vma->vm_pgoff << PAGE_SHIFT;/*XXX assume is 12*/ size = vma->vm_end - vma->vm_start; if(BUF0_OFF == offset) { phy_addr = PHYS_BASE0; } else if(BUF1_OFF == offset) { phy_addr = PHYS_BASE1; } else if(START_OFF == offset) { phy_addr = PHYS_BASE; } else { return -ENXIO; } /*phy_addr must be 4k *n*/ if(remap_pfn_range(vma, vma->vm_start, phy_addr >> PAGE_SHIFT, size, vma->vm_page_prot)) { return -ENXIO; } return 0;}

    对于以上代码,

    vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 表示要映射的内存是非cached的,这样不会存在缓存中的数据和实际数据不一致的情况,但是速度会比cached的要慢。

    offset 表示要映射的数据偏移,来自客户空间的 mmap 调用,在这里进行判断,尽管一般的文件就将这个偏移量做为文件偏移了,其实这个 offset 的含义,由驱动自己解释,不肯定就是字节偏移,驱动根据这个偏移量来决定映射哪块内存。

    size 表示要映射的内存的大小。

    remap_pfn_range(vma, vma->vm_start, phy_addr >> PAGE_SHIFT, size, vma->vm_page_prot) 表示将根据被映射的物理地址,以及虚拟起始地址,和大小等信息,将相应的部分映射到客户空间。

    其中参数vma直接来自 commdrv_mmap 函数的参数, phy_addr 是要映射的设施的物理地址(必需是页对齐的),只有一些的信息自定义,大多来自外部。最后映射的地址,通过客户调用的 mmap 函数返回,客户可以直接操作。

    三、 mmap 优点

    mmap 实现了将设施驱动在内核空间的部分地址直接映射到客户空间,使得客户程序可以直接访问和操作相应的内容。减少了额外的拷贝,而一般的 read , write 函数尽管表面上直接向设施写入,其实还需要进行一次拷贝。

    例如,下面是某个设施驱动中的的 write 实现,当外面客户程序调用 write 系统调用向相应设施文件写之后,最终会进入到这个函数进行真正的读取所需操作。static ssize_t commdrv_write(struct file* filp, char __user* buf, size_t count, loff_t* ppos){ char* wbuf; wbuf = (char*)vmalloc(count); if(!wbuf) { return 0; } ret = copy_from_user(wbuf, (char __user*)buf, count); if(0 != ret) { vfree(wbuf); return 0; } .....do others things with wbuf...... vfree(wbuf); return count;}

    由上面的代码可知,客户传入的数据指针 buf ,在驱动中(也就是内核空间)不能直接访问,必需使用 copy_from_user 将其拷贝到内核空间的一块内存,而后才能进行后续的操作(内核中不能不经过 copy_from_user ,直接访问客户传下来的指针 buf 的地址的内容)。而 mmap ,使得将内核空间直接映射到了客户空间,让客户空间通过返回的指针直接访问,这样内核和客户空间直接操作同样的内存。也就是说,假如不使用 mmap ,那么因为在内核空间的代码,和外面客户空间的代码对应的地址空间不同,这样内核空间和客户空间不能互相访问其指针;假如想要访问,对方指针的内容,那么只能通过 copy_from_user 之类的函数先将其数据拷贝到内核空间(相应的 read 一般使用 copy_to_user 可以将内核空间内的指针数据拷贝给客户空间的指针所指)再访问。除非直接将内存映射,否则肯定要拷贝才能访问客户空间数据。

    四、其它

    参考: http://blog.chinaunix.net/uid-9525959-id-3063123.html

    更多相关内容
  • mmap函数

    2020-10-26 21:13:02
    mmap函数 1.创建映射区 void *mmap(void *adrr,size_t length,int prot,int flags,int fp,off_t offset); addr:建立映射区的首地址,直接传NULL length:映射区的大小 prot:映射区权限PROT_READ,PROT_WRITE,PROT_...

    mmap函数

    1.创建映射区
    void *mmap(void *adrr,size_t length,int prot,int flags,int fp,off_t offset);
    
    addr:建立映射区的首地址,直接传NULL
    length:映射区的大小
    prot:映射区权限PROT_READ,PROT_WRITE,PROT_READ|PROT_WRITE
    flags:标志位参数,MAP_SHARED将映射区所做的操作反映到物流设备(磁盘)上(父子进程可以共享映射区)
    MAP_PRIVATE不反映(父子进程不共享映射区,各自独占)。
    offset:映射文件的偏移(4k的整数倍)为0时表示起始位置。
    
    成功:返回映射区的首地址,失败:返回MAP_FAILED
    
    • 新建立的文件不能创建映射区,也就是说,不能创建大小为0的映射区
    • 可以在堆区开辟大小为0的空间(void *malloc(unsigned int size));
    • 映射区的权限要 <= 打开文件的权限
    • 映射区创建的过程中隐含一次对文件的读操作
    • 父子进程共享mmap建立的映射区,和打开的文件
    • 当mmap中的flags参数设置成MAP_PRIVATE时,父子进程各自独占映射区,不共享。
    2.关闭映射区
    int munmap(void *addr,size_t length);
    
    ```c
    #include<stdio.h>
    #include<fcntl.h>
    #include<unistd.h>
    #include<stdlib.h>
    #include<sys/mman.h>
    #include<string.h>
    
    int main()
    {
            int fp = open("test",O_RDWR | O_CREAT,0644);
            if(fp == -1)
            {
                    perror("open error:");
                    exit(1);
            }
            int file_len = truncate("test",1024);
            if(file_len == -1)
            {
                    perror("truncate error");
                    exit(1);
             }
            char *mp = NULL;
            mp = mmap(NULL,1024,PROT_READ | PROT_WRITE,MAP_SHARED,fp,0);
            if(mp == MAP_FAILED)
            {
                    perror("mmap error:");
                    exit(1);
            }
            else
            {
                    strcpy(mp,"6666666666666666666\n");
                    int nump = munmap(mp,1024);
                    if(nump == -1)
                    {
                            perror("munmap error:");
                            exit(1);
                    }
                    close(fp);
            }
            return 0;
    }
    
    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<fcntl.h>
    #include<sys/mman.h>
    #include<sys/wait.h>
    
    int variety = 666;
    
    int main()
    {
            int fp = open("test",O_RDWR | O_CREAT | O_TRUNC,0644);
            if(fp == -1)
            {
                    perror("open error:");
                    exit(1);
            }
            ftruncate(fp,512);
            int len = lseek(fp,0,SEEK_END);
            printf("the length of test is:%d\n",len);
            int *mp;
            mp = mmap(NULL,len,PROT_READ | PROT_WRITE,MAP_SHARED,fp,0);
            if(mp == MAP_FAILED)
            {
                    perror("mmap error:");
                    exit(1);
            }
            close(fp);
    		int ul = unlink("test");
            if(ul == 0)
            {
                    printf("file is deleted successful\n");
            }
            pid_t pid = fork();
            if(pid == 0)
            {
                    printf("I am child\n");
                    *mp = 999;
                    variety = 888;
                    printf("*mp = %d,variety = %d\n",*mp,variety);
            }
            else
            {
            		sleep(1);
                    printf("I am father\n");
                    printf("*mp = %d,variety = %d\n",*mp,variety);
                    wait(NULL);
            }
            int numap = munmap(mp,len);
            if(numap == -1)
                    {
                            perror("numap error:");
                            exit(1);
                    }
            return 0;
    
    }
    

    匿名映射:

     - 无需依赖一个文件即可建立映射区
     - 在参数flags中加入:MAP_ANONYMOUS(ANON)
    
    ```cpp
    int*p=mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1,O);
    
    • 需注意的是,MAP_ANONYMOUS和MAP_ANON这两个宏是Linux操作系统特有的宏。在类Unix系统中如无宏定义,可使用如下两步来完成匿名映射区的建立。
    int fd=open("/dev/zero",O_RDWR);
    p=mmap(NULL,size,PROT_READ|PROT_WRITE,MMAP_SHARED,fd,0);
    
    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<fcntl.h>
    #include<sys/mman.h>
    #include<sys/wait.h>
    
    int variety = 666;
    
    int main()
    {
            int *mp;
            mp = mmap(NULL,128,PROT_READ | PROT_WRITE,MAP_SHARED | MAP_ANON,-1,0);
            if(mp == MAP_FAILED)
            {
                    perror("mmap error:");
                    exit(1);
            }
            pid_t pid = fork();
            if(pid == 0)
            {
                    printf("I am child\n");
                    *mp = 999;
                    variety = 888;
                    printf("*mp = %d,variety = %d\n",*mp,variety);
            }
            else
            {
                    sleep(1);
                    printf("I am father\n");
                    printf("*mp = %d,variety = %d\n",*mp,variety);
                    wait(NULL);
            }
            int numap = munmap(mp,128);
            if(numap == -1)
                    {
                            perror("numap error:");
                            exit(1);
                    }
            return 0;
     }
    

    总结:使用mmap时务必注意以下事项:

    1.创建映射区的过程中,隐含着一次对映射文件的读操作。
    2.当MAP_SHARED时,要求:映射区的权限应<=文件打开的权限(出于对映射区的保护)。而MAP_PRIVATE则无所谓,因为mmapf的权限是对内存的限制。
    3.映射区的释放与文件关闭无关。只要映射建立成功,文件可以立即关闭。
    4.特别注意,当映射文件大小为0时,不能创建映射区。所以:用于映射的文件必须要有实际大小!mmap.使用时常常会出现总线错误,通常是由于共享文件存储空间大小引起的。
    5.munmap.传入的地址一定是mmap的返回地址。坚决杜绝指针++/- -操作。
    6.offset文件偏移量必须为4K的整数倍
    7.mmap创建映射区出错概率非常高,一定要检查返回值,确保映射区建立成功再进行后续操作。
    8.因为创建mmap函数比较容易出错,所以创建完成后一定要通过判断返回值来确定是否创建成功

    将结构体写入文件中
    #include<sys/mman.h>
    #include<sys/wait.h>
    #include<string.h>
    #include<sys/types.h>
    #include<sys/stat.h>
    
    struct stu{
            int num;
            char name[16];
            int class;
    };
    
    int main(int argc,char *argv[])
    {
            struct stu student = {1,"zjl",11};
            if (argc<2)
            {
                    printf("argument is less\n");
                    exit(1);
            }
            int fp = open(argv[1],O_RDWR | O_CREAT | O_TRUNC,0644);
            int len = sizeof(student);
            printf("student len is:%d\n",len);
            ftruncate(fp,128);
            int length = lseek(fp,0,SEEK_END);
            printf("the len of fp is:%d\n",length);
    
            int *mp = mmap(NULL,len,PROT_WRITE | PROT_READ,MAP_SHARED,fp,0);
            if(mp == MAP_FAILED)
            {
                    perror("mmap error:");
                    exit(1);
            }
            close(fp);
            while(1)
            {
                    printf("enter while len is:%d\n",len);
                    printf("student.name = %s\n",student.name);
                    memcpy(mp,&student,64);
                    student.num++;
                    sleep(1);
            }
            int numap = munmap(mp,128);
            if(numap == -1)
                    {
                            perror("numap error:");
                            exit(1);
                    }
    
            return 0;
    }
    

    2个不相关进程间的通信

    先写入shared_file文件中
    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<fcntl.h>
    #include<sys/mman.h>
    #include<sys/wait.h>
    #include<string.h>
    #include<sys/types.h>
    #include<sys/stat.h>
    
    struct stu{
            int num;
            char name[16];
            int class;
    };
    
    int main(int argc,char *argv[])
    {
            struct stu student = {1,"zjl",11};
            int fp = open("sharerd_file",O_RDWR | O_CREAT | O_TRUNC,0644);
            int len = sizeof(student);
            printf("student len is:%d\n",len);
            ftruncate(fp,128);
    
            int *mp = mmap(NULL,len,PROT_WRITE | PROT_READ,MAP_SHARED,fp,0);
            if(mp == MAP_FAILED)
            {
                    perror("mmap error:");
                    exit(1);
            }
            close(fp);
            printf("start to write!!!\n");
            int n = 0;
    
            while(1)
            {
                    n += 1;
                    printf("[%d]write\n",n);
                    memcpy(mp,&student,64);
                    student.num++;
                    student.class++;
                    sleep(1);
            }
            int numap = munmap(mp,128);
            if(numap == -1)
                    {
                            perror("numap error:");
                            exit(1);
                    }
    
            return 0;
    }
    
    再从文件中读出
    #include<unistd.h>
    #include<fcntl.h>
    #include<sys/mman.h>
    #include<sys/wait.h>
    #include<string.h>
    #include<sys/types.h>
    #include<sys/stat.h>
    
    struct stu{
            int num;
            char name[16];
            int class;
    };
    
    int main(int argc,char *argv[])
    {
            struct stu student;
            int fp = open("sharerd_file",O_RDONLY);
    
            struct stu *mp = mmap(NULL,sizeof(student),PROT_READ,MAP_SHARED,fp,0);
            if(mp == MAP_FAILED)
            {
                    perror("mmap error:");
                    exit(1);
            }
    
            close(fp);
            printf("开始接受!!!\n");
            while(1)
            {
                    sleep(1);
                    printf("num = %d,name = %s,class = %d\n",mp->num,mp->name,mp->class);
            }
    
            return 0;
    }
    
    展开全文
  • mmap函数概述

    2020-10-26 12:31:07
    实现这样的映射关系后,进程就可以采用指针的方式读写操作这一段内存,而系统会自动回写脏页面到对应的文件磁盘上,即完成了对文件的操作而不必再调用read,write等系统调用函数。相反,内核空间对这段区域的修改也...

    mmap基础概念

    mmap, 函数名为memory map, 即地址的映射, 是一种内存映射文件的方法,将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系。实现这样的映射关系后,进程就可以采用指针的方式读写操作这一段内存,而系统会自动回写脏页面到对应的文件磁盘上,即完成了对文件的操作而不必再调用read,write等系统调用函数。相反,内核空间对这段区域的修改也直接反映用户空间,从而可以实现不同进程间的文件共享。如下图所示:
    在这里插入图片描述
    由上图可以看出,进程的虚拟地址空间,由多个虚拟内存区域构成。虚拟内存区域是进程的虚拟地址空间中的一个同质区间,即具有同样特性的连续地址范围。上图中所示的text数据段(代码段)、初始数据段、BSS数据段、堆、栈和内存映射,都是一个独立的虚拟内存区域。而为内存映射服务的地址空间处在堆栈之间的空余部分。

    linux内核使用vm_area_struct结构来表示一个独立的虚拟内存区域,由于每个不同质的虚拟内存区域功能和内部机制都不同,因此一个进程使用多个vm_area_struct结构来分别表示不同类型的虚拟内存区域。各个vm_area_struct结构使用链表或者树形结构链接,方便进程快速访问,如下图所示:
    在这里插入图片描述
    vm_area_struct结构中包含区域起始和终止地址以及其他相关信息,同时也包含一个vm_ops指针,其内部可引出所有针对这个区域可以使用的系统调用函数。这样,进程对某一虚拟内存区域的任何操作需要用要的信息,都可以从vm_area_struct中获得。mmap函数就是要创建一个新的vm_area_struct结构,并将其与文件的物理磁盘地址相连。

    mmap内存映射原理

    mmap内存映射的实现过程,总的来说可以分为三个阶段:

    (一)进程启动映射过程,并在虚拟地址空间中为映射创建虚拟映射区域

    1、进程在用户空间调用库函数mmap,原型:void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);

    2、在当前进程的虚拟地址空间中,寻找一段空闲的满足要求的连续的虚拟地址

    3、为此虚拟区分配一个vm_area_struct结构,接着对这个结构的各个域进行了初始化

    4、将新建的虚拟区结构(vm_area_struct)插入进程的虚拟地址区域链表或树中

    (二)调用内核空间的系统调用函数mmap(不同于用户空间函数),实现文件物理地址和进程虚拟地址的一一映射关系

    5、为映射分配了新的虚拟地址区域后,通过待映射的文件指针,在文件描述符表中找到对应的文件描述符,通过文件描述符,链接到内核“已打开文件集”中该文件的文件结构体(struct file),每个文件结构体维护着和这个已打开文件相关各项信息。

    6、通过该文件的文件结构体,链接到file_operations模块,调用内核函数mmap,其原型为:int mmap(struct file *filp, struct vm_area_struct *vma),不同于用户空间库函数。

    7、内核mmap函数通过虚拟文件系统inode模块定位到文件磁盘物理地址。

    8、通过remap_pfn_range函数建立页表,即实现了文件地址和虚拟地址区域的映射关系。此时,这片虚拟地址并没有任何数据关联到主存中。
    (三)进程发起对这片映射空间的访问,引发缺页异常,实现文件内容到物理内存(主存)的拷贝

    注:前两个阶段仅在于创建虚拟区间并完成地址映射,但是并没有将任何文件数据的拷贝至主存。真正的文件读取是当进程发起读或写操作时。

    9、进程的读或写操作访问虚拟地址空间这一段映射地址,通过查询页表,发现这一段地址并不在物理页面上。因为目前只建立了地址映射,真正的硬盘数据还没有拷贝到内存中,因此引发缺页异常。

    10、缺页异常进行一系列判断,确定无非法操作后,内核发起请求调页过程。

    11、调页过程先在交换缓存空间(swap cache)中寻找需要访问的内存页,如果没有则调用nopage函数把所缺的页从磁盘装入到主存中。

    12、之后进程即可对这片主存进行读或者写的操作,如果写操作改变了其内容,一定时间后系统会自动回写脏页面到对应磁盘地址,也即完成了写入到文件的过程。

    注:修改过的脏页面并不会立即更新回文件中,而是有一段时间的延迟,可以调用msync()来强制同步, 这样所写的内容就能立即保存到文件里了。

    为什么使用 mmap()

    Linux通过内存映像机制来提供用户程序对内存直接访问的能力。内存映像的意思是把内核中特定部分的内存空间映射到用户级程序的内存空间去。也就是说,用户空间和内核空间共享一块相同的内存。这样做的直观效果显而易见:内核在这块地址内存储变更的任何数据,用户可以立即发现和使用,根本无须数据拷贝。举个例子理解一下,使用mmap方式获取磁盘上的文件信息,只需要将磁盘上的数据拷贝至那块共享内存中去,用户进程可以直接获取到信息,而相对于传统的write/read IO系统调用, 必须先把数据从磁盘拷贝至到内核缓冲区中(页缓冲),然后再把数据拷贝至用户进程中。两者相比,mmap会少一次拷贝数据,这样带来的性能提升是巨大的。

    mmap相关函数

    函数原型

    void *mmap(void *start, size_t length, int prot, int flags,int fd, off_t offset);

    返回说明

    成功执行时,mmap()返回被映射区的指针。失败时,mmap()返回MAP_FAILED[其值为(void *)-1],error被设为以下的某个值:

    EACCES:访问出错
    EAGAIN:文件已被锁定,或者太多的内存已被锁定
    EBADF:fd不是有效的文件描述词
    EINVAL:一个或者多个参数无效
    ENFILE:已达到系统对打开文件的限制
    ENODEV:指定文件所在的文件系统不支持内存映射
    ENOMEM:内存不足,或者进程已超出最大内存映射数量
    EPERM:权能不足,操作不允许
    ETXTBSY:已写的方式打开文件,同时指定MAP_DENYWRITE标志
    SIGSEGV:试着向只读区写入
    SIGBUS:试着访问不属于进程的内存区

    参数

    start:映射区的开始地址

    length:映射区的长度

    prot:期望的内存保护标志,不能与文件的打开模式冲突。是以下的某个值,可以通过or运算合理地组合在一起

    PROT_EXEC:页内容可以被执行

    PROT_READ:页内容可以被读取

    PROT_WRITE:页可以被写入

    PROT_NONE:页不可访问

    flags:指定映射对象的类型,映射选项和映射页是否可以共享。它的值可以是一个或者多个以下位的组合体

    MAP_FIXED //使用指定的映射起始地址,如果由start和len参数指定的内存区重叠于现存的映射空间,重叠部分将会被丢弃。如果指定的起始地址不可用,操作将会失败。并且起始地址必须落在页的边界上。
    MAP_SHARED //与其它所有映射这个对象的进程共享映射空间。对共享区的写入,相当于输出到文件。直到msync()或者munmap()被调用,文件实际上不会被更新。
    MAP_PRIVATE //建立一个写入时拷贝的私有映射。内存区域的写入不会影响到原文件。这个标志和以上标志是互斥的,只能使用其中一个。
    MAP_DENYWRITE //这个标志被忽略。
    MAP_EXECUTABLE //同上
    MAP_NORESERVE //不要为这个映射保留交换空间。当交换空间被保留,对映射区修改的可能会得到保证。当交换空间不被保留,同时内存不足,对映射区的修改会引起段违例信号。
    MAP_LOCKED //锁定映射区的页面,从而防止页面被交换出内存。
    MAP_GROWSDOWN //用于堆栈,告诉内核VM系统,映射区可以向下扩展。
    MAP_ANONYMOUS //匿名映射,映射区不与任何文件关联。
    MAP_ANON //MAP_ANONYMOUS的别称,不再被使用。
    MAP_FILE //兼容标志,被忽略。
    MAP_32BIT //将映射区放在进程地址空间的低2GB,MAP_FIXED指定时会被忽略。当前这个标志只在x86-64平台上得到支持。
    MAP_POPULATE //为文件映射通过预读的方式准备好页表。随后对映射区的访问不会被页违例阻塞。
    MAP_NONBLOCK //仅和MAP_POPULATE一起使用时才有意义。不执行预读,只为已存在于内存中的页面建立页表入口。

    fd:有效的文件描述词。如果MAP_ANONYMOUS被设定,为了兼容问题,其值应为-1

    offset:被映射对象内容的起点

    相关函数

    int munmap( void * addr, size_t len )

    成功执行时,munmap()返回0。失败时,munmap返回-1,error返回标志和mmap一致;

    该调用在进程地址空间中解除一个映射关系,addr是调用mmap()时返回的地址,len是映射区的大小;

    当映射关系解除后,对原来映射地址的访问将导致段错误发生。

    int msync( void *addr, size_t len, int flags )

    一般说来,进程在映射空间的对共享内容的改变并不直接写回到磁盘文件中,往往在调用munmap()后才执行该操作。

    可以通过调用msync()实现磁盘上文件内容与共享内存区的内容一致。

    int msync( void *addr, size_t len, int flags )

    一般说来,进程在映射空间的对共享内容的改变并不直接写回到磁盘文件中,往往在调用munmap()后才执行该操作。

    可以通过调用msync()实现磁盘上文件内容与共享内存区的内容一致。

    mmap 优缺点

    优点

    1. 减少了数据的拷贝次数,用内存读写取代 I/O 读写,提高了文件读取效率。
    2. 实现了用户空间和内核空间的高效交互(映射)方式。各自的空间修改操作都会直接反映在共享(Shared)区域内,从而被对方空间及时捕捉到。
    3. 提供不同进程间共享内存及相互通信的方式。无论是父子进程,还是无亲缘关系的进程之间,都可以将自身的用户空间映射到同一个文件或匿名映射到同一片区域。从而通过各自对映射区域的改动,达到进程间通信和进程间共享的目的。例如:进程 A、B 都映射了区域 Z,当 A 第一次读取 C 时,通过缺页机制从磁盘中复制文件页到共享内存;当 B 再读 C 的相同页面时,虽然也会产生缺页异常,但是不再需要从磁盘中复制文件过来,而可直接使用已经保存在内存中的文件数据。
    4. 可用于实现高效的大规模数据传输。通常的,内存空间不足是制约大数据操作的一个方面,解决方案可以是借助硬盘空间协助操作,补充内存空间的不足。但是也会进一步的造成了大量的文件 I/O 操作,极大的影响了执行效率。这个问题可以通过 mmap 映射很好的解决,但凡需要用磁盘空间代替内存的时候,mmap 都可以发挥其功效。

    缺点

    1. 对变长文件不适合.
    2. 如果更新文件的操作很多,mmap避免两态拷贝的优势就被摊还,最终还是落在了大量的脏页回写及由此引发的随机IO上. 所以在随机写很多的情况下,mmap方式在效率上不一定会比带缓冲区的一般写快.

    为什么mmap效率高于read/write?

    常规文件操作为了提高读写效率和保护磁盘,使用了页缓存机制。这样造成读文件时需要先将文件页从磁盘拷贝到页缓存中,由于页缓存处在内核空间,不能被用户进程直接寻址,所以还需要将页缓存中数据页再次拷贝到内存对应的用户空间中。这样,通过了两次数据拷贝过程,才能完成进程对文件内容的获取任务。写操作也是一样,待写入的buffer在内核空间不能直接访问,必须要先拷贝至内核空间对应的主存,再写回磁盘中(延迟写回),也是需要两次数据拷贝。

    而使用mmap操作文件中,创建新的虚拟内存区域和建立文件磁盘地址和虚拟内存区域映射这两步,没有任何文件拷贝操作。而之后访问数据时发现内存中并无数据而发起的缺页异常过程,可以通过已经建立好的映射关系,只使用一次数据拷贝,就从磁盘中将数据传入内存的用户空间中,供进程使用。

    即常规文件操作需要从磁盘到页缓存再到用户主存的两次数据拷贝。而mmap操控文件,只需要从磁盘到用户主存的一次数据拷贝过程。因此mmap效率更高。

    示例

    1.创建文件zz,并用test.c代码向文件中写入数据。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    2.分别指定不同大小的字符数写入,比较操作时长。
    在这里插入图片描述
    在这里插入图片描述

    总结

    read和write执行了更多的系统调用,并做了更多的复制。read和write将数据从内核缓冲区中复制到应用缓冲区,然后再把数据从应用缓冲区复制到内核缓冲区。而mmap则直接把数据从映射到地址空间的一个内核缓冲区复制到另一个内核缓冲区。当引用尚不存在的内存页时,这样的复制过程就会作为处理页错误的结果而出现(每次错页读发生一次错误,每次错页写发生一次错误)。mmap的优势在于通过把文件的某一块内容映射到用户空间上,用户可以直接向内核缓冲池读写这一块内容,这样一来就少了内核与用户空间的来回拷贝所以通常更快。但 mmap方式只适用于更新、读写一块固定大小的文件区域而不能做像诸如不断的写内容进入文件导到文件增长这类的事。

    展开全文
  • 一、do_mmap 函数执行流程、 二、do_mmap 函数源码


    调用 mmap 系统调用 , 先检查 " 偏移 " 是否是 " 内存页大小 "" 整数倍 " , 如果偏移是内存页大小的整数倍 , 则调用 sys_mmap_pgoff 函数 , 继续向下执行 ;

    sys_mmap_pgoff 系统调用函数 中 , 最后调用了 vm_mmap_pgoff 函数 , 继续向下执行 ;

    vm_mmap_pgoff 函数 中 , 核心处理过程就是调用 do_mmap 函数 , 这是 " 内存映射 " 创建的主要函数逻辑 ;





    一、do_mmap 函数执行流程



    do_mmap 函数 , 主要功能是 创建 " 内存映射 " ;


    首先 , 执行 get_unmapped_area 函数 , 获取未被映射的内存区域 , 根据不同的情况 , 如 " 文件映射 " 还是 " 匿名映射 " , 调用对应的 " 分配虚拟地址区间 " 的函数 ;

    	/* Obtain the address to map to. we verify (or select) it and ensure
    	 * that it represents a valid section of the address space.
    	 */
    	addr = get_unmapped_area(file, addr, len, pgoff, flags);
    

    然后 , 计算 " 虚拟内存标志 " ;

    最后 , 通过调用 mmap_region 函数 , 创建 " 虚拟内存区域 " ;

    addr = mmap_region(file, addr, len, vm_flags, pgoff, uf);
    




    二、do_mmap 函数源码



    创建 " 内存映射 " 主要是 do_mmap 函数实现的 , 该函数定义在 Linux 内核源码的 linux-4.12\mm\mmap.c#1320 位置 ;


    do_mmap 函数源码如下 :

    /*
     * The caller must hold down_write(&current->mm->mmap_sem).
     */
    unsigned long do_mmap(struct file *file, unsigned long addr,
    			unsigned long len, unsigned long prot,
    			unsigned long flags, vm_flags_t vm_flags,
    			unsigned long pgoff, unsigned long *populate,
    			struct list_head *uf)
    {
    	struct mm_struct *mm = current->mm;
    	int pkey = 0;
    
    	*populate = 0;
    
    	if (!len)
    		return -EINVAL;
    
    	/*
    	 * Does the application expect PROT_READ to imply PROT_EXEC?
    	 *
    	 * (the exception is when the underlying filesystem is noexec
    	 *  mounted, in which case we dont add PROT_EXEC.)
    	 */
    	if ((prot & PROT_READ) && (current->personality & READ_IMPLIES_EXEC))
    		if (!(file && path_noexec(&file->f_path)))
    			prot |= PROT_EXEC;
    
    	if (!(flags & MAP_FIXED))
    		addr = round_hint_to_min(addr);
    
    	/* Careful about overflows.. */
    	len = PAGE_ALIGN(len);
    	if (!len)
    		return -ENOMEM;
    
    	/* offset overflow? */
    	if ((pgoff + (len >> PAGE_SHIFT)) < pgoff)
    		return -EOVERFLOW;
    
    	/* Too many mappings? */
    	if (mm->map_count > sysctl_max_map_count)
    		return -ENOMEM;
    
    	/* Obtain the address to map to. we verify (or select) it and ensure
    	 * that it represents a valid section of the address space.
    	 */
    	addr = get_unmapped_area(file, addr, len, pgoff, flags);
    	if (offset_in_page(addr))
    		return addr;
    
    	if (prot == PROT_EXEC) {
    		pkey = execute_only_pkey(mm);
    		if (pkey < 0)
    			pkey = 0;
    	}
    
    	/* Do simple checking here so the lower-level routines won't have
    	 * to. we assume access permissions have been handled by the open
    	 * of the memory object, so we don't do any here.
    	 */
    	vm_flags |= calc_vm_prot_bits(prot, pkey) | calc_vm_flag_bits(flags) |
    			mm->def_flags | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC;
    
    	if (flags & MAP_LOCKED)
    		if (!can_do_mlock())
    			return -EPERM;
    
    	if (mlock_future_check(mm, vm_flags, len))
    		return -EAGAIN;
    
    	if (file) {
    		struct inode *inode = file_inode(file);
    
    		switch (flags & MAP_TYPE) {
    		case MAP_SHARED:
    			if ((prot&PROT_WRITE) && !(file->f_mode&FMODE_WRITE))
    				return -EACCES;
    
    			/*
    			 * Make sure we don't allow writing to an append-only
    			 * file..
    			 */
    			if (IS_APPEND(inode) && (file->f_mode & FMODE_WRITE))
    				return -EACCES;
    
    			/*
    			 * Make sure there are no mandatory locks on the file.
    			 */
    			if (locks_verify_locked(file))
    				return -EAGAIN;
    
    			vm_flags |= VM_SHARED | VM_MAYSHARE;
    			if (!(file->f_mode & FMODE_WRITE))
    				vm_flags &= ~(VM_MAYWRITE | VM_SHARED);
    
    			/* fall through */
    		case MAP_PRIVATE:
    			if (!(file->f_mode & FMODE_READ))
    				return -EACCES;
    			if (path_noexec(&file->f_path)) {
    				if (vm_flags & VM_EXEC)
    					return -EPERM;
    				vm_flags &= ~VM_MAYEXEC;
    			}
    
    			if (!file->f_op->mmap)
    				return -ENODEV;
    			if (vm_flags & (VM_GROWSDOWN|VM_GROWSUP))
    				return -EINVAL;
    			break;
    
    		default:
    			return -EINVAL;
    		}
    	} else {
    		switch (flags & MAP_TYPE) {
    		case MAP_SHARED:
    			if (vm_flags & (VM_GROWSDOWN|VM_GROWSUP))
    				return -EINVAL;
    			/*
    			 * Ignore pgoff.
    			 */
    			pgoff = 0;
    			vm_flags |= VM_SHARED | VM_MAYSHARE;
    			break;
    		case MAP_PRIVATE:
    			/*
    			 * Set pgoff according to addr for anon_vma.
    			 */
    			pgoff = addr >> PAGE_SHIFT;
    			break;
    		default:
    			return -EINVAL;
    		}
    	}
    
    	/*
    	 * Set 'VM_NORESERVE' if we should not account for the
    	 * memory use of this mapping.
    	 */
    	if (flags & MAP_NORESERVE) {
    		/* We honor MAP_NORESERVE if allowed to overcommit */
    		if (sysctl_overcommit_memory != OVERCOMMIT_NEVER)
    			vm_flags |= VM_NORESERVE;
    
    		/* hugetlb applies strict overcommit unless MAP_NORESERVE */
    		if (file && is_file_hugepages(file))
    			vm_flags |= VM_NORESERVE;
    	}
    
    	addr = mmap_region(file, addr, len, vm_flags, pgoff, uf);
    	if (!IS_ERR_VALUE(addr) &&
    	    ((vm_flags & VM_LOCKED) ||
    	     (flags & (MAP_POPULATE | MAP_NONBLOCK)) == MAP_POPULATE))
    		*populate = len;
    	return addr;
    }
    

    源码路径 : linux-4.12\mm\mmap.c#1320
    在这里插入图片描述

    展开全文
  • icon1.jpgmmap 函数是 unix/linux下的系统调用。当存在客户-服务程序中复制文件时候,其数据流如下,要经历四次数据复制,开销很大。image.png果采用共享内存的...C语言mmap()函数:建立内存映射头文件:#include #...
  • 深入理解mmap函数

    2021-02-06 11:42:25
    进程间通信方式有多种,当然这不是我们这节要讨论的重点,这节主要说的是mmap函数,它是通过将一块物理内存映射到多个进程的虚拟地址空间上,来完成多个进程对同一块物理内存的读写从而使得进程间能够实现通信。...
  • C语言 mmap函数

    千次阅读 2022-02-07 15:10:13
    文章目录系列文章目录一、mmap简介二、mmap1.功能2.头文件3.函数声明4.函数参数5.返回值6.系统调用总结 一、mmap简介 mmap是一种内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘...
  • mmap函数的用法详解及实例分析

    千次阅读 2020-01-29 12:21:56
    1、mmap函数是一个比较神奇的函数,它可以把文件映射到进程的虚拟内存空间。通过对这段内存的读取和修改,可以实现对文件的读取和修改,而不需要用read和write函数。如下图所示,为mmap实现原理的示意图。 在这个...
  • mmap mmap将一个文件或者其它对象映射进内存。...函数原型 void* mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset); int munmap(void* start,size_t length); 条件 mmap
  • mmap函数使用实例

    2022-03-21 17:03:17
    mmap, munmap - map or unmap files or devices into memory SYNOPSIS #include <sys/mman.h> /* 映射,返回值为映射到的最终地址。 addr: 目标内存空间 放到当前进程空间的起始地址,若为空,则函数自己...
  • mmap函数和munmap函数

    千次阅读 2021-07-03 02:42:56
    mmap函数和munmap函数 mmap函数用于申请一段内存空间,我们可以将这段内存作为进程间通信的共享内存,也可以将文件直接映射到其中。munmap函数则用于释放有mmap创建的这段内存空间。他们的定义如下: #inlcude<...
  • 了解一下mmap函数

    千次阅读 2022-04-29 13:05:38
    这是一个c标准库(libc, -lc)里的函数。 它的主要功能,概括的说,就是分配内存,或将文件或设备映射到内存中去。 在使用此函数前,你需要引入头文件,#include <sys/mman.h>,函数声明是这样的, void * ...
  • 一、mmap 简介、 二、mmap 函数作用、
  • Linux中mmap函数使用

    2020-09-20 16:44:00
    这就是今天要说的mmap,它在android中的用处非常多,比如binder,还有腾讯的开源的IO框架MMKV。这里简单记录下使用。 二、优势和缺势 优势 1 .读写文件避免了 read() 和 write() 系统调用,也避免了数据的拷贝。 ...
  • 使用 mmap 时务必注意以下事项: 1.创建映射区的过程中,隐含着一次对映射文件的读操作。 2. 当 MAP_SHARED 时,要求:映射区的权限应 <= 文件打开的权限(出于对映射区的保护)。而 MAP_PRIVATE 则无所谓,因为 ...
  • mmap函数分析

    2021-05-17 10:29:33
    mmap系统调用是将一个打开的文件映射到进程的用户空间,mmap系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以像访问普通内存一样对文件进行访问,不必再调用read...
  • mmap函数的介绍 mmap函数的主要作用是可以将一个文件或者设备的内容映射到内存当中,用户就可以通过一些内存操作方式(如memcpy、memset)对文件或者设备进行直接的操作。这种操作可以减少一些IO的开销,如通过传统的...
  • mmap函数参数讲解

    千次阅读 2020-09-23 21:50:00
    函数原型: void *mmap{ void *addr; //映射区首地址,传NULL size_t length; //映射区的大小 //会自动调为4k的整数倍 //不能为0 //一般文件多大,length就指定多大 int prot...
  • 使用 shm_open 函数创建共享内存设备文件,shm_open 函数返回一个文件描述符,然后使用 mmap 函数映射该共享内存到进程的虚拟空间内,mmap 函数返回一个虚拟地址,之后便可以通过这个虚拟地址对共享内存进行读和写...
  • 1.mmap使用前需要一次读取文件的操作 2.作为内存映射的文件,文件本身要存在,文件要具有权限(简单体现 linux上文件上不能有把锁的图表) 3.作为内存映射的文件,文件不能为空 4.通过内存映射把内容写回文件,...
  • mmap返回-1报错失败: #define EPERM 1 /* Operation not permitted */ mmap error :1-Operation not permitted. #define EINVAL 22 /* Invalid argument */ mmap error :22-Invalid argument.
  • 最近开始学习Linux驱动程序,将内存映射和ioremap,mmap函数相关资料进行了整理一,内存映射对于提供了MMU(存储管理器,辅助操作系统进行内存管理,提供虚实地址转换等硬件支持)的处理器而言,Linux提供了复杂的存储...
  • Linux系统mmap函数映射物理地址

    千次阅读 2020-01-07 11:06:46
    Linux系统mmap函数映射物理地址代码64位报错 代码 代码中,mmap参数0,代表让系统分配地址(映射的地址)。BASE_ADDR为想要映射的物理地址,CPLD_VER_BCD等是相对于BASE_ADDR的偏移量。 /*read physical address*/ /...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 56,933
精华内容 22,773
关键字:

mmap函数

友情链接: 矩形块碰撞线框.rar