linux 内存映射

内存映射:Memory-mapped I/O

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

进程的虚拟地址空间会对应于实际的物理内存当中。。映射到  动态库(共享库)之中,内存动态保存文件的数据。

mmap函数: 

复制代码
 1 /*
 2     man 2 mmap:(m:memory内存  map:mapping映射)    kernel:内核
 3     #include <sys/mman.h>
 4     void* mmap(void* addr, size_t length, int prot, int flags, int fd, off_offset);
 5         - 功能: 将一个文件或者设备的数据映射到内存中
 6         - 参数:
 7             - void* addr:NULL,由内核指定
 8             - length: 要映射的数据的长度,这个值不能为0,一般使用文件的长度.(分配的大小为最少(分页)的整数倍)
 9                 获取文件的长度:stat lseek
10             - prot:对申请的内存映射区的操作权限
11                 PROT_EXEC: 可执行权限
12                 PROT_READ: 读权限
13                 PROT_WRITE:写权限
14                 PROT_NONE: 没有权限
15                 要操作映射区,必须要有读的权限
16                 PROT_READ、 PROT_READ | PROT_WRITE 一般这两种形式 
17             - flags:
18                 MAP_SHARED: 映射区的数据会自动和磁盘文件进行同步,进程间通信,必须要设置这个选项
19                 MAP_PRIVATE: 不同步,内存映射区的数据改变了,对原来的文件不会修改,会重新创建一个新的文件(copy on write)
20             - fd:需要映射的文件的文件描述符
21                 - 通过open得到,open的是一个磁盘文件
22                 - 注意:文件的大小不能为0,open指定的权限不能和prot参数有冲突
23                     prot:PROT_READ                   open: 只读/读写
24                     prot:PROT_READ | PROT_WRITE      open: 读写
25                     prot权限要小于等于 open的权限
26             - off_offset:偏移量,一般不用.必须指定的是 4K的整数倍,0表示不偏移.
27         - 返回值:
28             成功:返回创建的内存的首地址     
29             失败:返回MAPFAILED : (void*) -1
30 
31     int munmap(void* addr, size_t length);
32         - 功能: 释放内存映射
33         - 参数:
34             - addr:要释放的内存的首地址
35             - length:要释放的内存的大小,要和mmap函数中的length参数的值一样
36 */
复制代码

父子进程间通信:

复制代码
 1 /*
 2     使用内存映射实现进程间通信:
 3         1.有关系的进程(父子进程)
 4             - 还没有子进程的时候
 5                 - 通过唯一的父进程,先创建内存映射区
 6             - 有了内存映射区以后,再创建子进程
 7             - 父子进程共享创建的内存映射区
 8         2.没有关系的进程间通信
 9             - 准备一个大小不是0的磁盘文件
10             - 进程1 通过磁盘文件创建内存映射区
11                 - 得到一个操作这块内存的指针
12             - 进程2 通过磁盘文件创建内存映射区
13                 - 得到一个操作这块内存的指针
14             - 使用内存映射区通信
15         注意:内存映射区通信,是非阻塞的
16 */
17 #include <stdio.h>
18 #include <sys/mman.h>
19 #include <fcntl.h>
20 #include <sys/types.h>
21 #include <unistd.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #include <wait.h>
25 int main()
26 {
27     //1.打开一个文件
28     int fd = open("test.txt",O_RDWR);
29     int size = lseek(fd, 0, SEEK_END);//返回值获取偏移量:获取文件的大小
30     //2.创建内存映射区
31     void* ptr = mmap(NULL, size, PROT_READ | PROT_WRITE,MAP_SHARED,fd,0);
32     if(ptr == MAP_FAILED)//MAP_FAILED:void* -1
33     {
34         perror("mmap");
35         exit(0);
36     }
37     //3.创建子进程
38     pid_t pid = fork();
39     if(pid > 0)
40     {
41         wait(NULL);//回收完子进程资源后再读取数据
42         //父进程
43         char buf[64];
44         strcpy(buf, (char*)ptr);
45         printf("read data: %s\n",buf);
46     }
47     else if(pid == 0)
48     {
49         //子进程
50         strcpy((char*)ptr, "nihao a, dad!");//ptr为 void型  strcpy需要char型 因此强制转换
51     }
52     //关闭内存映射区
53     munmap(ptr, size);
54     return 0;
55 }
复制代码

 内存映射的注意事项: 

复制代码
1.如果对 mmap的返回值(ptr)做++操作(ptr++),munmap是否能够成功?
void* ptr = mmap(...);
prt++;可以对其进行++操作
munmap(ptr,len);//错误,要保存地址,释放的时候释放正确的地址

2.如果open时 O_RDONLY, mmap时prot参数指定 PROT_READ | PROT_WRITE会怎么样?
错误,返回MAP_FAILED(将-1转换为 void*)
open()函数中权限建议和prot参数的权限保持一致  prot权限 <= open权限

3.如果文件偏移量(offset)为1000会如何?
偏移量必须是4K的整数倍,否则返回MAP_FAILED

4.mmap什么情况下会调用失败?
    - 第二个参数:length = 0
    - 第三个参数:prot
        - 只指定了写权限(必须要有读权限)
        - prot权限 <= open权限

5.可以open的时候O_CREAT一个新文件来创建映射区吗?
    - 可以的,打死你创建的文件的大小如果为0的话,肯定不行
    - 可以对新的文件进行扩展
        - lseek() 移动偏移量
        - truncate()
    
6.mmap后关闭文件描述符,对mmap映射有没有影响
    int fd = open("xxx");
    mmap(...);
    close(fd);
    映射区还存在,创建映射区的fd被关闭,无影响。

7.对ptr越界操作会怎样?
    void* ptr = mmap(NULL,100...);//默认开辟分页大小 普遍4K 不同系统大小不同
    越界操作: 操作的是非法的内存 - 段错误
复制代码

内存映射实现文件拷贝:基本不使用,因为内存太小

复制代码
 1 //使用内存映射实现文件拷贝的功能
 2 /*
 3     1.对原始的文件进行内存映射
 4     2.创建一个新的文件(拓展该文件)
 5     3.把新文件的数据映射到内存中
 6     4.通过内存拷贝将第一个文件的内存数据拷贝到新的文件内存中
 7     5.释放资源
 8 */
 9 #include <stdio.h>
10 #include <sys/mman.h>
11 #include <fcntl.h>
12 #include <sys/stat.h>
13 #include <sys/types.h>
14 #include <unistd.h>
15 #include <string.h>
16 #include <stdlib.h>
17 int main()
18 {
19     //1.对原始的文件进行内存映射
20     int fd = open("english.txt",O_RDWR);
21     if(fd == -1)
22     {
23         perror("open");
24         exit(0);
25     }
26     //获取原始文件的大小
27     int len = lseek(fd,0,SEEK_END);
28     //2.创建一个新文件(预先拓展该文件)
29     int fd1 = open("cpy.txt",O_RDWR | O_CREAT,0664);
30     if(fd1 == -1)
31     {
32         perror("open");
33         exit(0);
34     }
35     //对新创建的文件进行拓展
36     truncate("cpy.txt", len);
37     write(fd1," ",1);
38     //3.分别做内存映射 
39     void* ptr = mmap(NULL, len, PROT_READ| PROT_WRITE, MAP_SHARED,fd,0);
40     void* ptr1 = mmap(NULL, len, PROT_READ| PROT_WRITE, MAP_SHARED,fd1,0);
41     if(ptr == MAP_FAILED)
42     {
43         perror("mmap");
44         exit(0);
45     }
46     if(ptr1 == MAP_FAILED)
47     {
48         perror("mmap");
49         exit(0);
50     }
51     //内存拷贝 
52     memcpy(ptr1, ptr, len);//往哪拷贝,从那拷贝,拷贝长度
53     //释放资源  谁先打开后释放  后打开先释放
54     munmap(ptr1,len);
55     munmap(ptr,len);
56     close(fd1);
57     close(fd);
58     return 0;
59 }
复制代码

匿名映射:

复制代码
 1 /*
 2     匿名映射:不需要文件实体进行一个内存映射
 3 */
 4 // 编译的时候  MAP_ANONYMOUS  未定义的话,在gcc编译时末尾加上-std=gnu99
 5 #include <stdio.h>
 6 #include <sys/mman.h>
 7 #include <fcntl.h>
 8 #include <sys/stat.h>
 9 #include <sys/types.h>
10 #include <unistd.h>
11 #include <string.h>
12 #include <stdlib.h>
13 #include <sys/wait.h>
14 int main()
15 {
16     //1.创建匿名内存映射区
17     int len = 4096;
18     void* ptr =mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1,0);
19     if(ptr == MAP_FAILED)
20     {
21         perror("mmap");
22         exit(0);
23     }
24     //父子间通信
25     pid_t pid = fork();
26     if(pid > 0)
27     {
28         //父进程
29         strcpy((char*)ptr,"hello,world");
30         wait(NULL);
31     }
32     else if(pid == 0)
33     {
34         //子进程
35         sleep(1);
36         printf("%s\n",(char*)ptr);
37     }
38     //释放内存映射区
39     int ret = munmap(ptr,len);
40     if(ret == -1)
41     {
42         perror("munmap");
43         exit(0);
44     }
45     return 0;
46 }
复制代码

posted on   廿陆  阅读(221)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示