什么是进程间通信
进程间通信(Inter-Process Communication),简称 IPC,意思就是在不同进程之间传递信息。它是一组编程接口,让程序员协调不同进程,使能够相互传递消息
进程间通信的意义
(1)数据传输:一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几兆字节之间。
(2)事件通知:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。
(3)数据共享:多个进程想要操作共享数据,一个进程对共享数据的修改,别的进程应该立刻看到。
(4)资源共享:多个进程之间共享同样的资源。为了作到这一点,需要内核提供锁和同步机制。
(5)进程控制:有些进程希望完全控制另一个进程的执行(如 debug 进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。
进程间通信方式
管道
单工,半双工,全双工
- 单工:数据传输是
单向的 - 半双工:它可以在一个信号载体的
两个方向上传送,但不能同时传送 - 全双工:可在信号载波的
两个方向同时传输
匿名管道
两个限制:① 单工:同时只能单向传输数据;② 只能在父子和兄弟进程之间传输数据
父子进程之间的通信三部曲:(由于 fork 的原因只能在父子和兄弟之间)
命名管道
打破匿名管道的两个限制,实现:① 半双工:可以双向传输(但没法同时双向,全双工才可以);② 由于有了名字,支持任意多个进程间通讯
标识符:每个内核的 IPC 结构(消息队列、信号量或共享内存)都用标识符(一个非负整数)引用。
键:由于标识符是 IPC 对象的内部名,所以为了是多个进程间能够访问到同一 IPC 对象,需要提供一个外部名。即键(key),。键和标识符之间存在一一映射的关系,分别存在管道中和 IPC 对象内部。(因为有键值的存在所以可以在任意多个进程之间通信)
·
信号量
信号量的本质就是一个计数器,用来实现进程之间的互斥与同步
比如某个资源只能同时被 5 个进程共享,那么每多一个进程去访问这些资源,则计数器加 1,每当某个进程结束该资源的访问,则计数器减 1。如果达到满,则暂停其他进程对该资源的访问。
信号量不仅仅用于进程之间的冲突,还用于线程之间的冲突
消息队列
消息队列是一种异步的通信方式
消息队列是消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
共享内存
共享内存是所有IPC方式中最快的一种,原因在于:共享内存一旦映射到进程地址空间,进程间数据的传递就不需要涉及内核。
对于管道、FIFO和消息队列,两个进程之间通过这三种方式进行通信,则内核就扮演着“中转站”的角色。
- 发送消息一方,通过系统调用(write或msgsnd)将消息
从用户层拷贝到内核层,由内核暂时保存这份信息; - 接受消息的一方,通过系统调用(read或msgrcv)将消息
从内核层提取到用户层;
注意:在使用read/write时,操作系统还会将数据缓存到临时缓冲区内。
我们在来看共享内存:
多个进程共同访问同一片物理内存的
内核负责构建出一片内存区域,两个或多个进程可以将这块内存区域映射到自己的虚拟地址空间,从此之后内核不再参与双方通信。
注意:建立共享内存之后,内核并不是完全不参与进程间的通信,因为当进程使用共享内存时,可能会发生缺页,引发缺页中断,这种情况下,内核还是会参与进来的。
套接字 Socket
- 服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用 accept 阻塞,等待客户端连接。
- 在这时如果有个客户端初始化一个 Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。
- 客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束