常用的进程间通讯方式:
a.管道(使用最简单)匿名管道
b.信号(开销最小)
c.共享映射区(无血缘关系)
d.本地套接字(最稳定)
e.FIFO(命名管道)
1.管道:
pipe:管道一般读写行为
2.fifo:(有名管道)
用于非血缘关系进程间通讯
命令:mkfifo
函数:mkfifo
3.共享映射区:
mmap
函数的参数使用注意事项
用于非血缘关系进程间通讯
管道的概念:
管道是一种最基本的IPC机制,作用于有血缘关系的进程之间,完成数据传递。调用pipe系统函数即可创建一个管道。有如下特质:
1.其本质是一个伪文件(实为内核缓冲区)
七种文件类型:
1). - 文件 2). d 目录 3). l 字符链接 此三种为真实文件,占据磁盘存储空间
4). s 套接字 5). b 块设备 6). c 字符设备 7). p 管道 此四种为伪文件
2.有两个文件描述符引用,一个表示读端,一个表示写端。
3.规定数据从管道的写端流入管道,从读端流出。
管道的原理:管道实为内核使用环形队列机制,借助内核缓冲区(4k)实现。【使用ulimit -a查看,函数fpathconf 参2:_PC_PIPE_BUF】
管道的局限性:
1.数据自己读不能自己写
2.数据一旦被读走,便不在管道中存在,不可反复读取。
3.由于管道采用半双工通讯方式。因此数据只能在一个方向上流动
4.只能在有公共祖先的进程间使用管道
常见的通信方式有:单工通信、半双工通信、全双工通信。
模拟一个管道间pipe的通信:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> int main(void) { int fd[2]; pid_t pid; int ret = pipe(fd); if(ret == -1){ perror("pipe error."); exit(1); } pid = fork(); if(pid == -1){ perror("fork error."); exit(1); } else if(pid == 0) { //规定子进程负责读端,父进程负责写端 //默认fd[0]--读端,fd[1]--写端 //关闭子进程的写端 close(fd[1]); char buf[1024]; ret = read(fd[0], buf, sizeof(buf)); if(ret == 0){ printf("---finished read---"); } write(STDOUT_FILENO, buf, ret); } else { //关闭父进程的读端 close(fd[0]); write(fd[1], "hello pipe ", strlen("hello pipe ")); } }
共享内存:
1.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的整数倍)
mmap示例:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <fcntl.h> #include <sys/mman.h> int main(void) { int len, ret; char *p = NULL; int fd = open("mytest.txt", O_CREAT|O_RDWR, 0644); if(fd < 0) { perror("open error:"); exit(1); } len = ftruncate(fd, 5); if(len == -1) { perror("ftruncate error:"); exit(1); } p = mmap(NULL, 5, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if(p == MAP_FAILED) { perror("mmap error:"); exit(1); } strcpy(p, "abc "); ret = munmap(p, 5); if(ret == -1) { perror("munmap error:"); exit(1); } close(fd); return 0; }
munmap函数
同malloc函数申请内存空间类似的,mmap建立的映射区在使用之后也应调用类似free的函数来释放。
使用mmap时的注意事项:
1).创建映射区的过程中,隐含着一次对映射文件的读操作
2).当MAP_SHARED时,要求:映射区的权限应<=文件打开的权限(出于对映射区的保护)。而MAP_PRIVATE则无所谓,
因为mmap中的权限是对内存的限制。
3).映射区的释放与文件关闭无关。只要映射建立成功,文件可以立即关闭。
4).特别注意,当映射文件大小为0时,不能创建映射区。所以:用于映射的文件必须要有实际大小!
mmap使用时常常会出现总线错误,通常是由于共享文件存储空间大小引起的。
5).munmap传入的地址一定是mmap的返回地址,坚决杜绝指针++操作。
6).如果有文件偏移量必须为4k的整数倍(4*1024*n)
7).mmap创建映射区的出错概率非常高,一定要检查返回值,确保映射区建立成功再进行后续操作。
2.借助共享内存放磁盘文件,借助指针访问磁盘文件
3.父子进程、血缘关系进程通信
4.匿名映射区
通过使用我们发现,使用映射区来完成文件读写操作十分方便,父子进程间通信也比较容易。但缺陷是,每次创建映射区
一定要依赖一个文件才能实现。通常为了建立映射区要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系统中无该宏定义,可使用下面两步来创建匿名映射区:
1). fd=open("/dev/zero", O_RDWR);
2). P=mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
5.mmap无血缘关系进程间通信
mmap_r.c和mmap_w.c共用一个文件file_shared模拟无血缘关系进程间通信
mmap_r.c表示读进程
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <fcntl.h> #include <sys/mman.h> #include <sys/stat.h> #include <sys/types.h> struct STU { int id; char name[20]; char sex; }; void sys_err(char *str){ perror(str); exit(1); } int main(int argc, char *argv[]){ int fd; struct STU student; struct STU *mm; if(argc < 2) { printf("./a.out file_shared "); 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 name=%s %c ", mm->id, mm->name, mm->sex); sleep(2); } munmap(mm, sizeof(student)); return 0; }
mmap_w.c表示写进程
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <fcntl.h> #include <sys/mman.h> #include <sys/stat.h> #include <sys/types.h> struct STU { int id; char name[20]; char sex; }; void sys_err(char *str){ perror(str); 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 "); exit(-1); } 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){ sys_err("mmap error:"); } close(fd); while(1) { memcpy(mm, &student, sizeof(student)); student.id++; sleep(1); } munmap(mm, sizeof(student)); return 0; }
先执行mmap_w.c后执行mmap_r.c
可以使用 strace mmap_w.out 来追踪mmap_w.out执行过程中调用了哪些系统函数。
yum install strace -y 来安装strace
模拟进程组:cat | cat | cat | cat
使用 ps ajx来查看
杀掉进程组 kill -9 -gpid