• mmap 创建共享内存映射


    所谓内存映射指的是 让一个磁盘文件与内存中的一个缓冲区相映射,进程访问这块内存时,就等同于访问文件对应映射部分,不必再调用 read / write 。

    我们可以使用mmap函数来建立内存和文件某一部分的映射关系。


    目录

    一、共享内存映射的创建 / 释放

    1、创建共享内存映射:mmap​​​​​​​

    2、释放共享内存映射:munmap

    二、mmap使用及其注意事项

    1、mmap使用

    (1) 打开文件

    (2) mmap建立映射

    (3) 通过映射向文件写入内容

    (4) 完整代码

    2、mmap使用注意事项

    (1) 映射区只要建立成功,文件可以立即关闭

    (2) 映射的文件大小必须大于0,否则会报总线错误

    (3) 文件偏移量必须为0或者4K的整数倍

    (4) 映射空间大小可以大于文件大小,但要注意不要访问区外部分

    (5) 设置的映射空间大小 ≠ 实际分配的映射空间大小


    一、共享内存映射的创建 / 释放

    1、创建共享内存映射:mmap

    mmap 函数的作用是创建共享内存映射。mmap函数的参数较多,几乎每一个参数都有注意事项,下面介绍的重点是mmap函数的参数。

    (1) addr

    用户可以手动指定要映射的内存地址,一般设置为NULL,让OS自动选择合适的内存地址,如果最后映射建立成功,mmap会返回内存中映射区的首地址。

    (2) length

    为内存中映射地址空间分配的字节数(length > 0)。这里分为了两种情况:

    • length > 文件映射部分大小
    • length < 文件大小

    当 length < 文件映射部分大小 时,文件有一部分无法映射到内存。

    当 length > 文件映射部分大小 时,有一部分无法映射到文件,这就意味着,即便向这部分内存写入内容,也不会反馈给文件。

    因此,一般建议设置的映射空间大小直接和文件大小保持一致。文件大小的计算可以使用lseek函数。

    1. // 起始偏移量为0,将文件指针移动到末尾(SEEK_END)
    2. // 返回的结果就是 文件指针相对于起始位置的字节数
    3. int size = lseek(fd, 0, SEEK_END);

    (3) prot

    指定内存映射空间的访问权限。其实就是要以何种形式来访问这块映射空间,如可读、可写、可执行等,可选值如下:

    可选值含义
    PROT_READ可读
    PROT_WRITE可写
     PROT_EXEC可执行
    PROT_NONE不可访问

    (4) flags

    指定内存映射空间的映射方式。可以是共享,代表其他进程可以看到;可以是私有,代表其他进程看不到;也可以是匿名,一般用于有血缘关系之间的进程。可选值如下:

    可选值含义
    MAP_SHARED共享
    MAP_PRIVATE私有
    MAP_ANONYMOUS匿名

    注意: MAP_SHARED 和  MAP_PRIVATE必选其一

    (5) fd

    指定要映射的文件。当一个文件被成功打开的时候,就会有一个文件描述符来唯一的指向该文件。如果是匿名映射,填 -1

    :为什么建立内存映射需要打开文件?

    :在创建映射区的过程中,隐藏着一次对映射文件的读操作,目的是将部分文件内容读取到映射区。(读取多少,取决于偏移量的设置)

    (6) offset

    表示映射文件的偏移量。设置为0表示从头开始映射。当你向映射区写入内容的时候,其实是向文件的起始映射位置开始写入,而不是从文件起始位置开始写入。

    映射部分:向映射区写入的内容,会映射到文件的对应部分

    剩余部分:内存是以页为单位进行分配的,一页大小是4K,映射区的大小如果小于4K,剩下没有映射的就是“剩余部分”,访问该部分的时候,不会有任何问题,因为这块内存还在一页的范围里。但是你向该部分写入的内容,不会映射到文件,因为在文件里不存在对应的映射区域。

    区外部分:该部分是内存没有分配给你的,访问这部分会报总线错误。

    (6) 返回值

     映射创建成功,返回创建映射区的首地址,失败返回 MAP_FAILED(((void *) -1)),设置errno值

    2、释放共享内存映射:munmap

    释放共享内存映射的时候,需要提供映射区的首地址,以及你手动分配的大小,注意这里要填的不是实际分配的大小,而是 mmap 第二个参数填入的值。

    二、mmap使用及其注意事项

    1、mmap使用

    (1) 打开文件

    映射文件必须以读写 (O_RDWR) 的形式打开,同时该文件的大小必须要大于0,否则会出现

    1. int fd = open("./log.txt", O_RDWR);
    2. if(fd < 0)
    3. {
    4. perror("open");
    5. return -1;
    6. }

    (2) mmap建立映射

    针对mmap 的各个参数,设置如下:

    • 第一个参数设为NULL,交由OS来分配内存
    • 第二个参数设为文件大小
    • 第三个参数设为可写 PROT_WRITE
    • 第四个参数设为共享 MAP_SHARED
    • 第五个参数填入上面得到的文件描述符
    • 第六个参数设为0,代表从文件起始位置开始映射
    1. int len = lseek(fd, 0, SEEK_END);
    2. void* addr = mmap(NULL, len, PROT_WRITE, MAP_SHARED, fd, 0);
    3. if(addr == MAP_FAILED)
    4. {
    5. perror("mmap");
    6. return -1;
    7. }

    (3) 通过映射向文件写入内容

    我们向内存映射空间中写入一个字符串,然后看一下文件中是否有对应的字符串

    1. const char* str = "hello, world";
    2. memcpy(addr, str, strlen(str));

    (4) 完整代码

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. int main(){
    8. int fd = open("./log.txt", O_RDWR);
    9. if(fd < 0)
    10. {
    11. perror("open");
    12. return -1;
    13. }
    14. int len = lseek(fd, 0, SEEK_END);
    15. void* addr = mmap(NULL, len, PROT_WRITE, MAP_SHARED, fd, 0);
    16. if(addr == MAP_FAILED)
    17. {
    18. perror("mmap");
    19. return -1;
    20. }
    21. const char* str = "hello, world";
    22. memcpy(addr, str, strlen(str));
    23. return 0;
    24. }

    2、mmap使用注意事项

    (1) 映射区只要建立成功,文件可以立即关闭

    在创建映射区的过程中,隐藏着一次对映射文件的读操作,目的是将部分文件内容读取到映射区。映射建立成功也就意味着读写完毕,此时只需要向内存映射中写入即可。

    (2) 映射的文件大小必须大于0,否则会报总线错误

    如果映射文件的大小为0,那就说明我们无论怎么向映射里写入内容,都不会反馈到文件中,那就失去了映射的意义。

    (3) 文件偏移量必须为0或者4K的整数倍

    (4) 映射空间大小可以大于文件大小,但要注意不要访问区外部分

    映射空间的大小可以大于文件大小,只是有一部分空间无法映射到文件,向这部分空间写入的内容不会反馈给文件。

    (5) 设置的映射空间大小 ≠ 实际分配的映射空间大小

    实际在分配内存的时候,是以页为单位进行分配的,每页大小是4K,实际大小会根据文件大小来定。并不是你设置了多少,内存就给你分配多少。

    如果文件大小是2000字节,即便mmap第二个参数填入的是6000,映射空间的大小就是 4K,此时需要注意,超出4K部分属于区外部分,访问区外部分会报总线错误。

    如果文件大小是6000字节,大于了一页的大小,此时就会分配两页大小,映射空间大小就是8K。

  • 相关阅读:
    2022年最新西藏建筑八大员(市政)模拟考试题库及答案
    测试计划包括哪些内容?目的和意义是什么?
    elasticsearch-reinde 实际操作步骤
    docker 环境部署
    MATLAB算法实战应用案例精讲-【概念篇】大模型
    释放搜索潜力:基于Milvus快速搭建语义检索系统(快速版),让信息尽在掌握
    灵境:为每个一需要的人配上一个后台
    模糊测试面面观 | 模糊测试是如何发现异常情况的?
    pygame4 练习课
    可怕!.Net 8正式发布了,.Net野心确实不小!
  • 原文地址:https://blog.csdn.net/challenglistic/article/details/128162085