一、mmap 函数
1、函数原型:
- #include <sys/mman.h>
- void *mmap(void *addr, size_t length, int prot, int flags,
- int fd, off_t offset);
2、返回值:
若执行成功: 返回创建的映射区的首地址;
若执行失败: 返回 MAP_FAILED 宏
3、参数:
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 的函数来解释。
返回值:
- int munmap(void * addr, size_t length);
若成功,返回 0 ; 若失败,返回 -1。
- #include<sys/stat.h>
- #include<fcntl.h>
- #include<stdlib.h>
- #include<string.h>
- #include<stdio.h>
- void sys_error(char *str)
- {
- perror(str);
- exit(1);
- }
- int main(void)
- {
- char *mem;
- int len = 0;
- int fd = open("hello123",O_RDONLY|O_CREAT|O_TRUNC,0644);
- if( fd == -1 )
- sys_error("open error");
- ftruncate(fd,20);
- /*
- len = lseek(fd,3,SEEK_SET); // 获取文件大小,根据文件大小建立映射区
- write(fd,"e",1); // 实质性完成文件拓展
- printf("The length of file = %d\n",len);
- */
- mem = mmap(NULL,20,PROT_READ,MAP_SHARED,fd,0);
- if( mem == MAP_FAILED ) // 出错判断
- sys_error("mmap error");
- mem++;
- close(fd);
- strcpy(mem,"xxx");
- printf("%s\n",mem);
- if( munmap(mem,4)<0 )
- sys_error("munmap error");
- return 0;
- }
三、mmap 的使用注意事项
1、创建映射区时,隐含着对映射文件的一次读操作。
2、当 MAP_SHARED时,要求映射区的权限应 <= 文件的打开权限(出于对映射区的保护)。而 MAP_PRIVATE则无所谓,因为 mmap 中的权限是对内存的限制。
3、映射区的释放与文件关闭无关。只要映射建立成功,则文件可以立即关闭。
4、特别注意:当映射文件大小为 0 时,不能创建映射区。所以,用于映射的文件必须有实际大小!mmap 使用时常常会出现总线错误,通常是由于共享文件存储空间大小引起的。
5、munmap 传入的地址一定是 mmap 的返回地址。杜绝指针 ++ ,-- 操作。
6、文件偏移量必须为 4k 的整数倍。
7、mmap 创建映射区出错概率非常高,一定要检查返回值,确保映射区成功建立,并进行后续操作。
四、mmap 父子进程间通信
父子等有血缘关系的进程之间也可以通过 mmap 建立的映射区来完成数据通信。但是相应的要在创建映射区的时候指定对应的标志位参数 flags:
MAP_PROVATE(私有映射):父子进程各自独占映射区
MAP_SHARED(共享进程) :父子进程共享映射区
结论:
父子进程共享:
(1)、打开的文件
(2)、mmap 建立的映射区(但必须使用 MAP_SHARED)
- #include<unistd.h>
- #include<sys/mman.h>
- #include<sys/types.h>
- #include<sys/stat.h>
- #include<fcntl.h>
- #include<stdlib.h>
- #include<stdio.h>
- #include<string.h>
- int var = 100;
- int main(void)
- {
- int *p;
- pid_t pid;
- int fd;
- fd = open("temp",O_RDWR|O_CREAT|O_TRUNC,0644);
- if( fd == -1 )
- {
- perror("open error");
- exit(1);
- }
- // 这里程序在运行期间 temp 文件还存在,运行完成后,推出进程后,temp文件被删除掉
- unlink("temp"); // 删除临时文件目录项,使之具备被释放的条件
- ftruncate(fd,4);
- p =(int*)mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
- if( p == MAP_FAILED )
- {
- perror("mmap error");
- exit(1);
- }
- close(fd); // 映射区建立完毕,即可关闭文件
- pid = fork(); // 创建子进程
- if( pid == 0 )
- {
- var = 1000;
- *p = 2000;
- printf("child,*p = %d,var = %d\n",*p,var);
- }
- else
- {
- sleep(1);
- printf("parent,*p = %d,var = %d\n",*p,var);
- wait(NULL);
- int ret = munmap(p,4);
- if( ret == -1 )
- {
- perror("munmao error");
- exit(1);
- }
- }
- return 0;
- }
五、匿名映射
使用 MAP_ANOYYMOUS(MAP_ANON),如:
- int * p = mmap(NULL, 4, PROT_READ | PROT_WRITE, MAXP_SHARED | MAP_ANONYMOUS, -1, 0);
注意:
- #include<unistd.h>
- #include<sys/mman.h>
- #include<sys/types.h>
- #include<sys/stat.h>
- #include<fcntl.h>
- #include<stdlib.h>
- #include<stdio.h>
- #include<string.h>
- int var = 100;
- int main(void)
- {
- int *p;
- pid_t pid;
- p =(int*)mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANON,-1,0);
- if( p == MAP_FAILED )
- {
- perror("mmap error");
- exit(1);
- }
- pid = fork(); // 创建子进程
- if( pid == 0 )
- {
- var = 1000;
- *p = 2000;
- printf("child,*p = %d,var = %d\n",*p,var);
- }
- else
- {
- sleep(1);
- printf("parent,*p = %d,var = %d\n",*p,var);
- wait(NULL);
- int ret = munmap(p,4);
- if( ret == -1 )
- {
- perror("munmao error");
- exit(1);
- }
- }
- return 0;
- }
MAP_ANONYMOUS 和 MAP_ANON 这两个宏是 Linux操作系统特有的宏,在类 UNIX 系统中无该宏定义,可使用如下两步来完成匿名映射区的建立。
(1)、fd = open("/dev/zero",O_RDWR);
(2)、p = mmap(NULL,size,PROT_READ|PROT_WRTIE,MAP_SHARED,fd,0);
六、mmap 无血缘关系进程间通信
实质上 mmap 是内核借助帮助文件帮我们创建了一个映射区,多个进程之间利用该映射区完成数据的传递。由于内核控件多进共享,因此,无血缘关系的进程间也可以使用 mmap 来完成通信。只要设置相应的标志位参数 flags 即可。若要实现共享,当然使用 MAP_SHARED 了。
非血缘关系写进程:
- #include<unistd.h>
- #include<sys/mman.h>
- #include<sys/types.h>
- #include<sys/stat.h>
- #include<fcntl.h>
- #include<stdlib.h>
- #include<string.h>
- #include<stdio.h>
- struct STU
- {
- int id;
- char name[20];
- char sex;
- };
- void sys_err(char *error)
- {
- perror(error);
- exit(1);
- }
- int main(int argc,char *argv[])
- {
- int fd;
- struct STU student = { 10,"xiaoming",'m'};
- char *mm;
- if( argc < 2 )
- {
- printf("./a.out file_shared\n");
- exit(-1);
- }
- fd = open(argv[1],O_RDWR|O_CREAT,0644);
- if( fd == -1 )
- sys_err("open error");
- ftruncate(fd,sizeof(student));
- mm = mmap(NULL,sizeof(student),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
- if( mm == MAP_FAILED )
- sys_err("mmap error");
- close(fd);
- while(1)
- {
- memcpy(mm,&student,sizeof(student));
- student.id++;
- sleep(1);
- }
- int ret = munmap(mm,sizeof(student));
- if( ret == -1 )
- sys_err("munmap error");
- return 0;
- }
非血缘关系读进程:
- #include<unistd.h>
- #include<sys/mman.h>
- #include<sys/types.h>
- #include<sys/stat.h>
- #include<fcntl.h>
- #include<stdlib.h>
- #include<string.h>
- #include<stdio.h>
- struct STU
- {
- int id;
- char name[20];
- char sex;
- };
- void sys_err(char *error)
- {
- perror(error);
- exit(1);
- }
- int main(int argc,char *argv[])
- {
- int fd;
- struct STU student;
- struct STU *mm;
- if( argc < 2 )
- {
- printf("./a.out file_shared\n");
- exit(-1);
- }
- fd = open(argv[1],O_RDONLY);
- if( fd == -1 )
- sys_err("open error");
- mm = mmap(NULL,sizeof(student),PROT_READ,MAP_SHARED,fd,0);
- if( mm == MAP_FAILED )
- sys_err("mmap error");
- close(fd);
- while(1)
- {
- printf("id = %d\tname=%s\t%c\n",mm->id,mm->name,mm->sex);
- sleep(2);
- }
- int ret = munmap(mm,sizeof(student));
- if( ret == -1 )
- sys_err("munmap error");
- return 0;
- }
七、文件通信
两个非血缘关系的进程可以通过文件的方式来进行通信。
strace + 文件名 能够追踪到当前程序执行过程中用到的系统调用。
注意:文件的一些操作都是用 mmap 来实现的。
- /*
- 先执行,将数据写入文件 test.txt
- */
- #include<unistd.h>
- #include<sys/types.h>
- #include<sys/stat.h>
- #include<fcntl.h>
- #include<stdlib.h>
- #include<stdio.h>
- #include<string.h>
- #define N 5
- void sys_err(char *error)
- {
- perror(error);
- exit(1);
- }
- int main(void)
- {
- char buf[1024];
- char *str= "-------------successs--------------\n";
- int ret;
- int fd = open("test.txt",O_RDWR|O_TRUNC|O_CREAT,0644);
- if( fd == -1 )
- sys_err("open error");
- // 直接打开文件写数据
- write(fd,str,strlen(str));
- printf("test1 write into test.txt finish\n");
- sleep(N);
- lseek(fd,0,SEEK_SET);
- ret = read(fd,buf,sizeof(buf));
- if( ret <=0 )
- sys_err("read error");
- ret = write(STDOUT_FILENO,buf,ret);
- if( ret == -1 )
- sys_err("write error");
- close(fd);
- return 0;
- }
- /*
- 先执行,将数据写入文件 test.txt
- */
- #include<unistd.h>
- #include<sys/types.h>
- #include<sys/stat.h>
- #include<fcntl.h>
- #include<stdlib.h>
- #include<stdio.h>
- #include<string.h>
- #define N 5
- void sys_err(char *error)
- {
- perror(error);
- exit(1);
- }
- int main(void)
- {
- char buf[1024];
- char *str= "-------------test2 write success--------------\n";
- int ret;
- sleep(2); // 睡两秒,保证 test1 将数据写入 test.txt中
- int fd = open("test.txt",O_RDWR);
- if( fd == -1 )
- sys_err("open error");
- // 尝试读取 test.txt文件中 test1 写入数据
- ret = read(fd,buf,sizeof(buf));
- if( ret <= 0 )
- sys_err("read error");
- // 将读到的数据打入屏幕
- write(STDOUT_FILENO,buf,ret);
- // 写入数据到 test.txt 中,未修改读写位置
- write(fd,str,strlen(str));
- printf("test2 read/write finish\n");
- close(fd);
- return 0;
- }
来源: http://blog.csdn.net/qq_35396127/article/details/78750551