进程间通信的方式(二):共享映射区
2017-01-18 00:28
218 查看
进程间通信方式(二)
共享映射区
(一).通信方式:进程间通信又称IPC(Inter Process Communication),它可以通过文件 管道 有名管道 共享内存 消息队列 信号量 套接字这几个方式进行通信,但是文件这种以及消息队列基本已被淘汰。
所以常用的通信方式有:
1.管道
2.信号
3.共享映射区
4.本地套接字
(二).共享映射区基本概念:
通过mmap系统调用,把普通文件映射到存储空间的一个缓冲区中,使得两个或多个进程之间可以共享这一块区域,就达到了进程间通信的目的。这种通信方式比通过文件进行通信更快,因为是直接操作缓冲区,而不是通过read以及write等函数进行操作。
这种方式可以用于父子间进程通信,也可以用于无血缘关系的进程之间通信。
当我们使用mmap建立了共享映射区之后,进行的操作就好比我们操作字符数组一样。
(三).使用共享映射区进行通信:
函数原型:
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
所需头文件:
#include <sys/mman.h>
参数含义:
1.第一个参数代表建立共享映射区在内存中的起始地址,由系统自动分配即可,所以传入NULL就行了;
2.第二个参数代表创建的映射区的大小;
3.第三个参数代表映射区的访问权限,由几个宏确定,常用的有:PROT_READ(可读),PROT_WRITE(可写),PROT_READ | PROT_WRITE(可读可写);
4.第四个参数代表标志位,常用的有:MAP_SHARED、MAP_PRIVATE、MAP_ANON,MAP_SHARED的意思是对这块映射区内容的修改会影响到被映射的文件 或者 多个进程间独享该映射区,MAP_PRIVATE的意思是对这块映射区内容的修改不会影响到被映射的文件 或者 多个进程间共享该映射区,MAP_ANON用于匿名映射;
5.第五个参数代表要映射的文件的文件描述符;
6.最后一个参数代表操作这块映射区的起始偏移量,和对数组的操作类似,不过必须是4k的整数倍。
返回值:
该函数成功调用会返回创建映射区的首地址,如果失败会返回一个宏:MAP_FAILED
由于mmap函数其实涉及到内存的分配,有了分配,自然要有释放。
释放的函数原型:
int munmap(void *addr, size_t length)
头文件:
#include <sys/mman.h>
参数含义:
1.第一个参数代表共享映射区的起始地址
2.第二个参数代表分配的大小
返回值:
成功返回0,失败返回-1
例子1:
#include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <sys/mman.h> int main() { int len, ret; char *p = NULL; int fd = open("mytest.txt", O_CREAT | O_RDWR, 0666); //以读写的方式打开文件,不存在则创建 if(fd < 0) { perror("open error:"); exit(1); } len = ftruncate(fd, 4); //扩展文件大小 if(len == -1) { perror("ftruncate error:"); exit(1); } p = mmap(NULL, 4, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); //以读写的方式建立映射区 if(p == MAP_FAILED) { perror("mmap error"); exit(1); } strcpy(p, "abc"); //将字符串abc赋给这块映射区 ret = munmap(p, 4); //释放 if(ret == -1) { perror("munmap error:"); exit(1); } close(fd); return 0; }
这个例子虽然简单,但是其实有几个需要注意的地方。
1.当我们打开一个文件的时候,操作该文件的权限是不能小于通过该文件建立映射区的权限的,即如果文件是只读的,那么映射区这边也不能进行写操作。
2.创建映射区时,包含着一次对映射文件的读操作,因为要把内容都映射到缓冲区中。所以文件的读权限应该要有才行。
3.偏移量必须是4k的整数倍。
4.munmap的第一个参数必须是映射区的首地址。
例子2:
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <fcntl.h> #include <sys/mman.h> #include <sys/wait.h> int main() { int *p; pid_t pid; //这次我们不打开文件,而是进行匿名映射。因为打开文件其实主要是起一个映射的作用,略显鸡肋,如果直接有这一块区域进行通信,那不更好 p = (int *)mmap(NULL, 4, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0); //原本的文件描述符参数应该填-1 if(p == MAP_FAILED) { perror("mmap error"); exit(1); } pid = fork(); if(pid == 0) { *p = 2; printf("child, *p = %d\n", *p); } else { sleep(1); printf("parent, *p = %d\n", *p); wait(NULL); int ret = munmap(p, 4); if(ret == -1) { perror("mumap error"); exit(1); } } return 0; }
注意事项:
MAP_ANON参数只适合在linux系统中使用。
例子3:
/****************读数据******************/ #include <stdio.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> #include <sys/mman.h> #include <string.h> struct STU { int id; char name[20]; char sex; }; int main(int argc, char *argv[]) //argv[1]用于传递要映射的文件名 { int fd; struct STU student; struct STU *mm; fd = open(argv[1], O_RDONLY); mm = mmap(NULL, sizeof(student), PROT_READ, MAP_SHARED, fd, 0); //注意这里必须是MAP_SHARED,不然数据无法共享 if(mm == MAP_FAILED) { perror("mmap error:"); exit(1); } close(fd); while(1) //读出映射区的数据 { printf("id = %d\tname = %s\t %c\n", mm->id, mm->name, mm->sex); sleep(2); } munmap(mm, sizeof(student)); return 0; } /***************写端***************/ #include <stdio.h> #include <sys/stat.h> #include <sys/types.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> #include <sys/mman.h> #include <string.h> struct STU { int id; char name[20]; char sex; }; int main(int argc, char *argv[]) { int fd; struct STU student = {10, "xiaoming", 'm'}; char *mm; fd = open(argv[1], O_RDWR | O_CREAT, 0664); ftruncate(fd, sizeof(student)); mm = mmap(NULL, sizeof(student), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if(mm == MAP_FAILED) { perror("mmap error"); exit(1); } close(fd); while(1) //写数据 { memcpy(mm, &student, sizeof(student)); student.id++; sleep(1); } munmap(mm, sizeof(student)); return 0; }
注意两个进程之间打开映射区的标志位必须是MAP_SHARED,而不是MAP_PRIVATE,否则数据无法共享。两个或多个无血缘关系的进程只要通过共享一块映射区,就可以达到通信的目的了。
相关文章推荐
- Android Studio配置Gradle(包括signingConfigs、buildTypes和productFlavors等)
- 安装 nginx
- 在Node.js中使用RabbitMQ系列二 任务队列
- Unable to locate Spring NamespaceHandler for XML schema namespace [http://www.springframework.org/schema/tx]
- iOS开发-UI (四)UIViewController
- 【小白的CFD之旅】20 计算区域的构建
- 求多个四元数的平均数
- js清除浏览器缓存的几种方法
- HDU 3605Escape(最大流+状压DP)@
- 杨辉三角形
- Hadoop2.5.2学习01--mapreduce统计单词数
- 【LeetCode】 313. Super Ugly Number
- Django之Helloworld
- fedora 18~20 中Qt 5.2.1 解决连接mysql数据库出现QMYSQL driver not loaded的问题
- 【leetcode】【Easy】【463. Island Perimeter】
- Java实现常用简单负载均衡(LoadBalance)
- Lightoj 1166 - Old Sorting (简单贪心)
- javaGUI基本知识讲解
- javaGUI基本知识讲解
- 疯狼算法日记0002-句子中每个单词逆序