精华内容
下载资源
问答
  • length参数 mmap
    2022-02-15 15:28:19

    mmap()参数详解

    mmap, 从函数名就可以看出来这是memory map, 即地址的映射, 是一种内存映射文件的方法, ,将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系。mmap()系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以向访问普通内存一样对文件进行访问,不必再调用read(),write()等操作。

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

    1. addr : 指定文件应被映射到进程空间的起始地址。一般被指定为NULL, 目的为了避免与现有的地址产生冲突。
    2. Length: 映射到进程地址空间的字节数,从offset偏移开始计算
    3. Prot : 指定共享内存的访问权限。PROT_READ(可读),PROT_WRITE(可写),PROT_EXEC(可执行),PROT_NORE(不可访问)。
    4. Flag : MAP_SHARED,MAP_PRIVATE,MAP_FIXED等,其中MAP_SHARED和MAP_PRIVATE互斥,MAP_FIXED是为了解决固定addr地址重叠问题,如果发生重叠,则丢弃重叠部分的映射。
    5. Fd : 即将映射到进程空间的文件描述符,一般由open返回,其值也可以设置为-1,此时需要指定flags参数中的MAP_ANON,表明进行的是匿名映射。
    6. Offset : 从文件的一个偏移量进行映射,这个值必须是PAGE_SIZE的倍数,不然会映射失败。

    更多相关内容
  • mmap函数参数讲解

    千次阅读 2020-09-23 21:50:00
    1.mmap-创建内存映射 作用:将磁盘文件的数据映射到内存,用户通过内存就能修改磁盘文件 函数原型: void *mmap{ void *addr; //映射区首地址,传NULL size_t length; //映射区的大小 //...

    1.mmap-创建内存映射

        作用:将磁盘文件的数据映射到内存,用户通过内存就能修改磁盘文件

        函数原型:

     
    1. void *mmap{

    2. void *addr; //映射区首地址,传NULL

    3. size_t length; //映射区的大小

    4. //会自动调为4k的整数倍

    5. //不能为0

    6. //一般文件多大,length就指定多大

    7. int prot; //映射区权限

    8. //PROT_READ 映射区比必须要有读权限

    9. //PROT_WRITE

    10. //PROT_READ | PROT_WRITE

    11. int flags; //标志位参数

    12. //MAP_SHARED 修改了内存数据会同步到磁盘

    13. //MAP_PRIVATE 修改了内存数据不会同步到磁盘

    14. int fd; //要映射的文件对应的fd

    15. off_t offset; //映射文件的偏移量,从文件的哪里开始操作

    16. //映射的时候文件指针的偏移量

    17. //必须是4k的整数倍

    18. //一般设置为0

    19.  
    20. }

    返回值:

    映射区的首地址-调用成功

    调用失败:MAP_FALED

    2.munmap-释放内存映射区

    函数原型:int munmap(void *addr,size_t length);

        addr-mmap的返回值

        length-mmap的第二个参数

    3.注意事项

    问:如果对mmap的返回值(ptr)做++操作(ptr++), munmap是否能够成功?
    答:不能
    问:如果open时O_RDONLY, mmap时prot参数指定PROT_READ | PROT_WRITE会怎样?
    答mmap调用失败
    open文件指定权限应该大于等于mmap第三个参数prot指定的权限
    问:如果文件偏移量为1000会怎样?
    答:必须是4096的整数倍
    问:mmap什么情况下会调用失败?
    第二个参数length = 0
    第三个参数必须指定PROT_READ
    fd对应的打开权限必须大于等于port权限
    偏移量:必须是4096的整数倍
    问:可以open的时候O_CREAT一个新文件来创建映射区吗?
    答:可以,需要做文件拓展
    lseek
    truncate(path,length)
    问:mmap后关闭文件描述符,对mmap映射有没有影响?
    答:没有
    问:对ptr越界操作会怎样?
    答:段错误

    4.mmap使用

    (1)基本代码

     
    1. #include <stdio.h>

    2. #include <unistd.h>

    3. #include <stdlib.h>

    4. #include <sys/types.h>

    5. #include <sys/stat.h>

    6. #include <string.h>

    7. #include <sys/mman.h>

    8. #include <fcntl.h>

    9.  
    10. int main(int argc, const char* argv[])

    11. {

    12. //打开一个文件

    13. int fd=open("english.txt",O_RDWR);

    14. int len=lseek(fd,0,SEEK_END);

    15.  
    16. //创建内存映射区

    17. void *ptr=mmap(NULL,len,PROT_READ | PROT_WRITE,MAP_SHARED,fd,0);

    18. if(ptr==MAP_FAILED)

    19. {

    20. perror("mmap error");

    21. exit(1);

    22. }

    23.  
    24. printf("%s",(char*)ptr);

    25.  
    26. //释放内存映射区

    27. munmap(ptr,len);

    28. close(fd);

    29. return 0;

    30. }

    运行结果:

     
    1. wangkai@wangkai-HP-242-G1-Notebook-PC:~/0110/6Day$ ./mmap

    2. sadaskdkasj

    3. sdasdasd

    4. asdasdsadasds

    5. dasdas

    (2)使用mmap进行有血缘关系的进程间通信

     
    1. #include <stdio.h>

    2. #include <unistd.h>

    3. #include <stdlib.h>

    4. #include <sys/types.h>

    5. #include <sys/stat.h>

    6. #include <string.h>

    7. #include <sys/mman.h>

    8. #include <sys/wait.h>

    9. #include <fcntl.h>

    10.  
    11. /*

    12. 利用mmap进行父子间通信,效率比文件I/O高,

    13. 数据不是从磁盘上读,而是从内存上读写

    14.  
    15. 不阻塞,所以读之前一定要写好

    16. */

    17. int main(int argc, const char* argv[])

    18. {

    19. //打开一个文件

    20. int fd=open("english.txt",O_RDWR);

    21. int len=lseek(fd,0,SEEK_END);

    22.  
    23. //创建内存映射区

    24. void *ptr=mmap(NULL,len,PROT_READ | PROT_WRITE,MAP_SHARED,fd,0);

    25. if(ptr==MAP_FAILED)

    26. {

    27. perror("mmap error");

    28. exit(1);

    29. }

    30.  
    31. close(fd);

    32. //创建子进程

    33. pid_t pid=fork();

    34. if(pid==-1)

    35. {

    36. perror("fork error");

    37. exit(1);

    38. }

    39. if(pid>0)

    40. {

    41. //写数据

    42. strcpy((char*)ptr,"我是你爸爸");

    43. //回收子进程

    44. wait(NULL);

    45. }

    46. else if(pid==0)

    47. {

    48. //读数据

    49. printf("%s\n",(char*)ptr);

    50. }

    51. printf("%s",(char*)ptr);

    52.  
    53. //释放内存映射区

    54. munmap(ptr,len);

    55.  
    56. return 0;

    57. }

    (3)创建匿名映射区

     
    1. int len=4096;

    2. //创建匿名内存映射区,不指定文件

    3. void *ptr=mmap(NULL,len,PROT_READ | PROT_WRITE,MAP_SHARED | MAP_ANON,-1,0);

    效果和(2)一样。

    (4)没有血缘关系的进程间通信

    写代码

     
    1. //mmap_w_ipc.c

    2. #include <stdio.h>

    3. #include <stdlib.h>

    4. #include <string.h>

    5. #include <unistd.h>

    6. #include <sys/types.h>

    7. #include <sys/mman.h>

    8. #include <fcntl.h>

    9.  
    10.  
    11. int main(int argc, char *argv[])

    12. {

    13. int fd = open("temp", O_RDWR | O_CREAT, 0664);

    14.  
    15. void* ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

    16. if(ptr == MAP_FAILED)

    17. {

    18. perror("mmap");

    19. exit(1);

    20. }

    21.  
    22. while(1)

    23. {

    24. char*p = (char*)ptr;

    25. p += 1024;

    26. strcpy(p, "hello parent, i am your 朋友!!!\n");

    27. sleep(2);

    28. }

    29.  
    30. // 释放

    31. int ret = munmap(ptr, 8192);

    32. if(ret == -1)

    33. {

    34. perror("munmap");

    35. exit(1);

    36. }

    37.  
    38. return 0;

    39. }

    40.  

     

    读代码

     
    1. //mmap_r_ipc.c

    2. #include <stdio.h>

    3. #include <stdlib.h>

    4. #include <string.h>

    5. #include <unistd.h>

    6. #include <sys/types.h>

    7. #include <sys/mman.h>

    8. #include <fcntl.h>

    9.  
    10.  
    11. int main(int argc, char *argv[])

    12. {

    13. int fd = open("temp", O_RDWR | O_CREAT, 0664);

    14. ftruncate(fd, 4096);

    15. int len = lseek(fd, 0, SEEK_END);

    16.  
    17. void* ptr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

    18. if(ptr == MAP_FAILED)

    19. {

    20. perror("mmap");

    21. exit(1);

    22. }

    23.  
    24. while(1)

    25. {

    26. sleep(1);

    27. printf("%s\n", (char*)ptr+1024);

    28. }

    29.  
    30. // 释放

    31. int ret = munmap(ptr, len);

    32. if(ret == -1)

    33. {

    34. perror("munmap");

    35. exit(1);

    36. }

    37.  
    38. return 0;

    39. }

    40.  
    41.  

    小结

     

    1. 进程间通信

        a. 有血缘关系的
    父子进程共享内存映射区
        b. 没有血缘关系的进程间通信
    i. 如何通信?
    不能使用匿名映射的方式
    只能借助磁盘文件创建映射区 - hello
    不阻塞
    ii. a(a.c)  b(b.c)
    a.c
    int fd = open("hello");
    void* ptr = mmap(,,,,,fd, 0);
    对映射区进行读写操作

    b.c
    int fd1 = open("hello");
    void* ptr1 = mmap(,,,,fd1, 0);

    对映射区做读写操作

    2. mmap 实现内存映射

    a. 必须有一个文件
    b. 文件数据什么时候有用:
    i. 单纯文件映射
    ii. 进程间通信:
    1) 文件数据是没有用的

    3. 如果创建匿名映射区

    mmap的时候:
        第二个参数:指定映射区大小
        第四个参数:需要添加MAP_ANON宏
        第五个参数:-1

    4. 父子进程永远共享的东西?

    文件描述符
    内存映射区

    展开全文
  • mmap()函数参数详解

    千次阅读 2019-04-17 15:20:41
    mmap将一个文件或者其它对象映射进内存。文件被映射到多个页上,如果文件的大小不是所有页的大小之和,最后一个页不被使用的空间将会清零。 munmap执行相反的操作,删除特定地址区域的对象映射。 基于文件的映射,在...

    author : wfs
    time : 2019.4.17


    功能描述

    • mmap将一个文件或者其它对象映射进内存。文件被映射到多个页上,如果文件的大小不是所有页的大小之和,最后一个页不被使用的空间将会清零。 munmap执行相反的操作,删除特定地址区域的对象映射。
    • 基于文件的映射,在mmap和munmap执行过程的任何时刻,被映射文件的st_atime可能被更新。
    • 在对映射区写入之后,调用msync()函数写回。
    用法
      #include <sys/mman.h>
      void *mmap(void *start, size_t length, int prot, int flags,int fd, off_t offset);
      int munmap(void *start, size_t length); 
    
    

    参数:

    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:被映射对象内容的起点。
      返回说明:
      成功执行时,mmap()返回被映射区的指针,munmap()返回0。失败时,mmap()返回MAP_FAILED[其值为(void *)-1],munmap返回-1。errno被设为以下的某个值
      EACCES:访问出错
      EAGAIN:文件已被锁定,或者太多的内存已被锁定
      EBADF:fd不是有效的文件描述词
      EINVAL:一个或者多个参数无效
      ENFILE:已达到系统对打开文件的限制
      ENODEV:指定文件所在的文件系统不支持内存映射
      ENOMEM:内存不足,或者进程已超出最大内存映射数量
      EPERM:权能不足,操作不允许
      ETXTBSY:已写的方式打开文件,同时指定MAP_DENYWRITE标志
      SIGSEGV:试着向只读区写入
      SIGBUS:试着访问不属于进程的内存区

    共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式。两个不同进程A、B共享内存的意思是,同一块物理内存被映射到进程A、B各自的进程地址空间。进程A可以即时看到进程B对共享内存中数据的更新,反之亦然。由于多个进程共享同一块内存区域,必然需要某种同步机制,互斥锁和信号量都可以。

    采用共享内存通信的一个显而易见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝。对于像管道和消息队列等通信方式,则需要在内核和用户空间进行四次的数据拷贝,而共享内存则只拷贝两次数据[1]:一次从输入文件到共享内存区,另一次从共享内存区到输出文件。实际上,进程之间在共享内存时,并不总是读写少量数据后就解除映射,有新的通信时,再重新建立共享内存区域。而是保持共享区域,直到通信完毕为止,这样,数据内容一直保存在共享内存中,并没有写回文件。共享内存中的内容往往是在解除映射时才写回文件的。因此,采用共享内存的通信方式效率是非常高的。

    Linux的2.2.x 内核支持多种共享内存方式,如mmap()系统调用,Posix共享内存,以及系统V共享内存。linux发行版本如Redhat 8.0支持mmap()系统调用及系统V共享内存,但还没实现Posix共享内存,本文将主要介绍mmap()系统调用及系统V共享内存API的原理及应用。

    一、内核怎样保证各个进程寻址到同一个共享内存区域的内存页面

    1、 page cache及swap cache中页面的区分:一个被访问文件的物理页面都驻留在page cache或swap cache中,一个页面的所有信息由struct page来描述。struct page中有一个域为指针mapping ,它指向一个struct address_space类型结构。page cache或swap cache中的所有页面就是根据address_space结构以及一个偏移量来区分的。

    2、文件与 address_space结构的对应:一个具体的文件在打开后,内核会在内存中为之建立一个struct inode结构,其中的i_mapping域指向一个address_space结构。这样,一个文件就对应一个address_space结构,一个 address_space与一个偏移量能够确定一个page cache 或swap cache中的一个页面。因此,当要寻址某个数据时,很容易根据给定的文件及数据在文件内的偏移量而找到相应的页面。

    3、进程调用mmap()时,只是在进程空间内新增了一块相应大小的缓冲区,并设置了相应的访问标识,但并没有建立进程空间到物理页面的映射。因此,第一次访问该空间时,会引发一个缺页异常。

    4、对于共享内存映射情况,缺页异常处理程序首先在swap cache中寻找目标页(符合address_space以及偏移量的物理页),如果找到,则直接返回地址;如果没有找到,则判断该页是否在交换区 (swap area),如果在,则执行一个换入操作;如果上述两种情况都不满足,处理程序将分配新的物理页面,并把它插入到page cache中。进程最终将更新进程页表。
    注:对于映射普通文件情况(非共享映射),缺页异常处理程序首先会在page cache中根据address_space以及数据偏移量寻找相应的页面。如果没有找到,则说明文件数据还没有读入内存,处理程序会从磁盘读入相应的页面,并返回相应地址,同时,进程页表也会更新。

    5、所有进程在映射同一个共享内存区域时,情况都一样,在建立线性地址与物理地址之间的映射之后,不论进程各自的返回地址如何,实际访问的必然是同一个共享内存区域对应的物理页面。
    注:一个共享内存区域可以看作是特殊文件系统shm中的一个文件,shm的安装点在交换区上。

    上面涉及到了一些数据结构,围绕数据结构理解问题会容易一些。

        回页首
    

    二、mmap()及其相关系统调用

    mmap()系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以向访问普通内存一样对文件进行访问,不必再调用read(),write()等操作。

    注:实际上,mmap()系统调用并不是完全为了用于共享内存而设计的。它本身提供了不同于一般对普通文件的访问方式,进程可以像读写内存一样对普通文件的操作。而Posix或系统V的共享内存IPC则纯粹用于共享目的,当然mmap()实现共享内存也是其主要应用之一。

    1、mmap()系统调用形式如下:

    void* mmap ( void * addr , size_t len , int prot , int flags , int fd , off_t offset )
    参数fd为即将映射到进程空间的文件描述字,一般由open()返回,同时,fd可以指定为-1,此时须指定flags参数中的MAP_ANON,表明进行的是匿名映射(不涉及具体的文件名,避免了文件的创建及打开,很显然只能用于具有亲缘关系的进程间通信)。len是映射到调用进程地址空间的字节数,它从被映射文件开头offset个字节开始算起。prot 参数指定共享内存的访问权限。可取如下几个值的或:PROT_READ(可读) , PROT_WRITE (可写), PROT_EXEC (可执行), PROT_NONE(不可访问)。flags由以下几个常值指定:MAP_SHARED , MAP_PRIVATE , MAP_FIXED,其中,MAP_SHARED , MAP_PRIVATE必选其一,而MAP_FIXED则不推荐使用。offset参数一般设为0,表示从文件头开始映射。参数addr指定文件应被映射到进程空间的起始地址,一般被指定一个空指针,此时选择起始地址的任务留给内核来完成。函数的返回值为最后文件映射到进程空间的地址,进程可直接操作起始地址为该值的有效地址。这里不再详细介绍mmap()的参数,读者可参考mmap()手册页获得进一步的信息。

    2、系统调用mmap()用于共享内存的两种方式:

    (1)使用普通文件提供的内存映射:适用于任何进程之间;此时,需要打开或创建一个文件,然后再调用mmap();典型调用代码如下:

        fd=open(name, flag, mode);
    

    if(fd<0)

    ptr=mmap(NULL, len , PROT_READ|PROT_WRITE, MAP_SHARED , fd , 0); 通过mmap()实现共享内存的通信方式有许多特点和要注意的地方,我们将在范例中进行具体说明。

    (2)使用特殊文件提供匿名内存映射:适用于具有亲缘关系的进程之间;由于父子进程特殊的亲缘关系,在父进程中先调用mmap(),然后调用fork()。那么在调用fork()之后,子进程继承父进程匿名映射后的地址空间,同样也继承mmap()返回的地址,这样,父子进程就可以通过映射区域进行通信了。注意,这里不是一般的继承关系。一般来说,子进程单独维护从父进程继承下来的一些变量。而mmap()返回的地址,却由父子进程共同维护。
    对于具有亲缘关系的进程实现共享内存最好的方式应该是采用匿名内存映射的方式。此时,不必指定具体的文件,只要设置相应的标志即可,参见范例2。

    3、系统调用munmap()

    int munmap( void * addr, size_t len )
    该调用在进程地址空间中解除一个映射关系,addr是调用mmap()时返回的地址,len是映射区的大小。当映射关系解除后,对原来映射地址的访问将导致段错误发生。

    4、系统调用msync()

    int msync ( void * addr , size_t len, int flags)
    一般说来,进程在映射空间的对共享内容的改变并不直接写回到磁盘文件中,往往在调用munmap()后才执行该操作。可以通过调用msync()实现磁盘上文件内容与共享内存区的内容一致。

        回页首
    

    三、mmap()范例

    下面将给出使用mmap()的两个范例:范例1给出两个进程通过映射普通文件实现共享内存通信;范例2给出父子进程通过匿名映射实现共享内存。系统调用 mmap()有许多有趣的地方,下面是通过mmap()映射普通文件实现进程间的通信的范例,我们通过该范例来说明mmap()实现共享内存的特点及注意事项。

    范例1:两个进程通过映射普通文件实现共享内存通信

    范例1包含两个子程序:map_normalfile1.c及map_normalfile2.c。编译两个程序,可执行文件分别为 map_normalfile1及map_normalfile2。两个程序通过命令行参数指定同一个文件来实现共享内存方式的进程间通信。 map_normalfile2试图打开命令行参数指定的一个普通文件,把该文件映射到进程的地址空间,并对映射后的地址空间进行写操作。 map_normalfile1把命令行参数指定的文件映射到进程地址空间,然后对映射后的地址空间执行读操作。这样,两个进程通过命令行参数指定同一个文件来实现共享内存方式的进程间通信。

    下面是两个程序代码:

    /-------------map_normalfile1.c-----------/
    #include <sys/mman.h>
    #include <sys/types.h>
    #include <fcntl.h>
    #include <unistd.h>
    typedef struct{
    char name[4];
    int age;
    }people;

    main(int argc, char** argv) // map a normal file as shared mem:
    {
    int fd,i;
    people *p_map;
    char temp;

        fd=open(argv[1],O_CREAT|O_RDWR|O_TRUNC,00777);
        lseek(fd,sizeof(people)*5-1,SEEK_SET);
        write(fd,"",1);
       
        p_map = (people*) mmap( NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0 );
        close( fd );
        temp = 'a';
        for(i=0; i<10; i++)
        {
                temp += 1;
                memcpy( ( *(p_map+i) ).name, &temp,2 );
                ( *(p_map+i) ).age = 20+i;
        }
        printf(" initialize over /n ");
        sleep(10);
    
        munmap( p_map, sizeof(people)*10 );
        printf( "umap ok /n" );
    

    }

    /-------------map_normalfile2.c-----------/
    #include <sys/mman.h>
    #include <sys/types.h>
    #include <fcntl.h>
    #include <unistd.h>
    typedef struct{
    char name[4];
    int age;
    }people;

    main(int argc, char** argv) // map a normal file as shared mem:
    {
    int fd,i;
    people p_map;
    fd=open( argv[1],O_CREAT|O_RDWR,00777 );
    p_map = (people
    )mmap(NULL,sizeof(people)10,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
    for(i = 0;i<10;i++)
    {
    printf( “name: %s age %d;/n”,(
    (p_map+i)).name, (*(p_map+i)).age );

        }
        munmap( p_map,sizeof(people)*10 );
    

    }

    map_normalfile1.c 首先定义了一个people数据结构,(在这里采用数据结构的方式是因为,共享内存区的数据往往是有固定格式的,这由通信的各个进程决定,采用结构的方式有普遍代表性)。map_normfile1首先打开或创建一个文件,并把文件的长度设置为5个people结构大小。然后从mmap()的返回地址开始,设置了10个people结构。然后,进程睡眠10秒钟,等待其他进程映射同一个文件,最后解除映射。

    map_normfile2.c只是简单的映射一个文件,并以people数据结构的格式从mmap()返回的地址处读取10个people结构,并输出读取的值,然后解除映射。

    分别把两个程序编译成可执行文件map_normalfile1和map_normalfile2后,在一个终端上先运行./map_normalfile2 /tmp/test_shm,程序输出结果如下:

    initialize over
    umap ok

    在map_normalfile1输出initialize over 之后,输出umap ok之前,在另一个终端上运行map_normalfile2 /tmp/test_shm,将会产生如下输出(为了节省空间,输出结果为稍作整理后的结果):

    name: b age 20; name: c age 21; name: d age 22; name: e age 23; name: f age 24;
    name: g age 25; name: h age 26; name: I age 27; name: j age 28; name: k age 29;

    在map_normalfile1 输出umap ok后,运行map_normalfile2则输出如下结果:

    name: b age 20; name: c age 21; name: d age 22; name: e age 23; name: f age 24;
    name: age 0; name: age 0; name: age 0; name: age 0; name: age 0;

    从程序的运行结果中可以得出的结论

    1、 最终被映射文件的内容的长度不会超过文件本身的初始大小,即映射不能改变文件的大小;

    2、可以用于进程通信的有效地址空间大小大体上受限于被映射文件的大小,但不完全受限于文件大小。打开文件被截短为5个people结构大小,而在 map_normalfile1中初始化了10个people数据结构,在恰当时候(map_normalfile1输出initialize over 之后,输出umap ok之前)调用map_normalfile2会发现map_normalfile2将输出全部10个people结构的值,后面将给出详细讨论。
    注:在linux中,内存的保护是以页为基本单位的,即使被映射文件只有一个字节大小,内核也会为映射分配一个页面大小的内存。当被映射文件小于一个页面大小时,进程可以对从mmap()返回地址开始的一个页面大小进行访问,而不会出错;但是,如果对一个页面以外的地址空间进行访问,则导致错误发生,后面将进一步描述。因此,可用于进程间通信的有效地址空间大小不会超过文件大小及一个页面大小的和。

    3、文件一旦被映射后,调用mmap()的进程对返回地址的访问是对某一内存区域的访问,暂时脱离了磁盘上文件的影响。所有对mmap()返回地址空间的操作只在内存中有意义,只有在调用了munmap()后或者msync()时,才把内存中的相应内容写回磁盘文件,所写内容仍然不能超过文件的大小。

    范例2:父子进程通过匿名映射实现共享内存

    #include <sys/mman.h>
    #include <sys/types.h>
    #include <fcntl.h>
    #include <unistd.h>
    typedef struct{
    char name[4];
    int age;
    }people;
    main(int argc, char** argv)
    {
    int i;
    people p_map;
    char temp;
    p_map=(people
    )mmap(NULL,sizeof(people)10,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1,0);
    if(fork() == 0)
    {
    sleep(2);
    for(i = 0;i<5;i++)
    printf(“child read: the %d people’s age is %d/n”,i+1,(
    (p_map+i)).age);
    (p_map).age = 100;
    munmap(p_map,sizeof(people)10); //实际上,进程终止时,会自动解除映射。
    exit();
    }
    temp = ‘a’;
    for(i = 0;i<5;i++)
    {
    temp += 1;
    memcpy((
    (p_map+i)).name, &temp,2);
    (
    (p_map+i)).age=20+i;
    }

        sleep(5);
        printf( "parent read: the first people,s age is %d/n",(*p_map).age );
        printf("umap/n");
        munmap( p_map,sizeof(people)*10 );
        printf( "umap ok/n" );
    

    }

    考察程序的输出结果,体会父子进程匿名共享内存:

    child read: the 1 people’s age is 20
    child read: the 2 people’s age is 21
    child read: the 3 people’s age is 22
    child read: the 4 people’s age is 23
    child read: the 5 people’s age is 24

    parent read: the first people,s age is 100
    umap
    umap ok

        回页首
    

    四、对mmap()返回地址的访问

    前面对范例运行结构的讨论中已经提到,linux采用的是页式管理机制。对于用mmap()映射普通文件来说,进程会在自己的地址空间新增一块空间,空间大小由mmap()的len参数指定,注意,进程并不一定能够对全部新增空间都能进行有效访问。进程能够访问的有效地址大小取决于文件被映射部分的大小。简单的说,能够容纳文件被映射部分大小的最少页面个数决定了进程从mmap()返回的地址开始,能够有效访问的地址空间大小。超过这个空间大小,内核会根据超过的严重程度返回发送不同的信号给进程。可用如下图示说明:

    注意:文件被映射部分而不是整个文件决定了进程能够访问的空间大小,另外,如果指定文件的偏移部分,一定要注意为页面大小的整数倍。下面是对进程映射地址空间的访问范例:

    #include <sys/mman.h>
    #include <sys/types.h>
    #include <fcntl.h>
    #include <unistd.h>
    typedef struct{
    char name[4];
    int age;
    }people;

    main(int argc, char** argv)
    {
    int fd,i;
    int pagesize,offset;
    people *p_map;

        pagesize = sysconf(_SC_PAGESIZE);
        printf("pagesize is %d/n",pagesize);
        fd = open(argv[1],O_CREAT|O_RDWR|O_TRUNC,00777);
        lseek(fd,pagesize*2-100,SEEK_SET);
        write(fd,"",1);
        offset = 0;        //此处offset = 0编译成版本1;offset = pagesize编译成版本2
        p_map = (people*)mmap(NULL,pagesize*3,PROT_READ|PROT_WRITE,MAP_SHARED,fd,offset);
        close(fd);
       
        for(i = 1; i<10; i++)
        {
                (*(p_map+pagesize/sizeof(people)*i-2)).age = 100;
                printf("access page %d over/n",i);
                (*(p_map+pagesize/sizeof(people)*i-1)).age = 100;
                printf("access page %d edge over, now begin to access page %d/n",i, i+1);
                (*(p_map+pagesize/sizeof(people)*i)).age = 100;
                printf("access page %d over/n",i+1);
        }
        munmap(p_map,sizeof(people)*10);
    

    }

    如程序中所注释的那样,把程序编译成两个版本,两个版本主要体现在文件被映射部分的大小不同。文件的大小介于一个页面与两个页面之间(大小为:pagesize2-99),版本1的被映射部分是整个文件,版本2的文件被映射部分是文件大小减去一个页面后的剩余部分,不到一个页面大小(大小为:pagesize-99)。程序中试图访问每一个页面边界,两个版本都试图在进程空间中映射pagesize3的字节数。

    版本1的输出结果如下:

    pagesize is 4096
    access page 1 over
    access page 1 edge over, now begin to access page 2
    access page 2 over
    access page 2 over
    access page 2 edge over, now begin to access page 3
    Bus error //被映射文件在进程空间中覆盖了两个页面,此时,进程试图访问第三个页面

    版本2的输出结果如下:

    pagesize is 4096
    access page 1 over
    access page 1 edge over, now begin to access page 2
    Bus error //被映射文件在进程空间中覆盖了一个页面,此时,进程试图访问第二个页面

    结论:采用系统调用mmap()实现进程间通信是很方便的,在应用层上接口非常简洁。内部实现机制区涉及到了linux存储管理以及文件系统等方面的内容,可以参考一下相关重要数据结构来加深理解。

    用法讲解2

    函数mmap是linux的一个系统函数。如下:

    函数原型:void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);

    配套函数原型:int munmap(void *addr, size_t length);

    头文件:#include <sys/mman.h>

    返回值:成功返回创建的映射区的首地址;失败返回宏MAP_FAILED。

    参数介绍:

    addr: 建立映射区的首地址,由Linux内核指定。使用时,直接传递NULL。

    length: 欲创建映射区的大小。

    prot: 映射区权限PROT_READ、PROT_WRITE、PROT_READ|PROT_WRITE。

    flags: 标志位参数(常用于设定更新物理区域、设置共享、创建匿名映射区);

    MAP_SHARED: 会将映射区所做的操作反映到物理设备(磁盘)上。

    MAP_PRIVATE: 映射区所做的修改不会反映到物理设备。

    fd: 用来建立映射区的文件描述符。

    offset: 映射文件的偏移(4k的整数倍)。

    munmap函数:

    同malloc函数申请内存空间类似的,mmap建立的映射区在使用结束后也应调用类似free的函数来释放。

    返回值:成功:0; 失败:-1

    接下来来使用mamp函数创建一个映射区:代码如下

    #include <stdio.h>
    
    #include <stdlib.h>
    
    #include <sys/mman.h>
    
    #include <sys/types.h>
    
    #include <unistd.h>
    
    #include <sys/stat.h>
    
    #include <fcntl.h>
    
    #include <string.h>
    
     
    
    void sys_err(char p[])//处理错误
    
    {
    
        perror(p);
    
        exit(1);
    
    }
    
    int main()
    
    {
    
        int o_ret = open("my_mmap.txt", O_CREAT | O_RDWR, 0644);//创建一个新文件
    
        if (-1 == o_ret) //错误检查是一个很好的习惯
    
        {
    
            sys_err("open ");
    
        }
    
     
    
        int f_ret = ftruncate(o_ret, 512);//在此文件大小的基础上扩展512字节大小,即文件现有大小为512字节(是个好函数,该记着。)
    
        if (-1 == f_ret)//成功返回0
    
        {
    
            sys_err("ftruncate ");
    
        }
    
     
    
        //———————————————————————————————————————————
    
        struct stat statbuf;
    
        int s_ret = stat("my_mmap.txt", &statbuf);
    
        if (-1 == s_ret)
    
        {
    
            sys_err("stat ");
    
        }
    
        //———————————————————————————————————————————
    
        /*
    
        上面这段代码是我用来获取文件大小的方法,并没有一个系统函数能够直接获取文件的大小。所以我利用stat结构体存储文件的相关信息,然后获取到文件的大小:statbuf.st_size
    
        */
    
        char *const address = (char *)mmap(NULL, statbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, o_ret, 0);
    
        //这里注意,我写的是char *const address这保证了address的值不变。
    
        if (MAP_FAILED == address)//若是mmap函数调用失败
    
        {
    
            sys_err("mmap ");
    
        }
    
        strcpy(address, "hallo, mmap");//能做这个操作是有先决条件的:映射区拥有写权限。
    
        int c_ret = close(o_ret);
    
        if (-1 == c_ret)
    
        {
    
            sys_err("close ");
    
        }
    
     
    
        int mun_ret = munmap(address, statbuf.st_size);//释放掉映射区。
    
        if (-1 == mun_ret)
    
        {
    
            sys_err("munmap ");
    
        }
    
     
    
        //printf("hello from mmap_test!\n");
    
        return 0;
    
    }
    

    很简单,这段代码编译之后就可以良好的运行下去。现在,我们来思考几个问题:

    可以open的时候O_CREAT一个新文件来创建映射区吗?

    如果open时O_RDONLY, mmap时PROT参数指定PROT_READ|PROT_WRITE会怎样?

    文件描述符先关闭,对mmap映射有没有影响?

    若是length参数大于文件的大小会怎样?

    对mmap函数的返回值进行越界操作(++,–)会怎样?

    如果文件偏移量为1000会怎样?

    如果不检测mmap的返回值,会怎样?

    答案:

    可以,只要是正确的文件描述符传递给mmap都行

    O_RDONLY代表只读,PROT_READ|PROT_WRITE代表读写,通过测试,我们就知道这是不行的,文件的权限代表着能对文件执行的操作;这里有一个结论:当第四个参数为MAP_SHARED时,映射区的权限应小于等于文件打开的权限。这样做的原因是出于对映射区的保护。而当第四个参数为MAP_PRIVATE时,就无所谓了,因为mmap中的权限时对内存的限制(映射区时在缓存区中创建的,缓存区是由内核管理调动的,不属于内存区域)。

    只要mmap调用成功,文件就可以立即关闭,映射区的释放、操作与文件的关闭无关,因为映射区创建成功后时通过另一种方式(指针)来管理的。

    注意:当用于创建映射区的文件大小为0时,不能创建映射区,会提示总线错误。所以:用于创建映射区的文件必须有实际的文件大小,并且创建的映射区大小不能超过文件大小。当出现总线错误的时候,多半是由于共享文件储存空间大小不正确引起的。

    会造成调用munmap函数失败,不能释放映射区。所以推荐使用const关键字限定指针的值。

    会出错,前面说过,文件的偏移量必须是4096(4k)的整数倍。

    会死的很难看,因为使用mmap函数出错的几率很高。

    在补充一下:

    p = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);看到标颜色的字词了吗?其中MAP_ANON是MAP_ANONYMOUS(MAP_ANONYMOUS //匿名映射,映射区不与任何文件关联。)的别称,不再被使用。因为我们创建一个匿名映射,所以我们不需要传递文件描述符。

    展开全文
  • 主要介绍了使用mmap实现多进程对大文件拷贝,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • 【计算机网络】零拷贝之MMAP

    千次阅读 2022-07-19 10:50:04
    mmap/munmap接口是用户空间的最常用的一个系统调用接口,无论是在用户程序中分配内存、读写大文件,链接动态库文件,还是多进程间共享内存,都可以看到mmap/munmap的身影。

    mmap/munmap接口是用户空间的最常用的一个系统调用接口,无论是在用户程序中分配内存、读写大文件,链接动态库文件,还是多进程间共享内存,都可以看到mmap/munmap的身影。

    mmap说明

    mmap系统调用并不是完全为了用于共享内存而设计的。它本身提供了不同于一般对普通文件的访问方式,进程可以像读写内存一样对普通文件的操作。而Posix或系统V的共享内存IPC则纯粹用于共享目的,当然mmap()实现共享内存也是其主要应用之一。

    mmap系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以像访问普通内存一样对文件进行访问,不必再调用read(),write()等操作。

    mmap/munmap函数声明如下:

    #include <sys/mman.h>
    
    void *mmap(void *addr, size_t length, int prot, int flags,
    		   int fd, off_t offset);
    int munmap(void *addr, size_t length);
    

    addr

    addr用于指定映射到进程空间的起始地址,为了应用程序的可移植性,一般设置为NULL,让内核来选择一个合适的地址。

    mmap函数就是为了寻找一个可用的地址空间,将文件描述符fd对应的文件从偏移量offset开始,copy文件的length个字符进入用户地址空间。将起始地址返回。为什么第一个参数还要传用户地址空间的指针呢?这不是骑驴找驴吗?

    这个addr参数可以NULL,这种情况我们比较容易理解。当addr不为NULL的时候,这其实是个建议查找地址,用来指导内存区定位的线索,是个用户指定的经验值。

    • 如果flags带上了MAP_FIXED标志,表示用户王八吃秤砣,铁了心要addr这个地址作为映射起始地址,直接把addr返回。
    • 如果没带上MAP_FIXED标志,则首先在建议地址addr附近寻找合适的区域。
    • addr为NULL,让内核自己选择。

    length、fd、offset

    将fd对应的文件,从offset位置开始,长为len的内容映射到内存地址空间。

    prot

    prot参数通常表示映射页面的的读写权限,可以有如下参数组合:

    • PROT_EXEC:表示映射的页面是可以执行的。
    • PROT_READ:表示映射的页面是可以读取的。
    • PROT_WRITE:表示映射的页面是可以写入的。
    • PROT_NONE:表示映射的页面是不可访问的。

    flags

    flags参数也是一个重要的参数,有如下常见的参数:

    • MAP_SHARED:创建一个共享映射的区域。多个进程可以通过共享映射方式来映射一个文件,这样其他进程也可以看到映射内容的改变,修改后的内容会同步到磁盘文件中。
    • MAP_PRIVATE:创建一个私有的写时复制的映射。多个进程可以通过私有映射的方式来映射一个文件,这样其他进程不会看到映射内容的改变,修改后的内容也不会同步到磁盘文件中。
    • MAP_ANONYMOUS:创建一个匿名映射,即没有关联到文件的映射。
    • MAP_FIXED:使用参数addr创建映射,如果内核无法映射指定地址addr,那么mmap会返回失败,参数addr要求按页对齐。如果addr和length指定的进程地址空间和已有的VMA区域重叠,那么内核会调用do_munmap()函数把这段重叠区域销毁,然后重新映射新的内容。
    • MAP_POPULATE:对于文件映射来说,会提前预读文件内容到映射区域,该特性只支持私用映射。

    mmap映射类型

    参数fd可以看出mmap映射是否和文件相关联,因此Linux内核中映射可以分为匿名映射和文件映射。

    • 匿名映射:没有映射对应的相关文件,这种映射的内存区域的内容会被初始化为0。
    • 文件映射:映射和实际文件相关联,通常是把文件的内容映射到进程地址空间,这样应用程序就可以像操作进程地址空间一样读写文件。

    最后根据文件关联性和映射区域是否共享等属性,又可以分为如下4种:

    私有映射共享映射
    匿名映射私有匿名映射-通常用于内存分配共享匿名映射-通常用于进程间共享内存
    文件映射私有文件映射-通常用于加载动态库共享文件映射-通常用于内存映射IO,进程间通信

    私有匿名映射

    当使用参数fd=-1且flags=MAP_ANONYMOUS | MAP_PRIVATE时,创建的mmap映射是私有匿名映射。私有匿名映射最常见的用途是在glibc分配大块内存中,当需要的分配的内存大于MMAP_THREASHOLD(128KB)时,glibc会默认使用mmap代替brk来分配内存。

    共享匿名映射

    当使用参数fd=-1且flags=MAP_ANONYMOUS | MAP_SHARED。在这种情况下,创建共享匿名映射。共享匿名映射让相关进程共享一块内存区域,通常用于父子进程的之间通信。

    创建共享匿名映射有如下两种方式:

    1. fd=-1且flags= MAP_ANONYMOUS|MAP_SHARED。在这种情况下,do_mmap_pgoff()->mmap()函数最终调用shmem_zero_setup()来打开一个"/dev/zero"特殊的设备文件。
    2. 另外一个是直接打开"/dev/zero"设备文件,然后使用这个文件句柄来创建mmap。

    私有文件映射

    私有文件映射时flags的标志位被设置为MAP_PRIVATE,那么就会创建私有文件映射。

    私有文件映射的最常用的场景是加载动态共享库。

    共享文件映射

    创建文件映射时flags的标志位被设置为MAP_SHARED,那么就会创建共享文件映射。如果prot参数指定了PROT_WRITE,那么打开文件需要制定O_RDWR标志位。共享文件映射通常有如下场景:

    • 读写文件:把文件内容映射到进程地址空间,同时对映射的内容做了修改,内核的回写机制(writeback)最终会把修改的内容同步到磁盘中。
    • 进程间通信:进程之间的进程地址空间相互隔离,一个进程不能访问到另外一个进程的地址空间。如果多个进程都同时映射到一个相同的文件,就实现了多进程间的共享内存的通信。如果一个进程对映射内容做了修改,那么另外的进程是可以看到的。

    mmap的使用

    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <sys/mman.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    #include <sys/syscall.h>
    
    int main() {
    
            int fd = open("/root/demo/aa", O_CREAT | O_RDWR, 777);
    
            size_t length = 1024;
            ftruncate(fd, length); // 一定要这句,否则会报Bus error
            char *addr = mmap(NULL, length, PROT_READ | PROT_WRITE,
                           MAP_SHARED, fd, 0);
    
            char* data = "hello";
            memcpy(addr, data, 5);
    
            char result[5];
            memcpy(result, addr, 5);
    
            printf("%s\n", result);
    
            sleep(100);
    
            munmap(addr, length);
            close(fd);
    
            return 0;
    }
    

    对一个空的新文件进行mmap前,需要往里面写入一点内容,否则会报错Bus error。发生错误的原因是因为mmap不能去扩展一个内容为空的新文件,因为大小为0,所有本没有与之对应的合法的物理页,不能扩展。

    解决方案有2个:

    • 一个就是上面的链接里的方案: 只需要在新创建的空文件中先写入一些数据即可;
    • 另外一个是通过ftruncate对新建立的文件进行扩展后再映射修改。

    查询进程所有的mmap映射:

    # cat /proc/1078/maps
    5636aadb5000-5636aadb6000 r--p 00000000 08:10 41881                      /root/demo/a.out
    5636aadb6000-5636aadb7000 r-xp 00001000 08:10 41881                      /root/demo/a.out
    5636aadb7000-5636aadb8000 r--p 00002000 08:10 41881                      /root/demo/a.out
    5636aadb8000-5636aadb9000 r--p 00002000 08:10 41881                      /root/demo/a.out
    5636aadb9000-5636aadba000 rw-p 00003000 08:10 41881                      /root/demo/a.out
    5636abae0000-5636abb01000 rw-p 00000000 00:00 0                          [heap]
    7f9f4e83f000-7f9f4e861000 r--p 00000000 08:10 12367                      /usr/lib/x86_64-linux-gnu/libc-2.31.so
    7f9f4e861000-7f9f4e9d9000 r-xp 00022000 08:10 12367                      /usr/lib/x86_64-linux-gnu/libc-2.31.so
    7f9f4e9d9000-7f9f4ea27000 r--p 0019a000 08:10 12367                      /usr/lib/x86_64-linux-gnu/libc-2.31.so
    7f9f4ea27000-7f9f4ea2b000 r--p 001e7000 08:10 12367                      /usr/lib/x86_64-linux-gnu/libc-2.31.so
    7f9f4ea2b000-7f9f4ea2d000 rw-p 001eb000 08:10 12367                      /usr/lib/x86_64-linux-gnu/libc-2.31.so
    7f9f4ea2d000-7f9f4ea33000 rw-p 00000000 00:00 0
    7f9f4ea3b000-7f9f4ea3c000 r--p 00000000 08:10 12234                      /usr/lib/x86_64-linux-gnu/ld-2.31.so
    7f9f4ea3c000-7f9f4ea5f000 r-xp 00001000 08:10 12234                      /usr/lib/x86_64-linux-gnu/ld-2.31.so
    7f9f4ea5f000-7f9f4ea67000 r--p 00024000 08:10 12234                      /usr/lib/x86_64-linux-gnu/ld-2.31.so
    7f9f4ea67000-7f9f4ea68000 rw-s 00000000 08:10 768                        /root/demo/aa
    7f9f4ea68000-7f9f4ea69000 r--p 0002c000 08:10 12234                      /usr/lib/x86_64-linux-gnu/ld-2.31.so
    7f9f4ea69000-7f9f4ea6a000 rw-p 0002d000 08:10 12234                      /usr/lib/x86_64-linux-gnu/ld-2.31.so
    7f9f4ea6a000-7f9f4ea6b000 rw-p 00000000 00:00 0
    7ffe68bf2000-7ffe68c13000 rw-p 00000000 00:00 0                          [stack]
    7ffe68dd5000-7ffe68dd8000 r--p 00000000 00:00 0                          [vvar]
    7ffe68dd8000-7ffe68dd9000 r-xp 00000000 00:00 0                          [vdso]
    

    查看内存映射的区域地址范围:

    # ll /proc/1078/map_files/
    total 0
    dr-x------ 2 root root  0 Jul 12 11:02 ./
    dr-xr-xr-x 9 root root  0 Jul 12 11:02 ../
    lr-------- 1 root root 64 Jul 12 11:02 5636aadb5000-5636aadb6000 -> /root/demo/a.out*
    lr-------- 1 root root 64 Jul 12 11:02 5636aadb6000-5636aadb7000 -> /root/demo/a.out*
    lr-------- 1 root root 64 Jul 12 11:02 5636aadb7000-5636aadb8000 -> /root/demo/a.out*
    lr-------- 1 root root 64 Jul 12 11:02 5636aadb8000-5636aadb9000 -> /root/demo/a.out*
    lr-------- 1 root root 64 Jul 12 11:02 5636aadb9000-5636aadba000 -> /root/demo/a.out*
    lr-------- 1 root root 64 Jul 12 11:02 7f9f4e83f000-7f9f4e861000 -> /usr/lib/x86_64-linux-gnu/libc-2.31.so*
    lr-------- 1 root root 64 Jul 12 11:02 7f9f4e861000-7f9f4e9d9000 -> /usr/lib/x86_64-linux-gnu/libc-2.31.so*
    lr-------- 1 root root 64 Jul 12 11:02 7f9f4e9d9000-7f9f4ea27000 -> /usr/lib/x86_64-linux-gnu/libc-2.31.so*
    lr-------- 1 root root 64 Jul 12 11:02 7f9f4ea27000-7f9f4ea2b000 -> /usr/lib/x86_64-linux-gnu/libc-2.31.so*
    lr-------- 1 root root 64 Jul 12 11:02 7f9f4ea2b000-7f9f4ea2d000 -> /usr/lib/x86_64-linux-gnu/libc-2.31.so*
    lr-------- 1 root root 64 Jul 12 11:02 7f9f4ea3b000-7f9f4ea3c000 -> /usr/lib/x86_64-linux-gnu/ld-2.31.so*
    lr-------- 1 root root 64 Jul 12 11:02 7f9f4ea3c000-7f9f4ea5f000 -> /usr/lib/x86_64-linux-gnu/ld-2.31.so*
    lr-------- 1 root root 64 Jul 12 11:02 7f9f4ea5f000-7f9f4ea67000 -> /usr/lib/x86_64-linux-gnu/ld-2.31.so*
    lrw------- 1 root root 64 Jul 12 11:02 7f9f4ea67000-7f9f4ea68000 -> /root/demo/aa*
    lr-------- 1 root root 64 Jul 12 11:02 7f9f4ea68000-7f9f4ea69000 -> /usr/lib/x86_64-linux-gnu/ld-2.31.so*
    lr-------- 1 root root 64 Jul 12 11:02 7f9f4ea69000-7f9f4ea6a000 -> /usr/lib/x86_64-linux-gnu/ld-2.31.so*
    

    查看内存映射地址范围的内容:

    # cat /proc/1078/map_files/7f9f4ea67000-7f9f4ea68000
    hello
    

    我们看到/root/demo/aa文件已经映射到内存中取了,占用空间0x7f9f4ea67000-0x7f9f4ea68000=4K,虽然申请的是1K,但是映射的分配是以页面为单位分配的,即最小分配4K。

    展开全文
  • Linux之mmap函数简介

    2021-05-14 12:12:32
    本文主要讲述mmap 函数的使用,与驱动中 mmap 函数的实现mmap 怎么使用,怎么实现,为什么 mmap 可以减少额外的拷贝?下面简单详情。一、 mmap 的使用#include void *mmap(void *addr, size_t len, int prot, int ...
  • mmap函数是unix/linux下的系统调用。当存在客户-服务程序中复制文件时候,其数据流如下,要经历四次数据复制,开销很大。...语法如下:*mmap(void *addr, size_t length, int prot, int flags,int fd, of...
  • mmap映射需要文件描述符的读权限(即open时需要加上读权限),不然不能使用。 所有内存保护组合与使用O_RDWR标记打开文件是兼容的。 一些硬件架构不允许对一个分页的只写访问。 prot值 描述 PROT_NONE ...
  • Linux之mmap

    2022-03-16 09:26:29
    创作人QQ:851301776,邮箱:lfr... 欢迎大家一起技术交流,本博客主要是自己学习的心得体会,只为每天进步一点点! 个人座右铭: 1.... 2.... 其中memory Mapping Segment主要用作mmap。 3.1 mmap系统调用说明 mm...
  • 一、准备 mmap 函数的参数、 二、mmap 函数远程调用、
  • Linux的mmap 源码分析

    2020-12-23 17:23:31
    Linux的mmap 源码分析 1、mmap基础概念 mmap() 将文件或设备映射到内存中。这是一种内存映射文件I / O的方法,实现用户进程和内核空间的映射。 1.1 功能特点: mmap分为文件映射和匿名映射 文件映射:将进程的虚拟...
  • linux下mmap()函数

    2021-05-13 10:30:02
    不过关于mmap的内存映射机制理解的不透彻,毕竟书上讲的东西有限,于是找到一篇解说的挺详细的文章,学习和分享一下.mmap: memory map在讲述文件映射的概念时,不可避免的要牵涉到虚存(SVR 4的VM)....
  • 1、如何将文件映射到内存 实际案例: (1)在访问某些二进制文件时,希望能把文件映射到内存中,可以实现随机访问。(framebuffer设备文件) ...使用标准库中mmap模块的mmap()函数,它需要一个打开的文...
  • mmap手册见先关章节。本文主要介绍了 mmap 的原理和使用方式,通过本文我们可以知道,使用 mmap 对文件进行读写操作时可以减少内存拷贝的次数,并且可以减少系统调用的次数,从而提高对读写文件操作的效率。 由于...
  • linux 进程通信之 mmap

    2021-05-19 01:33:08
    #include void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);int munmap(void *addr, size_t length);函数mmap:打开一个文件,指定一个文件的区域,作为一个区域,映射到内存中,...
  • linux 中mmap的用法

    2019-10-07 06:52:34
    函数:void *mmap(void *start,size_t length,int prot,int flags,int fd,off_t offsize); 参数start(dst):指向欲映射的内存起始地址,通常设为 NULL,代表让系统自动选定地址,映射成功后返回该地址。 参数...
  • MMAP使用(一、基本接口)

    千次阅读 2019-06-03 22:33:54
    每个参数又有什么作用? 回答: 1.mmap是什么?有什么用? mmap将一个文件或者其它对象映射进内存。mmap操作提供了一种机制,让用户程序直接访问设备内存,这种机制,相比较在用户空间和内核空间互相拷贝数据,...
  • 内存映射mmap

    2021-05-11 07:39:10
    什么是mmap2. 使用方法2.1. mmap构造器的格式2.2. 例子12.3. 例子23. 其它4. 参考资料什么是mmap通常在Unix系统里有两种操作的数据类型:内存地址和流文件(stream)。通过操作内存地址的方法涉及的操作有:pointers, ...
  • C语言 mmap函数

    千次阅读 2022-02-07 15:10:13
    函数参数5.返回值6.系统调用总结 一、mmap简介 mmap是一种内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系。实现这样的映射...
  • mmap函数

    2020-10-26 21:13:02
    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:...
  • python -mmap-内存共享

    2020-12-21 18:44:18
    一些工程是用C#编写的,但是用到了机器学习。当前很多框架都是基于python的,...其实python中就有一个函数这样的函数mmap来实现内存共享。内存共享是两个不同的进程共享内存的意思:同一块物理内存被映射到两个进程...
  • iOS之深入解析文件内存映射MMAP

    万次阅读 热门讨论 2021-08-31 19:33:16
    mmap 内存映射原理 进程启动映射过程,并在虚拟地址空间中为映射创建虚拟映射区域: 进程在用户空间调用库函数 mmap,原型:void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset...
  • Linux内存映射-mmap

    2021-03-18 22:02:35
    1.mmap驱动源码 #include <linux/miscdevice.h> #include <linux/delay.h> #include <linux/kthread.h> #include <linux/kernel.h> #include <linux/module.h> #include <...
  • mmap 模块提供“内存映射的文件对象”,mmap 对象可以用在使用 plain string 的地方,mmap 对象和 plain string 的区别是:mmap 对象不提供字符串对象的方法;mmap 对象是可变的,而 str 对象是不可变的mmap 对象...
  • LINUX 中的mmap浅析

    千次阅读 2021-05-17 14:54:10
    LINUX 中的mmap浅析一、mmap基本原理和分类在LINUX中我们可以使用mmap用来在进程虚拟地址空间中分配创建一片虚拟内存地址映射其可以是1、文件映射使用文件内容初始化内存2、匿名映射初始化全为0的内存空间(calloc也...
  • linux mmap使用例子

    2021-01-14 16:22:43
    linux mmap使用例子转自:http://blog.163.com/zhaoxin851055@126/blog/static/8112929820122872212734/#include/* for mmap and munmap */#include/* for open */#include/* for open */#include/* f...
  • mmap详细解析及式例

    2021-12-01 10:38:38
    进程在用户空间调用库函数mmap,原型 : void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset); 2. 在当前进程的虚拟地址空间中,寻找一段空闲的满足要求的连续的虚拟地址 3. ...
  • mmap基础概念 mmap内存映射原理 mmap和常规文件操作的区别 mmap优点总结 mmap相关函数 mmap使用细节 性能总结 mmap基础概念 mmap是一种内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间,...
  • mmap针对Windows和Unix的版本在具体实现上有所不同,对于Windows版本,当length参数比file本身size大的时候,会自动扩展file为指定length大小;而Unix版本不支持自动扩展,即length只能小于等于size of file,如果...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 14,180
精华内容 5,672
关键字:

length参数 mmap