匿名管道

匿名管道

匿名管道用於進程之間通信,且僅限於本地父子進程之間通信,結構簡單,類似於一根非水平狀態的水管,一端進水另一端出水(單工)。相對於命名管道,其占用小實現簡單,在特定情況下,比如實現兩圍棋引擎本地對戰可以使用匿名管道。

基本介紹

  • 中文名:匿名管道
  • 外文名:Anonymous pipes
簡介,管道特徵,匿名管道的使用,Windows作業系統,

簡介

匿名管道正因為提供的功能很單一,所以它所需要的系統的開銷也就比命名管道小很多,在本地機器上可以使用匿名管道來實現父進程和子進程之間的通信,這裡需要注意兩點,第一就是在本地機器上,這是因為匿名管道不支持跨網路之間的兩個進程之間的通信,第二就是實現的是父進程和子進程之間的通信,而不是任意的兩個進程。然後得話還順便介紹匿名管道的另外一種功能,其通過匿名管道可以實現子進程輸出的重定向。

管道特徵

管道是IPC最基本的一種實現機制。我們都知道在Linux下“一切皆檔案”,其實這裡的管道就是一個檔案。管道實現進程通信就是讓兩個進程都能訪問該檔案。
匿名管道的特徵:
①只提供單向通信,也就是說,兩個進程都能訪問這個檔案,假設進程1往檔案內寫東西,那么進程2 就只能讀取檔案的內容。
②只能用於具有血緣關係的進程間通信,通常用於父子進程建通信
③管道是基於位元組流來通信的
④依賴於檔案系統,它的生命周期隨進程的結束結束(隨進程)
⑤其本身自帶同步互斥效果

匿名管道的使用

匿名管道主要用於本地父進程和子進程之間的通信,在父進程中的話,首先是要創建一個匿名管道,在創建匿名管道成功後,可以獲取到對這個匿名管道的讀寫句柄,然後父進程就可以向這個匿名管道中寫入數據和讀取數據了,但是如果要實現的是父子進程通信的話,那么還必須在父進程中創建一個子進程,同時,這個子進程必須能夠繼承和使用父進程的一些公開的句柄,為什麼呢?因為在子進程中必須要使用父進程創建的匿名管道的讀寫句柄,通過這個匿名管道才能實現父子進程的通信,所以必須繼承父進程的公開句柄。同時在創建子進程的時候,必須將子進程的標準輸入句柄設定為父進程中創建匿名管道時得到的讀管道句柄,將子進程的標準輸出句柄設定為父進程中創建匿名管道時得到的寫管道句柄。然後在子進程就可以讀寫匿名管道了。

Windows作業系統

使用CreatePipe創建匿名管道。使用ReadFile與WriteFile函式來讀寫管道。讀寫操作總是阻塞式。新建進程可繼承管道句柄。
讀管道時收到一個end-of-file,意味著管道的寫端句柄已經關閉。
例子:
//  父進程#include <windows.h>int main(){    STARTUPINFO si;    PROCESS_INFORMATION pi;    char ReadBuf[100];    DWORD ReadNum;    HANDLE hRead; // 管道讀句柄    HANDLE hWrite; // 管道寫句柄    BOOL bRet = CreatePipe(&hRead, &hWrite, NULL, 0); // 創建匿名管道    if (bRet == TRUE)        printf("成功創建匿名管道!\n");    else        printf("創建匿名管道失敗,錯誤代碼:%d\n", GetLastError());    HANDLE hTemp = GetStdHandle(STD_OUTPUT_HANDLE);// 得到本進程的當前標準輸出    SetStdHandle(STD_OUTPUT_HANDLE, hWrite);// 設定標準輸出到匿名管道    GetStartupInfo(&si); // 獲取本進程的STARTUPINFO結構信息    bRet = CreateProcess( // 創建子進程                NULL,   // No module name (use command line)   (LPSTR)(LPCSTR)"Client.exe",        // Command lineNULL,           // Process handle not inheritable   NULL,           // Thread handle not inheritable   FALSE,          // Set handle inheritance to FALSE   0,              // No creation flags   NULL,           // Use parent's environment block   NULL,           // Use parent's starting directory    &si,            // Pointer to STARTUPINFO structure   &pi )           // Pointer to PROCESS_INFORMATION structure     if (bRet == TRUE)          printf("成功創建子進程!\n");    else        printf("創建子進程失敗,錯誤代碼:%d\n", GetLastError());    SetStdHandle(STD_OUTPUT_HANDLE, hTemp); // 恢複本進程的標準輸出    CloseHandle(hWrite); // 關閉寫句柄    while (ReadFile(hRead, ReadBuf, 100, &ReadNum, NULL))// 讀管道直至管道關閉    {        ReadBuf[ReadNum] = '\0';        printf("從管道[%s]讀取%d位元組數據\n", ReadBuf, ReadNum);    }    if (GetLastError() == ERROR_BROKEN_PIPE) // 輸出信息        printf("管道被子進程關閉\n");    else        printf("讀數據錯誤,錯誤代碼:%d\n", GetLastError());    return 0;}
//子進程的標準輸出實際上已經重定向到匿名管道寫端#include <stdio.h>int main(int argc, char* argv[]){    for (int i = 0; i < 100; i++) // 傳送一些數據到標準輸出和標準錯誤    {        printf("i = %d\n", i); // 列印提示        cout << "標準輸出:" << i << endl; // 列印到標準輸出        cerr << "標準錯誤:" << i << endl; // 列印到標準錯誤    }    return 0;}
使用管道需要注意的4種特殊情況:
  • 如果所有指向管道寫端的檔案描述符都關閉了,而仍然有進程從管道的讀端讀數據,那么檔案內的所有內容被讀完後再次read就會返回0,就像讀到檔案結尾。
  • 如果有指向管道寫端的檔案描述符沒有關閉(管道寫段的引用計數大於0),而持有管道寫端的進程沒有向管道內寫入數據,假如這時有進程從管道讀端讀數據,那么讀完管道內剩餘的數據後就會阻塞等待,直到有數據可讀才讀取數據並返回。
  • 如果所有指向管道讀端的檔案描述符都關閉,此時有進程通過寫端檔案描述符向管道內寫數據時,則該進程就會收到SIGPIPE信號,並異常終止。
  • 如果有指向管道讀端的檔案描述符沒有關閉(管道讀端的引用計數大於0),而持有管道讀端的進程沒有從管道內讀數據,假如此時有進程通過管道寫段寫數據,那么管道被寫滿後就會被阻塞,直到管道內有空位置後才寫入數據並返回。

相關詞條

熱門詞條

聯絡我們