處理序間通訊(Inter Process Communication), 簡稱IPC. 它的實現有很多種方法, 這篇文章講最簡單最古老的一種方法, 管道.
1. 概念:
當在shell中執行"ls /usr/src | grep linux-2.6.18"時, "|"即為管道符號, 這條命令會由shell對管道前後的兩種語句分別單獨建立一個進程執行, 然後將前一條命令進程的標準輸出用管道與後一條命令進程的標準輸入相串連.
2. 限制:
管道是UNIX系統IPC的最古老的形式, 所有UNIX系統都提供此種通訊機制. 但管道有以下兩種限制:
- 半雙工, 即資料只能在洋方向上流動.
- 它們只能在具有公用祖先的進程之間使用.
3. 建立管道(pipe):
- 標頭檔: <unistd.h>
- 原型: init pipe(int filedes[2]);
- 傳回值: 成功則返回0, 出錯則返回-1.
- 參數: filedes返回兩個檔案描述符, filedes[0]為讀而開啟, filedes[1]為寫而開啟. filedes[1]的輸出是filedes[0]的輸入.
我們可以把管道理解為在核心空中存在的實體, 進程通過這個實體進行通訊.
- filedes[1]向管道中寫入資料.
- filedes[0]從管道中讀取資料.
但前提是這兩個進程必須有公用的祖先.
比較常見的是, 一個管道由一個進程建立, 然後該進程調用fork, 此後父子進程之間的通訊就使用該管道.
管道在單個進程中幾乎是沒有用處的, 通常調用pipe的進程接著調用fork, 這樣就建立了從父進程到子進程的IPC通道.
- 為了構造父進程到子進程的管道, 父進程關閉讀端(filedes[0]), 子進程關閉寫端(filedes[1]).
- 為了構造子進程到父進程的管道, 父進程關閉寫端(filedes[1]), 子進程關閉讀端(filedes[0]).
當管道一端被關閉後, 下列兩條規則起作用:
- 當讀一個寫端已被關閉的管道時, 在所有資料都被讀取後, read返回0, 以指示達到了檔案結束處. (通常一個管道只有一個讀進程和一個寫進程).
- 當寫一個讀端已被關閉的管道, 則產生訊號SIGPIPE. 如果忽略該訊號或者捕捉該訊號並從其處理常式返回, 則write返回-1, errno設定為EPIPE.
4. 範例程式碼:
#include <stdio.h>
#include <unistd.h>
#define MAXLINE 512
int main()
...{
int n;
int fd[2];
pid_t pid;
char line[MAXLINE];
if (pipe(fd) < 0)
perror("pipe");
if ((pid = fork()) < 0)
...{
perror("fork");
}
else if (pid > 0) /**//* parent */
...{
close(fd[0]);
write(fd[1], "hello world ", 12);
}
else
...{
close(fd[1]);
n = read(fd[0], line, MAXLINE);
write(STDOUT_FILENO, line, n);
}
return 0;
}
該段代碼直接對管道描述符調用read和write, 更好的結構應該是對管道的讀和寫操作封裝為通訊操作.
5. popen和pclose函數:
常見的操作是建立一個管道串連到另一個進程, 然後讀其輸出端或者向其輸入端發送資料. 為此, 標準I/O庫提供了兩個函數popen和pclose. 這兩個函數實現的操作是: 建立一個管道, 調用fork產生一個子進程, 關閉該管道的不使用端, 執行一個shell以運行命令, 然後等待命令終止.
- 標頭檔: <stdio.h>
- 原型:
- FILE *popen(const char *cmdstring, const char *type);
- int pclose(FILE *fp);
- 傳回值:
- popen: 成功則返迴文件指標, 出錯則返回NULL.
- pclose: 返回cmdstring的終止狀態, 出錯則返回-1.
- 說明: 函數popen先執行fork, 然後調用exec執行cmdstring, 並且返回一個標準I/O檔案指標.
- 如果type是"r", 則檔案指標串連到cmdstring的標準輸出.
- 如果type是"w", 則檔案指標串連到cmdstring的標準輸入.