一、理解通信
进程需要通过通信来协同或传递数据(通知就绪,传单纯数据,控制信息...)
进程通信的前提:先让不同的进程看到同一份操作系统资源(一段内存)
1、一定是某个进程要通信,让OS创建一个共享资源。
2、OS要提供很多系统调用接口。
3、共享资源不同,系统调用接口不同导致进程间通信的种类不同。
二、进程通信常见方式
1、system V 本地通信(不常用)
(1)消息队列
(2)共享内存
(3)信号量
2、管道
(1)匿名管道
特点
只用于具有血缘关系的进程之间通信,常用于父子间通信。
问题
1、为什么父子进程会向同一个显示器终端打印数据?
进程创建默认打开三个标准输入输出文件0,1,2,本质就是父进程bash打开的,所以之后的进程都能看到三个打开的文件。
2、子进程关闭文件为什么父进程不受影响?
文件中有引用计数,只有文件引用计数 == 0,才会释放文件资源。
注意
1、管道文件只允许单向通信。
2、管道文件用于两进程间通信,不要刷新到磁盘。
3、父子进程要保证管道文件单向通信就要关闭不要的读写文件,那为什么一开始都要打开?要让子进程继承下去。
创建匿名管道
函数 int pipe(int pipefd[2])
pipefd[2]:输出型参数,pipefd[0]表示读文件,pipefd[1]表示写文件。
成功创建返回0,失败返回-1
本质就是复用 open() 函数。
创建时不要文件路径,文件名,就是创建匿名管道文件。
要双向通信就创建两个管道文件。
举例
进程池
父进程提前创建一批子进程,由于父进程写端打开,如果不写入读端会被阻塞,这样就达到了父进程只要一把任务写道管道,子进程就能接收到运行,这样就不用到要用子进程时创建,节省资源。
但是会出现一种情况:
由于每一次创建子进程都会复制父进程的写端,就导致子进程会有写端,这样就不符合父进程写入,子进程读入的形式,所以每次创建子进程时要遍历管道数组,把写端关闭。
(2)命名管道
理解
两个毫不相干的进程之间打开同一个文件,一个读,一个写,这个文件就是管道文件。
为什么要命名?
用于两个毫不相干的进程之间通信,标识管道文件的路径是找到文件的办法。
对于读端而言,如果我们打开文件但写端未打开,就会阻塞在open()函数,直到写端打开,匿名管道一开始就是打开的。这其实也是一种进程同步。
创建命名管道
pathname:文件路径
mode:文件权限
删除指定文件
删除指定路径下的文件。
(3)管道的4个特点
a、管道内部自带同步机制(多执行流执行代码时具有明显顺序性)
b、文件的生命周期时随进程的
c、管道文件在通信时是面向字节流(写入与读入的次数不是一一对应的)
d、管道的通信模式是特殊的半双工模式
全双工与半双工:两者既能写入又能读入,半双工指双方不能同时写入或读入,全双工可以同时,由于管道一个进程只能写入或读入,所以是特殊的半双工模式。
(4)管道的4种情况
a、如果管道内部为空,写端未关闭(不写入),读条件不具备,读进程就会阻塞。
b、如果管道写满,读端未关闭(不读入),写进程就会被阻塞。
c、管道一直在读,但是写端已经关闭,read() 返回0,表示读到文件末尾。
d、管道一直在写,但是读端已经关闭,操作系统用13号信号杀死进程,出现异常。
当写入管道的大小小于4096字节就是原子的安全的。