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 實現通信的編碼方式,但是大部分實現接口并不會在面試中被考察,關注的重點在于定義。