1. 程式人生 > 實用技巧 >Linux程序間的通訊

Linux程序間的通訊

Linux程序間的通訊

常見的程序間通訊方式:

管道(有名/匿名管道)、訊息佇列(systemv、posix兩個版本)、記憶體共享對映(MMAP)、網路套接字

訊號量、條件變數、互斥鎖、檔案鎖、程序鎖、訊號等

管道

絕大多數程序間的通訊都是基於核心區域

在核心中建立一個緩衝區, 兩個程序向緩衝區讀/寫資料(一般只能取一次)

匿名管道、有名管道

管道大小:4096Byte(根據版本有不同:Ubuntu16.04)

結構:環形佇列

匿名管道

函式

int pipe(int fds[2]);

引數:

傳出管道的兩個訪問方式:讀:fd[0], 寫:fd[1]。

返回值:

成功返回0, 失敗返回-1

使用方式

:程序A和程序B通訊

1. 程序A呼叫pipe()建立管道,獲取管道的訪問方式

2. 程序B要訪問這個管道,需要獲取管道的訪問方式:程序B如果是程序A的子程序, 則程序B會繼承程序A的訪問方式

3. 確定通訊方向:讓一個程序只保留讀訪問方式, 一個程序只保留寫訪問方式

close(fd[0]);
close(fd[1]);

4. 通過wirte寫, read讀

https://www.cnblogs.com/xkDiogt/p/13551918.html

特點

匿名管道只能完成親緣之間的通訊。

讀寫端沒有規定每次傳送/讀取資料的大小, 讀寫時可能產生異常。

單工通訊:只能讀或寫

如果管道寫端關閉, 則讀端會讀到0(檔案末尾)

如果管道讀端關閉, 則寫端會直接被殺死(SIGPIPE訊號)

讀寫端都存在, 管道被寫滿後, 寫端將會阻塞

讀寫端都存在, 管道中沒資料, 讀端將會阻塞

有名管道

1. 建立有名管道:

匿名管道創建出一個核心緩衝區, 有名管道建立核心緩衝區和一個管道檔案

建立管道檔案(不需要手動建立核心緩衝區)

(1)命令

mkfifo filename # 通過命令建立管道檔案

(2)函式

mkfifo(char *names, int mod);

有名管道對管道檔案檔案描述符的讀寫, 實際上是對核心緩衝區的讀寫。

管道檔案只是提供一個操作核心緩衝區的方式, 不允許編輯。

https://www.cnblogs.com/xkDiogt/p/13552017.html

可以進行非親緣程序間的通訊

半雙工

使用有名管道時, 程序需要足夠的許可權(RDWR), 否則會阻塞

記憶體共享對映(MMAP)

1. 建立對映檔案(也可以是一個記憶體塊):size != 0(touch mapfiles即可)

2. 在程序中建立一塊對映記憶體

3. 對映方式:私有對映、共享對映

  私有對映:將對映檔案中的內容拷貝到程序的對映記憶體, 兩塊記憶體互不影響

  共享對映:使用同步機制, 修改一方, 另一方也會改變。

函式

mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); // 建立程序中的對映記憶體

引數:

1. NULL:系統自定義對映空間, 也可以自定義

2. 對映檔案大小, 不可以為0

3. 對映記憶體訪問許可權

4. 對映方式

5. 對映檔案檔案描述符

6. 對映偏移量, 只能是4K整數倍

返回值:

成功返回對映空間地址, 失敗返回NULL

munmap(void *p, size); // 釋放對映記憶體

https://www.cnblogs.com/xkDiogt/p/13553031.html

訊號

將訊號發給不同的程序可以終止、掛起程序等。

檢視系統相容的訊號命令:

kill -l

1~31:Unix經典訊號

SIG開頭:

% kill -l
HUP INT QUIT ILL TRAP ABRT BUS FPE KILL USR1 SEGV USR2 PIPE ALRM TERM STKFLT CHLD CONT STOP TSTP TTIN TTOU URG XCPU XFSZ VTALRM PROF WINCH POLL PWR SYS

32~33:執行緒庫使用(隱藏)

34~64:自定義訊號/實時訊號(硬體)

傳送訊號的方式

需要有足夠的許可權

1. 終端組合按鍵:

ctrl+c # 2號訊號:SIGINT
ctrl+\ # 3號訊號:SIGQUIT
ctrl+z # 20號訊號:掛起程序

2. 通過kill命令傳送訊號:

kill -訊號編號 程序pid # 傳送指定訊號到指定程序

3. 通過函式產生訊號:

kill(pid_t pid, int signo); // 程序pid, 訊號編號
raise(int signo); // 向自己傳送訊號
abort(); // 向自己傳送SIGABRT訊號
sigqueue(pid_t pid, int signo, union sigval); // 可以傳送訊號, 也可以攜帶資訊(程序通訊)
pthread_kill(pthread_t tid, int signo); // 向執行緒傳送訊號

4. 硬體異常產生訊號:

例:非法操作記憶體核心傳送訊號(段錯誤)

5. 浮點數例外導致異常終止(浮點數例外)

6. 排程異常導致異常終止

7. 軟條件產生訊號

例:定時器(SIGARRM)

例:管道讀端關閉傳送訊號殺死寫端(SIGPIPE)

訊號的三種行為和處理動作

1. 預設行為:SIG_DFL

五種預設動作:

  TREM 殺死程序

  CORE 殺死程序同時轉儲核心, 生成core檔案

  IGN 忽略動作, 程序不會受到任何影響

  STOP 掛起程序

  COUNT 喚醒程序

2. 忽略行為:SIG_IGN

沒有任何動作

3. 捕捉行為:SIG_ACTION

自定義動作

訊號失效方案:訊號遮蔽(訊號阻塞)、訊號忽略、訊號捕捉

訊號傳送的流程

https://blog.csdn.net/flowing_wind/article/details/79967588

當我們在shell上寫出一個死迴圈退不出來的時候,只需要一個組合鍵,ctrl+c,就可以解決了,這就是一個訊號,但是真正的過程並不是那麼簡單的。

1、當用戶按下這一對組合鍵時,這個鍵盤輸入會產生一個硬體中斷,如果CPU正在執行這個程序的程式碼時,則該程序的使用者程式碼先暫停執行,使用者從使用者態切換到核心態處理硬體中斷

2、終端驅動程式將這一對組合鍵翻譯成一個SIGINT訊號記在該程序的PCB中(也就是傳送了一個SIGINT訊號給該程序)

3、當某個時刻要從核心態回到該程序的使用者·空間程式碼繼續執行之前,首先處理PCB中的訊號,發現有一個SIGINT訊號需要處理,而這個訊號的預設處理方式是終止程序,所以直接終止程序,不再返回使用者空間執行程式碼。

注意:ctrl+c只能終止前臺程序。一個命令可以加&可以將程序放在後臺執行,這樣shell就不必等待程序結束就可以接收新的命令,啟動新的程序

2、shell可以同時執行一個前臺程序和多個後臺程序,只有前臺程序才能收到ctrl+c這種組合鍵產生的訊號

3、前臺程序在執行過程中使用者可以隨時按下ctrl+c產生一個訊號也就是說前臺程序的使用者空間程式碼執行到任意一個時刻都可能接收到SIGINT訊號而終止,所以訊號對於程序的控制流來說是非同步的

如果未決訊號集對應位為1時, 訊號過來, 不支援排隊, 直接丟棄。

未決訊號集:只有核心可以設定。

阻塞訊號集:使用者可以設定, 用於阻塞/遮蔽某個訊號, 無法抵達, 使其暫時失效。