1. 前言
上一小结谈到了操作系统中进程和线程的区别,其中进程之间、线程之间的通信方式不同,进程通信(Inter-Process Communication,简称 IPC)是指不同进程之间交换信息。操作系统中时刻都在进行 IPC,例如微信读取本地的文件,就是微信程序和文件系统进程交互的过程。
2. 进程间通信
面试官提问: 操作系统进程之间的通信方式有哪些?有什么特点?
题目解析:
操作系统中最常用的 IPC 方式有 5 种,分别是管道、命名管道、信号、共享内存以及套接字。
2.1 管道
管道(pipe),默认指无名管道。管道在两个进程之间建立一个通道,一个进程向这个通道写入字节流,另一个进程从这个通道读取字节流。用 C 语言描述管道示例:
#include <unistd.h> // 引入linux头文件
int pipe(int fd[2]); // 返回:如果成功返回0,失败则返回-1
上述定义的 fd 对象,其中 fd[0]
表示读文件描述符,f[1]
表示写文件描述符。
假设存在两个进程,分别为进程 A 和进程 B,那么进程 A 往 f[1]
写入,进程 B 则从自身的 f[0]
读取内容。
需要注意管道是半双工通信,也就是数据的流向是固定的,必须有一端是写入端,另一端是读取端。
2.2 信号
信号(Signal)是 Unix 系统中就已有的 IPC 方式,继承于 Unix 的 Linux 系统和 MacOS 系统也具有相同的通信方式。
信号的工作原理是向某个进程发送特定的消息,目标进程在收到消息之后,就知道特定事件已经发生,此时进程可以忽略消息即不做处理,或者是处理消息调用固定的函数。
以 MacOS 为例,在 shell 终端输入 kill -l
可以列出支出的全部信号名称:HUP INT QUIT ILL TRAP ABRT EMT FPE KILL BUS SEGV SYS PIPE ALRM TERM URG STOP
。
2.3 共享内存
共享内容(Shared Memory)是指两个进程之间可以读和写相同的操作系统内存空间,每个进程的操作对另外的进程都是可见的,这种通信方式非常类似线程之间的通信。
C 语言实现的共享内存步骤:
(1)shmget()
:创建一段共享内存,或者引用已有的共享内存的空间;
(2)shmat()
:连接已有的共享内存的地址;
(3)shmctl()
:建立连接之后,对共享内存进行读写操作;
(4)shmdt()
:所有操作都执行完成之后,断开连接。
2.4 命名管道
命名管道(Named Pipe)实际上就是先进先出队列(First In First Out,简称 FIFO),候选人需要区分命名管道和管道,两者最大的区别在于管道只能在具有亲缘关系的两个进程之间通信,例如父子进程之间或者兄弟进程之间,命名管道则可以在任何两个进程之间通信,更加零活。
如图所示,用户进程 A 是写入进程,写入的消息是 1 2 3 4 5
,因为遵循先进先出的原则,用户进程 B 读出的消息顺序也是 1 2 3 4 5
。
2.5 套接字
上述介绍的 IPC 方式都是同一个主机内进程的交互方式,都是本地通信,套接字(Socket)一般用来处理不同主机进程之间的通信,也就是远程通信,是网络通信最常用的方式。Socket 通信需要 TCP 或者 UDP 协议的支持。使用 C 语言创建 Socket 的示例:
#include <sys/types.h>
#include <sys/socket.h> //引入头文件
int socket(int domain, int type, int protocol); //创建一个socket
3. 小结
本章节介绍了 5 种最常见的进程间通信方式,候选人需要掌握没种通信方式的原理,最好能够画出原型图,而操作系统级别的通信一般不需要我们手动实现,有兴趣的同学可以了解下具体的实现,例如使用 Socket API 实现通信的编码方式,但是大部分实现接口并不会在面试中被考察,关注的重点在于定义。