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

[科普中國]-匿名管道

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

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

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

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

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

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

讀管道時收到一個end-of-file,意味著管道的寫端句柄已經(jīng)關(guān)閉。

例子:

// 父進(jìn)程#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);// 得到本進(jìn)程的當(dāng)前標(biāo)準(zhǔn)輸出 SetStdHandle(STD_OUTPUT_HANDLE, hWrite);// 設(shè)置標(biāo)準(zhǔn)輸出到匿名管道 GetStartupInfo(&si); // 獲取本進(jìn)程的STARTUPINFO結(jié)構(gòu)信息 bRet = CreateProcess( // 創(chuàng)建子進(jìn)程 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)建子進(jìn)程!\n"); else printf("創(chuàng)建子進(jìn)程失敗,錯誤代碼:%d\n", GetLastError()); SetStdHandle(STD_OUTPUT_HANDLE, hTemp); // 恢復(fù)本進(jìn)程的標(biāo)準(zhǔn)輸出 CloseHandle(hWrite); // 關(guān)閉寫句柄 while (ReadFile(hRead, ReadBuf, 100, &ReadNum, NULL))// 讀管道直至管道關(guān)閉 { ReadBuf[ReadNum] = '\0'; printf("從管道[%s]讀取%d字節(jié)數(shù)據(jù)\n", ReadBuf, ReadNum); } if (GetLastError() == ERROR_BROKEN_PIPE) // 輸出信息 printf("管道被子進(jìn)程關(guān)閉\n"); else printf("讀數(shù)據(jù)錯誤,錯誤代碼:%d\n", GetLastError()); return 0;}//子進(jìn)程的標(biāo)準(zhǔn)輸出實際上已經(jīng)重定向到匿名管道寫端#include int main(int argc, char* argv[]){ for (int i = 0; i