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 }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)