正如Dashogun和Charlie Martin指出的,這是一個很大的問題。他們答案的某些部分是不準確的,所以我也要回答。
我感興趣的是編寫單獨的程序模塊,這些模塊作爲獨立的線程運行,我可以用管道連接在一起。
請謹慎使用管道作爲單個進程的線程之間的通信機制。因爲您將在單個進程中讀寫管道的末端,所以您永遠不會收到EOF(零字節)指示。
如果你確實指的是進程,那麼這就是構建工具的經典Unix方法的基礎。許多標準的Unix程序都是從標準輸入中讀取,以某種方式進行轉換並將結果寫入標準輸出的過濾器。例如,tr
,sort
,grep
和cat
都是過濾器,僅舉幾例。當您操作的數據允許時,這是一個很好的範例。並不是所有的數據操作都有利於這種方法,但有很多。
的動機是我可以完全獨立地編寫和測試每個模塊,甚至可以用不同的語言編寫它們,或者在不同的機器上運行不同的模塊。
好點。請注意,機器之間並沒有真正的管道機制,儘管您可以使用諸如rsh
或(更好)ssh
之類的程序來接近它。但是,在內部,這樣的程序可能會從管道讀取本地數據並將該數據發送到遠程計算機,但它們通過套接字在計算機之間進行通信,而不使用管道。
這裏有各種各樣的可能性。我已經使用管道了一段時間,但我不熟悉其行爲的細微差別。
OK;提問是一種(好的)學習方式。當然,實驗也是另一回事。
好像接收端會阻塞等待輸入,我期望這樣做,但發送端塊有時會等待某人從流中讀取數據嗎?
是的。管道緩衝區的大小是有限制的。通常情況下,這是相當小的 - 4096或5120是常見的價值。您可能會發現現代Linux使用更大的價值。您可以使用fpathconf()
和_PC_PIPE_BUF來找出管道緩衝區的大小。 POSIX只要求緩衝區爲512(即_POSIX_PIPE_BUF爲512)。
如果我寫了一個eof到流,我可以繼續寫入該流,直到我關閉它?
從技術上講,沒有辦法將EOF寫入流;您關閉管道描述符以指示EOF。如果你是作爲一個EOF字符思維控制d或控制-Z,那麼那些只是常規字符就管道而言 - 他們只當是在標準模式下運行的終端類型有像EOF的影響(熟,或正常)。
是否有指定的行爲和命名管道的差異?
是的,沒有。最大的區別是,命名管道必須通過一個過程來建立,並且只能由該進程和誰分享過程作爲一個共同的祖先兒童使用。相比之下,以前未關聯的進程可以使用命名管道。下一個重大差異是第一個的結果。有一位不願透露姓名管,你回來,從一個單一的功能(系統)調用pipe()
兩個文件描述符,但你打開使用常規open()
功能的FIFO或命名管道。(有人必須創建具有mkfifo()
通話,然後才能打開一個FIFO;命名管道不需要任何這樣的現有設置。)然而,一旦你有一個文件描述符開放的,有一個命名管道和一個未命名管道之間珍貴的差別不大。
首先用命名管道打開管道的哪一端是否有問題?
否。打開FIFO的第一個進程將(通常)阻塞,直到有另一端打開的進程。如果你打開它閱讀和寫作(非傳統但可能),那麼你不會被封鎖;如果您使用O_NONBLOCK標誌,則不會被阻止。
是管道不同的Linux系統之間保持一致的行爲?
是的。在我使用過的任何系統中,我都沒有聽說過或遇到任何管道問題。
管道的行爲取決於我使用的shell還是我配置它的方式?
否:管道和FIFO獨立於您使用的外殼。
是否有其他問題我應該問或問題我應該知道如果我想用這種方式使用管道?
請記住,您必須在將要寫入的進程中關閉管道的讀取端,並在正在讀取的進程中寫入管道的末端。如果您想通過管道進行雙向通信,請使用兩個單獨的管道。如果你製造複雜的管道安排,小心僵局 - 這是可能的。然而,線性流水線不會死鎖(儘管如果第一個流程從不關閉其輸出,下游流程可能會無限期地等待)。
我觀察到上面和其他答案的意見,管道緩衝區經典限於相當小的尺寸。 @Charlie Martin反駁說,有些版本的Unix有動態管道緩衝區,而且這些緩衝區可能非常大。
我不知道他的心事哪些。我用在Solaris,AIX,HP-UX,MacOS X系統,Linux和Cygwin的/ Windows XP中(如下結果)下面的測試程序:
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
static const char *arg0;
static void err_syserr(char *str)
{
int errnum = errno;
fprintf(stderr, "%s: %s - (%d) %s\n", arg0, str, errnum, strerror(errnum));
exit(1);
}
int main(int argc, char **argv)
{
int pd[2];
pid_t kid;
size_t i = 0;
char buffer[2] = "a";
int flags;
arg0 = argv[0];
if (pipe(pd) != 0)
err_syserr("pipe() failed");
if ((kid = fork()) < 0)
err_syserr("fork() failed");
else if (kid == 0)
{
close(pd[1]);
pause();
}
/* else */
close(pd[0]);
if (fcntl(pd[1], F_GETFL, &flags) == -1)
err_syserr("fcntl(F_GETFL) failed");
flags |= O_NONBLOCK;
if (fcntl(pd[1], F_SETFL, &flags) == -1)
err_syserr("fcntl(F_SETFL) failed");
while (write(pd[1], buffer, sizeof(buffer)-1) == sizeof(buffer)-1)
{
putchar('.');
if (++i % 50 == 0)
printf("%u\n", (unsigned)i);
}
if (i % 50 != 0)
printf("%u\n", (unsigned)i);
kill(kid, SIGINT);
return 0;
}
我很好奇,想從其他平臺的額外效果。這是我找到的尺寸。所有結果都比我預想的要大,我必須承認,但查理和我可能在談到緩衝區大小時爭論「相當大」的含義。
- 8196 - HP-UX 11.23的IA-64(的fcntl(F_SETFL)失敗)
- 16384 - 的Solaris 10
- 16384 - 的MacOS X 10.5(O_NONBLOCK沒有工作,雖然的fcntl(F_SETFL)沒有失敗)
- 32768 - AIX 5。3
- 65536 - Cygwin的/ Windows XP中(O_NONBLOCK沒有工作,雖然的fcntl(F_SETFL)沒有失敗)
- 65536 - 在SuSE Linux 10(和CentOS)(的fcntl(F_SETFL)失敗)
從這些測試中清楚的一點是,O_NONBLOCK可以在某些平臺上使用管道,而不是在其他平臺上使用。
該程序創建一個管道,和叉。孩子關閉管道的寫入端,然後進入休眠狀態,直到獲得信號 - 這就是暫停()所做的。然後,父級關閉管道的讀取端,並在寫入描述符上設置標誌,以便它不會阻塞試圖在完整管道上寫入的操作。然後循環,每次寫入一個字符,併爲每個寫入的字符打印一個點,並每隔50個字符輸入一個計數和換行符。當它檢測到寫入問題時(緩衝區已滿,因爲孩子沒有讀取任何東西),它會停止循環,寫入最終計數並殺死孩子。
在有統計的管道()的內容偷看並非在所有平臺上可靠。 – 2008-12-13 06:53:05
如果管道緩衝區填滿,寫入結束可能會阻塞 - 它不是很大。 – 2008-12-13 06:53:39
跨機器管道是不存在的?最接近的方法可能是一個套接字,但這不同於管道。 – 2008-12-13 06:54:35