LINUX進程通訊shell
公開的交流方式有:信號量,消息隊列,共享內存,有名管道,文件socket
祕密的信息僅限於交流雙方知道的有:信號通訊,無名管道通訊和socket通訊函數
一.匿名管道pipespa
#include <unistd.h>設計
int pipe(int filedes[2]);code
管道做用於有血緣關係的進程之間,經過fork來傳遞 blog
調用pipe函數時在內核中開闢一塊緩衝區(稱爲管道)用於通訊,它有一個讀端一個寫端,而後經過filedes參數傳出給用戶程序兩個文件描述符,filedes[0]指向管道的讀端,filedes[1]指向管道的寫端(很好記,就像0是標準輸入1是標準輸出同樣)。因此管道在用戶程序看起來就像一個打開的文件,經過read(filedes[0]);或者write(filedes[1]);向這個文件讀寫數據實際上是在讀寫內核緩衝區。pipe函數調用成功返回0,調用失敗返 回-1。繼承
1.父進程調用pipe開闢管道,獲得兩個文件描述符指向管道的兩端。隊列
2.父進程調用fork建立子進程,那麼子進程也有兩個文件描述符指向同一管道。進程
3.父進程關閉管道讀端,子進程關閉管道寫端。父進程能夠往管道里寫,子進程能夠從管道里讀,管道是用環形隊列實現的,數據從寫端流入從讀端流出,這樣就實現了進程間通訊。
使用管道須要注意如下4種特殊狀況(假設都是阻塞I/O操做,沒有設置O_NONBLOCK標 志):
1.寫端關閉,讀端將剩餘數據讀出後,再read會返回0
2.寫端未關閉,讀端將剩餘數據讀出後,再次read會阻塞,直到管道中有數據可讀了纔讀取數據並返回
3.讀端都關閉了,寫端write,那麼該進程會收到信號SIGPIPE,一般會致使進程異常終止。
4.讀端未關閉,但未read,那麼在管道被寫滿時再次write會阻塞,直到管道中有空位置了才寫入數據並返回。
管道的主要侷限性正體如今它的特色上:
1. 只支持單向數據流;
2. 只能用於具備親緣關係的進程之間;
3. 沒有名字;
4. 管道的緩衝區是有限的(管道制存在於內存中,在管道建立時,爲緩衝區分配一個頁面大小);
5. 管道所傳送的是無格式字節流,這就要求管道的讀出方和寫入方必須事先約定好數據的格式,好比多少字節算做一個消息(或命令、或記錄)等等
默認建立的爲阻塞管道,若是想設置爲非阻塞管道, fcntl函數設置O_NONBLOCK標誌
實踐經驗:在進程通訊中,沒法判斷每次通訊的字節數,即沒法對數據進行自動拆分,致使子進程一次性讀取父進程兩次通訊的報文狀況,爲了能正常拆分發送報文,咱們常採用如下幾種方法:
1.固定長度,2.顯示長度;每一個報文有長度域和數據域組成。3.短鏈接,進程間每次須要通訊時,建立一個通訊線路,發送一條報文後,當即廢棄這條通訊線路.Soket通訊中經常使用。
經典模型:
一 。 單向管道流
二 。 雙向管道流模型
三。鏈接標準I/O的標準管道模型
設計一個父進程標準輸出流鏈接到子進程標準輸入流的管道
使用函數dup2重定向
代碼示例:
#include <unistd.h> #include <stdio.h> int main() { int fildes[2]; pid_t pid; int i, j; char buf[256]; if (pipe(fildes) < 0 || (pid = fork()) < 0) /* 建立管道和子進程 */ { fprintf(stderr, "error!\n"); return 1; } if (pid == 0) { /* ―――――――――――――――――子進程―――――――――――――――――― */ close(fildes[1]); dup2(fildes[0], 0); /* 重定向stdin到fildes[0]中 */ close(fildes[0]); gets(buf); /* 讀入輸入,實際上是讀取父進程輸出 */ fprintf(stderr, "child:[%s]\n", buf); return 2; } /* ―――――――――――――――――父進程―――――――――――――――――― */ close(fildes[0]); dup2(fildes[1], 1); /* 重定向stdout到fildes[1]中 */ close(fildes[1]); puts("Hello!"); /* 輸出,同時增長子進程的輸入信息*/ return 0; }
父進程的標準輸出已經重定向到管道中,故父進程puts未能將結果打印到屏幕上。
四。popen模型
#include <stdio.h>
FILE *popen(const char* command, char* type);
int pclose (FILE* stream);
popen工做原理:相似於system,首先fork一個子進程,而後調用exec執行參數command中給定的shell命令。不一樣的是,函數popen自動在父進程和exec建立的子進程之間創建了一個管道。
參數type:r 建立與子進程的標準輸出鏈接的管道(管道數據由子進程流向父進程)
open調用成功返回標準I/O的FILE文件流,它的讀寫屬性由參數type決定,調用失敗返回NULL
pclose關閉popen打開的文件流,成功調用返回exec進程退出時的狀態,不然返回-1。
w 建立與子進程的標準輸出鏈接的管道(管道數據由父進程流向子進程)
/*------------popen----------*/ #include <stdio.h> void main() { FILE *out, *in; if ( (out = popen( "grep init", "w" ) ) == NULL ) { fprintf( stderr, "error!/n" ); return; } if ( (in = popen( "ps -ef", "r" ) ) == NULL ) { fprintf( stderr, "error !/n" ); return; } while ( fgets( buf, sizeof(buf), in ) ) /*讀取ps -ef結果*/ fputs( buf, out ); pclose( out ); pclose( in ); }
命令ps -ef: 分析當前運行的所有進程,並將結果打印到屏幕上。
進程「grep init」從輸入的字符串中查找包含字符「init」的子串,並打印結果。