今天来谈谈进程间通信:
进程间通信简称也叫ipc!
基本介绍:
操作系统提供给用户的几种进程间通信方式。
操作系统为什么要给用户提供进程间通信方式:进程的独立性(虚拟地址空间加页表)----提供一个公共的媒介。
进程间通信是干什么的:进程间数据传输,数据共享,进程控制,事件通知,也正因为有不同的应用场景(不同的需求)因此操作系统也提供了多种不同的进程间通信方式继承于unix而来的管道(匿名管道/命名管道)。
进程间通信分类
管道
- 匿名管道pipe
- 命名管道
System V IPC
- System V 消息队列
- System V 共享内存
- System V 信号量
POSIX IPC
- 消息队列
- 共享内存
- 信号量
- 互斥量
- 条件变量
- 读写锁
下面主要讲下管道:
管道:(匿名管道/命名管道)
管道是一个半双工通信:提供双向选择,但是只能单向传输。
管道创建成功后,提供io操作—返回文件描述符作为句柄,句柄(文件描述符)有两个:一个用于读取数据,一个用于写入数据。
有名字则可以通过名字打开相同的管道进行通信。
没有名字,因此只能通过子进程复制父进程的方式(复制了文件描述符)实现通信。
匿名管道只能用于具有亲缘关系的进程间通信。
命名管道可以用于任意的进程间通信。
管道原理:操作系统在内核提供一块缓冲区(只要进程能够访问到这块缓冲区就可以实现通信)。
匿名管道:
int pipe(int pipefd[2]);
pipefd:输出型参数—用于获取管道操作句柄
pipefd[0] 管道的读取端
pipefd[1] 管道的写入端
返回值:0 失败:-1
从本质上来说,pipe()函数的功能就是创建一个内存文件,但与创建普通文件的函数不同,函数pipe()将在参数fildes中为进程返回这个文件的两个文件描述fildes[0]和fildes[1]。其中,fildes[0]是一个具有“只读”属性的文件描述符,fildes[1]是一个具有“只写”属性的文件描述符,即进程通过fildes[0]只能进行文件的读操作,而通过fildes[1]只能进行文件的写操作。
读写特性:
管道中若没有数据,则read会阻塞,直到读取到数据(有数据写入管道了)
管道中若数据满了,则write会阻塞,直到有空闲空间(有数据被读出了)
若所有读端被关闭,则write会触发异常—SIGPIPE(导致进程退出)
若所有写端被关闭,则read读完数据之后不会阻塞,而是返回0
进程在操作管道的时候,如果没有用到某一端,则把这一端关闭掉!
实例代码如下:实现一个父写子读的过程
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<errno.h>
5 #include<string.h>
6
7 int main()
8 {
9 int pipefd[2];
10 int ret = pipe(pipefd); //创建一个匿名管道
11 if(ret<0){
12 perror("pipe error!");
13 return -1;
14 }
15 int pid = fork();
16 if(pid<0){
17 perror("fork error!");
18 return -1;
19 }
20 //实现父子之间的通信 父写 子读
21 else if(pid==0){
22 while(1){
23 close(pipefd[1]); //子进程只读的话就把写端关闭
24 sleep(1);
25 char buf[1024]={0};
26 ret = read(pipefd[0],buf,1023);
27 if(ret<0){
28 perror("read error!");
29 }
30 else{
31 printf("buf:[%s]\n",buf);
32 }
33 }
34 }
35 else{
36 close(pipefd[0]); //父进程只写就关掉读端
37 int i = 0;
38 while(1){
39 sleep(1);
40 char* ptr = "hello my child !";
41 i += write(pipefd[1],ptr,strlen(ptr));
42 printf("ret:%d\n",i);
43 }
44 }
45
46 return 0;
47 }
这样就实现了基本的父写子读功能!
命名管道:
管道可以见于文件系统,会创建一个管道文件(文件只是名字)
管道通信的本质还是内核的那块缓冲区
int mkfifo(const char *pathname, mode_t mode);
pathname: 管道文件的路径名
mode: 创建文件的权限
返回值:0 失败:-1
由于需要由管道自身来保证通信进程间的同步,命名管道也是一个只能单方向访问的文件,并且数据传输方式为FIFO方式。
也就是说,命名管道提供了一个路径名与之关联,以FIFO的文件形式存在于文件系统中,在文件系统中产生一个物理文件,其他进程只要访问该文件路径,就能彼此通过管道通信。在读数据端以只读方式打开管道文件,在写数据端以只写方式打开管道文件。
打开特性:
若管道文件没有被写的方式打开,则以只读打开会阻塞
若管道文件没有被读的方式打开,则以只写打开会阻塞
读写特性雷同于匿名管道
实例代码如下:创建命名管道,通过一个文件,实现一端写,一端读并显示到终端上。
//fifo_read.c
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<fcntl.h>
5 #include<errno.h>
6
7 int main()
8 {
9 char* ptr = "./a.fifo"; //这就是提供的公共访问路径名
10 int ret = mkfifo(ptr,0664); //0664是权限,可以自由设置
11 if(ret<0){
12 if(errno!=EEXIST){ //如果不是早已存在的话,就说明创建失败
13 perror("mkfifo error");
14 return -1;
15 }
16 }
17 printf("mkfifo sucess!\n");
18 int fd = open(ptr,O_RDONLY); //以只读方式打开
19 if(fd<0){
20 perror("open error!\n");
21 return -1;
22 }
23 printf("open fifo sucess!\n");
24 while(1){
25 sleep(10);
26 char buf[1024]={0};
27 int ret = read(fd,buf,1023);
28 if(ret<0){
29 perror("read error!\n");
30 return -1;
31 }
32 else if(ret==0){
33 printf("write closed!\n");
34 return -1;
35 }
36 printf("buf:[%s]\n",buf);
37 }
38 close(ret);
39 return 0;
40 }
//fifo_write.c
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<fcntl.h>
4 #include<unistd.h>
5 #include<string.h>
6 #include<errno.h>
7
8 int main()
9 {
10 char* ptr = "./a.fifo";
11 int ret = mkfifo(ptr,0664);
12 if(ret<0){
13 if(errno!=EEXIST){
14 perror("mkfifo error!\n");
15 return -1;
16 }
17 }
18 printf("mkfifo success!\n");
19 int fd = open(ptr,O_WRONLY); //以只写方式打开
20 if(fd<0){
21 perror("open error!\n");
22 return -1;
23 }
24 printf("open fifo success!\n");
25 while(1){
26 char buf[1024]={0};
27 scanf("%s",buf);
28 write(fd,buf,strlen(buf)); //将指定内容写到fd所指区域
29 }
30 close(ret);
31 return 0;
32 }
运行结果如下:我xshell6开了两个窗口
这是写端的情况:
下面是读端的情况:
可以看到写端关闭后,读端没有阻塞,而是正常退出了!
小结
管道:本质是内核的一块缓冲区
匿名管道只能用于具有亲缘关系的进程间通信/命名管道可以用于任意的进程间通信
管道的读写特性+命名管道的打开特性
管道是半双工通信—可选方向的单向通信
管道的生命周期随进程
管道提供流式服务—字节流传输(传输灵活/数据粘连)
管道自带同步与互斥功能(读写操作数据大小不超过PIPE_BUF大小,读写操作受保护)
互斥:对临界资源(公共资源)的唯一访问性(我操作的时候别人不能操作)
同步:对临界资源访问的时序可控性(我操作完了别人才能操作)
管道的同步与互斥体现就是:管道没有放数据则读阻塞,放了数据之后唤醒对方阻塞,
对方读数据(我放了,你才能读–同步),我数据往管道中没放完别人不能读也不能写(互斥)。
管道提供字节流服务