目录
一、认识进程通信
在了解进程之间的通信之前,我们知道进程具有独立性,不会干扰彼此的运行,但是进程是可以互相发送消息的,这种通信不会影响进程的独立。
- 进程通信的目的
数据传输:一个进程需要将它的数据发送给另一个进程。
资源共享:多个进程之间共享同样的资源。
通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如子进程终止时要通知父进程)。
进程控制:有些进程希望完全控制另一个进程的执行(如 Debug 进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。
- 进程通信的本质
进程之间通信的本质,就是让两个进程同时操作同一份资源。这份资源只能由操作系统提供,不能由进程之间的任何一方提供,否则,就会破坏进程的独立性。
- 进程通信的分类
管道
匿名管道pipe
命名管道
System V IPC
System V 消息队列
System V 共享内存
System V 信号量
POSIX IPC
消息队列
共享内存
信号量
互斥量
条件变量
读写锁
二、管道通信
管道通信是Unix 中最古老的进程间通信的形式。
我们把从一个进程连接到另一个进程的一个数据流称为一个"管道"。
- 管道分为匿名管道和命名管道。
1.匿名管道
匿名管道通常用于具有亲缘关系的进程之间,比如父子进程之间。
管道通信是半双工模式通信,即只能单方向通信。
匿名管道文件是内存级文件,即用即销毁,在磁盘中没有其对应的存储。
- 从文件描述符角度理解匿名管道
一个进程的PCB关联着一张文件描述符表,0、1、2默认是被三个标准流占用的,如果一个进程以'r'的方式打开一个文件,又以'w'的方式打开同一个文件,在文件描述符表中是会占用两个文件描述符的。
在父进程fork创建子进程,发生浅拷贝,各自的fd指向的内容是相同的。
由于匿名管道是单向通信,因此通信时,只能有一个进程在读管道,也只能有一个进程在向管道写数据,所以,父进程和子进程管道通信之前,父子进程要各自关掉一个描述符,(由于此时的文件被多个进程打开,当其中一个进程关文件,则只是将文件的引用计数减一,并不是真的关掉文件)。
- 父子进程管道通信的流程
- 匿名管道通信时的4种情况
1.父进程阻塞:管道内没有数据,并且此时子进程不关闭写端文件fd,则读端(父进程)就要阻塞等待,直到管道中有数据。
2.子进程阻塞:管道大小被写满,此时父进程不关闭读端文件fd,则写端(子进程)就要阻塞等待
3.如果写端不再向管道中写数据,并且此时关闭了写端文件fd,则父进程读完管道中的数据后,最后会读到0,用来表示读到了文件尾。
4.如果读端不再从管道中读数据,并且此时关闭了读端文件fd,而写端文件还在向管道中写数据,则操作系统会直接终止写入的进程(子进程),通过发送信号13 SIGPIPE来杀掉子进程。
- 管道通信的5种特性
1.由于管道通信时,要么是读端在读,要么是写端在写,不会出现一边读一边写的情况,所以管道自带同步机制。
2.管道通信常见于具有亲缘关系的进程,比如父子进程。
3.管道通信是面向字节流的。
4.父进程或者子进程退出,则管道自动释放,管道文件的生命周期是跟随进程的。
5.管道通信是半双工模式,并且是一种特殊的半双工模式,特殊指在一次通信中,写端只能写,读端只能读。
- 匿名管道的实例
bash
int main()
{
//1.父进程创建管道
int pipefd[2];
int n = pipe(pipefd);
if(n < 0)return 1;
//管道规定,0为读,1为写
printf("pipefd[0]:%d,pipefd[1]:%d\n",pipefd[0],pipefd[1]);
//2.创建子进程
pid_t id = fork();
if(id == 0)
{
//子进程
close(pipefd[0]);
Pro_Wrte(pipefd[1]);
exit(0);
}
//父进程
close(pipefd[1]);
Pro_Read(pipefd[0]);
wait(NULL);
return 0;
}
- 实现一个进程池
主进程有多个子进程。要求实现负载均衡,给每个进程均衡分配任务。
2.命名管道
命名管道和匿名管道唯一的区分就是,命名管道对应在磁盘上是有名称的特殊文件,但是作为内存中的管道来通信同样不需要和磁盘做IO。
需要我们记住的是,匿名管道常常用于具有亲缘关系的进程之间通信,而命名管道可以帮助两个毫不相干的进程通信。
管道通信的本质就是利用了多个文件描述符指向的多个文件结构体的缓冲区是同一份,**由于毫不相干的进程要利用管道通信,就必须要让这两个进程都能"看到"这同一个管道文件,**因此命名管道常常用于不相干的进程之间通信,而具有亲缘关系的进程只用匿名管道即可通信。