前言:天下武功为快不破!在信息爆炸、快速发展的新时代...,扯远了...。进程间通信方式有很多,但最快的方式你知道么?由我娓娓道来...
一、共享内存方式
主角闪亮登场了,噔噔瞪...,最快的方式就是共享内存了。实现共享内存的方式主要有两种:
- 存储映射I/O mmap函数实现
- shmget函数
感觉学两个以上相同的知识时,就会学他们的区别,我也会避免不了进入俗套,也要简单来说它们的区别:
注:此图引自《unix环境高级编程》
看到图片中“主要区别”四个大字了么?别告诉我:你近视看不到,打死你个龟孙!开玩笑啦,哈哈哈!只希望在学习知识时,不要感觉太乏味哦。在下文介绍完两种方式之后,你就会不明白它们的区别了。
二、mmap函数实现共享内存
1、存储映射I/O
先来画个见图来介绍一下实现原理,如下图: PS:依旧是全博客园最丑图,不接受反驳!!!
mmap函数主要作用就是将磁盘文件映射到内存中,并返回这段内存的首地址,并可以将这块内存来当做“数组”来处理。要对这个图有一个模型(虽然是全博客园最丑图),对接下来的讲解有一定帮助。
2、mmap函数及相关函数
原型:void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
返回:成功:返回创建的映射区首地址;失败:MAP_FAILED宏,注意错误跟普通的不一样!
参数说明:
addr: 建立映射区的首地址,由Linux内核指定。使用时,直接传递NULL
length: 欲创建映射区的大小
prot: 映射区权限PROT_READ、PROT_WRITE、PROT_READ|PROT_WRITE
flags: 标志位参数(常用于设定更新物理区域、设置共享、创建匿名映射区)
MAP_SHARED: 会将映射区所做的操作反映到物理设备(磁盘)上。
MAP_PRIVATE: 映射区所做的修改不会反映到物理设备。
fd: 用来建立映射区的文件描述符
offset: 映射文件的偏移(4k的整数倍)
3、munmap函数
原型:int munmap(void *addr, size_t length); 成功:0; 失败:-1
功能:同malloc函数申请内存空间类似的,mmap建立的映射区在使用结束后也应调用类似free的函数来释放。
4、父子进程用mmap示例程序
程序主要用共享内存进行通信,如下:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/mman.h> #include <sys/wait.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 < 0){ perror("open error"); exit(1); } unlink("temp"); //删除临时文件目录项,使之具备被释放条件. ftruncate(fd, 4); //p = (int *)mmap(NULL, 4, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); p = (int *)mmap(NULL, 4, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); if(p == MAP_FAILED){ //注意:不是p == NULL perror("mmap error"); exit(1); } close(fd); //映射区建立完毕,即可关闭文件 pid = fork(); //创建子进程 if(pid == 0){ *p = 2000; var = 1000; printf("child, *p = %d, var = %d ", *p, var); } else { sleep(1); printf("parent, *p = %d, var = %d ", *p, var); wait(NULL); int ret = munmap(p, 4); //释放映射区 if (ret == -1) { perror("munmap error"); exit(1); } } return 0; }
三、匿名映射
1、说明
通过使用我们发现,使用映射区来完成文件读写操作十分方便,父子进程间通信也较容易。但缺陷是,每次创建映射区一定要依赖一个文件才能实现。通常为了建立映射区要open一个temp文件,创建好了再unlink、close掉,比较麻烦。 可以直接使用匿名映射来代替。其实Linux系统给我们提供了创建匿名映射区的方法,无需依赖一个文件即可创建映射区。同样需要借助标志位参数flags来指定。
使用MAP_ANONYMOUS (或MAP_ANON), 如:
int *p = mmap(NULL, 4, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
"4"随意举例,该位置表大小,可依实际需要填写。
需注意的是,MAP_ANONYMOUS和MAP_ANON这两个宏是Linux操作系统特有的宏。在类Unix系统中如无该宏定义,可使用如下两步来完成匿名映射区的建立。
① fd = open("/dev/zero", O_RDWR);
② p = mmap(NULL, size, PROT_READ|PROT_WRITE, MMAP_SHARED, fd, 0);
2、用"/dev/sero"示例匿名映射
程序如下:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/mman.h> int main(void) { int *p; pid_t pid; int fd; fd = open("/dev/zero", O_RDWR); p = mmap(NULL, 400, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); if(p == MAP_FAILED){ //注意:不是p == NULL perror("mmap error"); exit(1); } pid = fork(); //创建子进程 if(pid == 0){ *p = 2000; printf("child, *p = %d ", *p); } else { sleep(1); printf("parent, *p = %d ", *p); } munmap(p, 4); //释放映射区 return 0; }
总结:shmget函数会在另一篇博客介绍,欢迎评论交流