版權歸原作者所有,如有侵權,請聯(lián)系我們

[科普中國]-匿名管道

科學百科
原創(chuàng)
科學百科為用戶提供權威科普內容,打造知識科普陣地
收藏

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

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

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

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

Windows操作系統(tǒng)使用CreatePipe創(chuàng)建匿名管道。使用ReadFile與WriteFile函數(shù)來讀寫管道。讀寫操作總是阻塞式。新建進程可繼承管道句柄。

讀管道時收到一個end-of-file,意味著管道的寫端句柄已經關閉。

例子:

// 父進程#include int main(){ STARTUPINFO si; PROCESS_INFORMATION pi; char ReadBuf[100]; DWORD ReadNum; HANDLE hRead; // 管道讀句柄 HANDLE hWrite; // 管道寫句柄 BOOL bRet = CreatePipe(&hRead, &hWrite, NULL, 0); // 創(chuàng)建匿名管道 if (bRet == TRUE) printf("成功創(chuàng)建匿名管道!\n"); else printf("創(chuàng)建匿名管道失敗,錯誤代碼:%d\n", GetLastError()); HANDLE hTemp = GetStdHandle(STD_OUTPUT_HANDLE);// 得到本進程的當前標準輸出 SetStdHandle(STD_OUTPUT_HANDLE, hWrite);// 設置標準輸出到匿名管道 GetStartupInfo(&si); // 獲取本進程的STARTUPINFO結構信息 bRet = CreateProcess( // 創(chuàng)建子進程 NULL, // No module name (use command line) (LPSTR)(LPCSTR)"Client.exe", // Command line NULL, // 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("成功創(chuàng)建子進程!\n"); else printf("創(chuàng)建子進程失敗,錯誤代碼:%d\n", GetLastError()); SetStdHandle(STD_OUTPUT_HANDLE, hTemp); // 恢復本進程的標準輸出 CloseHandle(hWrite); // 關閉寫句柄 while (ReadFile(hRead, ReadBuf, 100, &ReadNum, NULL))// 讀管道直至管道關閉 { ReadBuf[ReadNum] = '\0'; printf("從管道[%s]讀取%d字節(jié)數(shù)據(jù)\n", ReadBuf, ReadNum); } if (GetLastError() == ERROR_BROKEN_PIPE) // 輸出信息 printf("管道被子進程關閉\n"); else printf("讀數(shù)據(jù)錯誤,錯誤代碼:%d\n", GetLastError()); return 0;}//子進程的標準輸出實際上已經重定向到匿名管道寫端#include int main(int argc, char* argv[]){ for (int i = 0; i