Linux 进程 | 进程间的通信方式
文章目錄
- 管道
- 匿名管道 pipe
- 命名管道 FIFO
- 共享內(nèi)存
- 共享內(nèi)存的使用流程:
- 消息隊(duì)列
- 信號(hào)量
- 套接字
在之前的博客中講過(guò),虛擬空間出現(xiàn)的其中一個(gè)目的就是解決 進(jìn)程沒(méi)有獨(dú)立性,可能訪問(wèn)同一塊物理內(nèi)存 的問(wèn)題。因?yàn)檫@種獨(dú)立性,進(jìn)程之間無(wú)法直接進(jìn)行通信,操作系統(tǒng)為了解決這種問(wèn)題,提出了多種適用于不同情境下的通信方式:
- 數(shù)據(jù)傳輸:管道、消息隊(duì)列
- 數(shù)據(jù)共享:共享內(nèi)存
- 進(jìn)程控制:信號(hào)量
管道
管道的本質(zhì)其實(shí)就是內(nèi)核中的一塊緩沖區(qū),多個(gè)進(jìn)程通過(guò)訪問(wèn)同一個(gè)緩沖區(qū)就可以實(shí)現(xiàn)進(jìn)程間的通信。自 Linux 2.6.11 內(nèi)核起,管道容量的大小默認(rèn)是 65536 字節(jié),但可以通過(guò) fcntl函數(shù) 來(lái)修改管道容量。
管道分為兩種:匿名管道、命名管道
匿名管道 pipe
因?yàn)闆](méi)有具體的文件描述符,所以只能用于具有親緣(父子)關(guān)系的進(jìn)程之間的通信。
原理: 父進(jìn)程在創(chuàng)建管道的時(shí)候操作系統(tǒng)會(huì)返回管道的文件描述符,然后生成子進(jìn)程時(shí)子進(jìn)程會(huì)通過(guò)拷貝父進(jìn)程的 pcb 來(lái)獲取到這個(gè)管道的描述符,所以他們可以通過(guò)這個(gè)文件描述符來(lái)訪問(wèn)同一個(gè)管道,來(lái)實(shí)現(xiàn)進(jìn)程間的通信。而不具備親緣關(guān)系的進(jìn)程則無(wú)法通過(guò)這個(gè)文件描述符來(lái)訪問(wèn)同一個(gè)管道。
返回值:成功返回 0,失敗返回 -1 。
也就是說(shuō)讀/寫(xiě)操作的流程是:
圖解父子進(jìn)程通過(guò)管道通信的流程:
一開(kāi)始父進(jìn)程創(chuàng)建管道
父進(jìn)程fork創(chuàng)建子進(jìn)程
關(guān)閉多余描述符
代碼示例:示例一 示例二
如果沒(méi)有特意規(guī)定,那么父子進(jìn)程究竟是誰(shuí)先執(zhí)行是不確定的,假設(shè)如果子進(jìn)程還沒(méi)寫(xiě)入,父進(jìn)程卻已經(jīng)開(kāi)始讀了,這時(shí)候應(yīng)該是會(huì)讀不到東西的,會(huì)在屏幕上輸出空行,但是這種情況并沒(méi)有發(fā)生,這里就牽扯到了管道的讀寫(xiě)特性:
- 如果管道中沒(méi)有數(shù)據(jù),則調(diào)用 read 讀取數(shù)據(jù)會(huì)阻塞。
- 如果管道中數(shù)據(jù)滿了,則調(diào)用 write 寫(xiě)入數(shù)據(jù)會(huì)阻塞。
- 如果管道的所有 讀端 被關(guān)閉,繼續(xù)調(diào)用 write 時(shí),會(huì)因?yàn)闊o(wú)法讀出而產(chǎn)生異常導(dǎo)致進(jìn)程退出。
- 如果管道的所有 寫(xiě)端 被關(guān)閉,繼續(xù)調(diào)用 read 時(shí),read 讀完管道中的所有數(shù)據(jù)后不再阻塞,返回 0 退出。
命名管道 FIFO
命名管道也是內(nèi)核中的一塊緩沖區(qū),但是它 具有標(biāo)識(shí)符 。這個(gè)標(biāo)識(shí)符是一個(gè)可見(jiàn)于文件系統(tǒng)的管道文件,能夠被其他進(jìn)程找到并打開(kāi)管道文件來(lái)獲取管道的操作句柄,多個(gè)進(jìn)程可以通過(guò)打開(kāi)這個(gè)管道文件來(lái)訪問(wèn)同一塊緩沖區(qū)來(lái)實(shí)現(xiàn)通信。
- 可以在沒(méi)有親緣關(guān)系(非父子)的進(jìn)程之間進(jìn)行通信,這是與無(wú)名管道最大的區(qū)別。
- 他是以一個(gè)特性的文件存儲(chǔ)在文件系統(tǒng)中, 所以對(duì)他的操作與其路徑名有關(guān)聯(lián)。
接口:
int mkfifo(const char *filename,mode_t mode);filename——管道的標(biāo)識(shí)符,通過(guò)這個(gè)標(biāo)識(shí)符來(lái)訪問(wèn)管道,創(chuàng)建之前這個(gè)標(biāo)識(shí)符必須不存在。
mode——權(quán)限掩碼。
返回值——若成功則返回 0,否則返回 -1 。
代碼示例: 命名管道實(shí)現(xiàn)進(jìn)程的信息傳遞【mkfifo函數(shù)、open函數(shù)】
open 打開(kāi)命名管道的特性:
管道的特性:
共享內(nèi)存
共享內(nèi)存即在 物理內(nèi)存 上開(kāi)辟一塊空間,然后 多個(gè)進(jìn)程 通過(guò) 頁(yè)表 將這 同一個(gè)物理內(nèi)存 映射到自己的 虛擬地址空間 中,通過(guò)自己的 虛擬地址空間 來(lái)訪問(wèn)這塊 物理內(nèi)存 ,達(dá)到了數(shù)據(jù)共享的目的。
也正是因?yàn)檫@種特性,使得 共享內(nèi)存成為了最快的進(jìn)程間通信的方式 ,因?yàn)樗?直接通過(guò)虛擬地址來(lái)訪問(wèn)物理內(nèi)存,比前面的管道和后面的消息隊(duì)列 少了內(nèi)核態(tài)和用戶態(tài)的幾次數(shù)據(jù)拷貝和交互 。
特點(diǎn):
共享內(nèi)存的使用流程:
1. 創(chuàng)建共享內(nèi)存
頭文件: #include <sys/ipc.h> #include <sys/shm.h> 定義函數(shù): int shmget(key_t key, size_t size, int shmflg) 參數(shù): key:這個(gè)共享內(nèi)存段名字 size:共享內(nèi)存大小 shmflg:由九個(gè)權(quán)限標(biāo)志構(gòu)成,它們的用法和創(chuàng)建文件時(shí)使用的mode模式標(biāo)志是一樣的。返回值:成功返回共享內(nèi)存標(biāo)識(shí)符,失敗返回-1。2. 將共享內(nèi)存映射到虛擬地址空間
頭文件: #include <sys/types.h> #include <sys/shm.h> 定義函數(shù): void *shmat(int shmid, const void *shmaddr, int shmflg) 參數(shù): shmid: 共享內(nèi)存標(biāo)識(shí) shmaddr:指定連接的地址 shmflg:權(quán)限標(biāo)志返回值:成功返回指向共享內(nèi)存映射在虛擬地址空間的指針(即首地址),失敗返回-1。3. 共享內(nèi)存管理
頭文件: #include <sys/types.h> #include <sys/shm.h> 定義函數(shù): int shmctl(int shmid, int cmd, struct shmid_ds *buf) 參數(shù): shmid:由shmget返回的共享內(nèi)存標(biāo)識(shí)碼 cmd:將要采取的動(dòng)作 buf:指向一個(gè)保存著共享內(nèi)存的模式狀態(tài)和訪問(wèn)權(quán)限的數(shù)據(jù)結(jié)構(gòu)返回值:成功返回0,失敗返回-14. 解除映射關(guān)系
頭文件: #include <sys/types.h> #include <sys/shm.h> 定義函數(shù): int shmdt(const void *shmaddr) 參數(shù):shmaddr: 由shmat所返回的指針 返回值:成功返回0,失敗返回-1消息隊(duì)列
概念:
特性:
- 自帶同步與互斥
- 生命周期隨內(nèi)核
流程:
1. 創(chuàng)建消息隊(duì)列
頭文件: #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> 定義函數(shù): int msgget(key_t key, int msgflg) 參數(shù): key:消息隊(duì)列對(duì)象的關(guān)鍵字 msgflg:消息隊(duì)列的建立標(biāo)志和存取權(quán)限返回值:成功執(zhí)行時(shí),返回消息隊(duì)列標(biāo)識(shí)值。失敗返回-12. 進(jìn)程可以向隊(duì)列中添加/獲取節(jié)點(diǎn)
頭文件: #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> 添加節(jié)點(diǎn): int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); 獲取節(jié)點(diǎn): ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg); 參數(shù): msqid:消息隊(duì)列對(duì)象的標(biāo)識(shí)符 msgp:消息緩沖區(qū)指針 msgsz:消息數(shù)據(jù)的長(zhǎng)度 msgtyp:決定從隊(duì)列中返回哪條消息 msgflg:消息隊(duì)列狀態(tài)返回值:成功執(zhí)行時(shí),返回0。失敗返回-13. 刪除消息隊(duì)列
頭文件: #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> 定義函數(shù): int msgctl(int msqid, int cmd, struct msqid_ds *buf) 參數(shù): msqid:消息隊(duì)列對(duì)象的標(biāo)識(shí)符 cmd:函數(shù)要對(duì)消息隊(duì)列進(jìn)行的操作 buf:取出系統(tǒng)保存的消息隊(duì)列的 msqid_ds 數(shù)據(jù),并將其存入?yún)?shù) buf 指向的 msqid_ds 結(jié)構(gòu)中返回值:成功執(zhí)行時(shí),返回0。失敗返回-1信號(hào)量
概念:
- 信號(hào)量與IPC結(jié)構(gòu)不同,它其實(shí)是 內(nèi)核中的一個(gè)計(jì)數(shù)器和阻塞隊(duì)列 ,通過(guò)信號(hào)量來(lái)對(duì) 臨界資源的訪問(wèn)進(jìn)行控制,來(lái) 實(shí)現(xiàn)進(jìn)程間的同步與互斥 ,而不是用于進(jìn)程間消息的通信的。
- 信號(hào)量用于進(jìn)程間同步,若要在進(jìn)程間傳遞數(shù)據(jù)需要結(jié)合共享內(nèi)存。
- 信號(hào)量基于操作系統(tǒng)的 PV 操作,程序?qū)π盘?hào)量的操作都是原子操作。
- 每次對(duì)信號(hào)量的 PV 操作不僅限于對(duì)信號(hào)量值加 1 或減 1,而且可以加減任意正整數(shù)。
例如:有一個(gè)能容納 n 人的餐廳,用一個(gè)計(jì)數(shù)器表示 n,如果有人進(jìn)入則 n - 1,如果有人出來(lái)則 n + 1,只有 n > 0 時(shí)才能進(jìn)入,如果 n <= 0 時(shí),則說(shuō)明沒(méi)有位置,需要將進(jìn)程掛起并放入阻塞隊(duì)列中,直到有人出來(lái)使資源釋放時(shí),才能將后續(xù)進(jìn)程從阻塞隊(duì)列中喚醒獲取資源。
- 同步:通過(guò)條件判斷實(shí)現(xiàn)臨界資源訪問(wèn)的合理性
- 互斥:通過(guò)同一時(shí)間的唯一訪問(wèn)來(lái)實(shí)現(xiàn)臨界資源訪問(wèn)的安全性
POSIX信號(hào)量: POSIX 信號(hào)量和 SystemV 信號(hào)量作用相同,都是用于同步操作,達(dá)到無(wú)沖突的訪問(wèn)共享資源目的。 但 POSIX 可以用于線程間同步。
流程:
#include <semaphore.h>//初始化信號(hào)量 int sem_init(sem_t *sem, int pshared, unsigned int value); /* 參數(shù): pshared:0表示線程間共享,非零表示進(jìn)程間共享 value:信號(hào)量初值 *///銷毀信號(hào)量 int sem_destroy(sem_t *sem);//等待信號(hào)量 int sem_wait(sem_t *sem); //功能:等待信號(hào)量,會(huì)將信號(hào)量的值減1//發(fā)布信號(hào)量 int sem_post(sem_t *sem); //功能:發(fā)布信號(hào)量,表示資源使用完畢,可以歸還資源了。將信號(hào)量值加1。套接字
使用套接字也可以實(shí)現(xiàn)進(jìn)程間的通信,與其他機(jī)制不同的是,它可以實(shí)現(xiàn)不同機(jī)器之前的進(jìn)程間的通信。
總結(jié)
以上是生活随笔為你收集整理的Linux 进程 | 进程间的通信方式的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 《风色幻想III》流程攻略
- 下一篇: Linux命令集—— cat AND m