1.mmap-创建内存映射
作用:将磁盘文件的数据映射到内存,用户通过内存就能修改磁盘文件
函数原型:
void *mmap{ void *addr; //映射区首地址,传NULL size_t length; //映射区的大小 //会自动调为4k的整数倍 //不能为0 //一般文件多大,length就指定多大 int prot; //映射区权限 //PROT_READ 映射区比必须要有读权限 //PROT_WRITE //PROT_READ | PROT_WRITE int flags; //标志位参数 //MAP_SHARED 修改了内存数据会同步到磁盘 //MAP_PRIVATE 修改了内存数据不会同步到磁盘 int fd; //要映射的文件对应的fd off_t offset; //映射文件的偏移量,从文件的哪里开始操作 //映射的时候文件指针的偏移量 //必须是4k的整数倍 //一般设置为0 }
返回值:
映射区的首地址-调用成功
调用失败: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)基本代码
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <string.h> #include <sys/mman.h> #include <fcntl.h> int main(int argc, const char* argv[]) { //打开一个文件 int fd=open("english.txt",O_RDWR); int len=lseek(fd,0,SEEK_END); //创建内存映射区 void *ptr=mmap(NULL,len,PROT_READ | PROT_WRITE,MAP_SHARED,fd,0); if(ptr==MAP_FAILED) { perror("mmap error"); exit(1); } printf("%s",(char*)ptr); //释放内存映射区 munmap(ptr,len); close(fd); return 0; }
运行结果:
wangkai@wangkai-HP-242-G1-Notebook-PC:~/0110/6Day$ ./mmap sadaskdkasj sdasdasd asdasdsadasds dasdas
(2)使用mmap进行有血缘关系的进程间通信
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <string.h> #include <sys/mman.h> #include <sys/wait.h> #include <fcntl.h> /* 利用mmap进行父子间通信,效率比文件I/O高, 数据不是从磁盘上读,而是从内存上读写 不阻塞,所以读之前一定要写好 */ int main(int argc, const char* argv[]) { //打开一个文件 int fd=open("english.txt",O_RDWR); int len=lseek(fd,0,SEEK_END); //创建内存映射区 void *ptr=mmap(NULL,len,PROT_READ | PROT_WRITE,MAP_SHARED,fd,0); if(ptr==MAP_FAILED) { perror("mmap error"); exit(1); } close(fd); //创建子进程 pid_t pid=fork(); if(pid==-1) { perror("fork error"); exit(1); } if(pid>0) { //写数据 strcpy((char*)ptr,"我是你爸爸"); //回收子进程 wait(NULL); } else if(pid==0) { //读数据 printf("%s\n",(char*)ptr); } printf("%s",(char*)ptr); //释放内存映射区 munmap(ptr,len); return 0; }
(3)创建匿名映射区
int len=4096; //创建匿名内存映射区,不指定文件 void *ptr=mmap(NULL,len,PROT_READ | PROT_WRITE,MAP_SHARED | MAP_ANON,-1,0);
效果和(2)一样。
(4)没有血缘关系的进程间通信
写代码
//mmap_w_ipc.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/mman.h> #include <fcntl.h> int main(int argc, char *argv[]) { int fd = open("temp", O_RDWR | O_CREAT, 0664); void* ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if(ptr == MAP_FAILED) { perror("mmap"); exit(1); } while(1) { char*p = (char*)ptr; p += 1024; strcpy(p, "hello parent, i am your 朋友!!!\n"); sleep(2); } // 释放 int ret = munmap(ptr, 8192); if(ret == -1) { perror("munmap"); exit(1); } return 0; }
读代码
//mmap_r_ipc.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/mman.h> #include <fcntl.h> int main(int argc, char *argv[]) { int fd = open("temp", O_RDWR | O_CREAT, 0664); ftruncate(fd, 4096); int len = lseek(fd, 0, SEEK_END); void* ptr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if(ptr == MAP_FAILED) { perror("mmap"); exit(1); } while(1) { sleep(1); printf("%s\n", (char*)ptr+1024); } // 释放 int ret = munmap(ptr, len); if(ret == -1) { perror("munmap"); exit(1); } return 0; }
小结
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. 父子进程永远共享的东西?
文件描述符内存映射区