2008-12-12 73 views
9

我有興趣編寫單獨的程序模塊作爲獨立的線程運行,我可以用管道連接在一起。動機是我可以完全獨立地編寫和測試每個模塊,甚至可以用不同的語言編寫它們,或者在不同的機器上運行不同的模塊。這裏有各種各樣的可能性。我已經使用管道了一段時間,但我不熟悉其行爲的細微差別。管道作爲進程間通信

  • 看起來好像接收端會阻止等待輸入,我期望,但發送端塊有時會等待某人從流中讀取?
  • 如果我寫了一個eof到流,我可以繼續寫入該流直到我關閉它?
  • 命名管道和未命名管道的行爲是否存在差異?
  • 首先用命名管道打開管道的哪一端是否有問題?
  • 不同Linux系統之間的管道行爲是否一致?
  • 管道的行爲取決於我使用的shell還是我配置它的方式?
  • 如果我想以這種方式使用管道,是否還有其他問題我應該提問或問題,我應該知道嗎?

回答

4

哇,這是很多問題。讓我們看看我能不能面面俱到......

好像接收端將 塊等待輸入,我會 期待

您預計正確的實際「讀」,來電會阻止,直到有東西在那裏。但是,我相信有一些C函數可以讓你「窺視」管道中等待的東西(以及多少)。不幸的是,我不記得這是否會阻止。

將發送端塊有時 等待有人從 流

沒有讀取,發送不應該阻止。想想看,如果這是通過網絡傳輸到另一臺計算機的結果。你想等待(通過可能的高延遲)讓另一臺計算機響應它收到它嗎?現在,如果目的地的閱讀器句柄已關閉,則這是不同的情況。在這種情況下,你應該有一些錯誤檢查來處理。

如果我寫了一個EOF流中我可以 保持繼續寫入該流 ,直到我關閉它

我認爲這取決於你使用的是什麼語言及其實施管道。在C中,我會說不。在linux shell中,我會說是的。有更多經驗的人將不得不回答這個問題。

行爲 命名管道和未命名管道是否存在差異? 據我所知,是的。但是,我沒有太多的關於named和unnamed的經驗。我相信不同的是:

  • 單向VS雙向通信
  • 讀取和寫入的「in」和線程

會有問題的「走出去」流這管道末端I 首先用命名管道打開?

通常不會,但您可能會遇到初始化時遇到的問題,試圖創建並鏈接線程。你需要有一個主線程來創建所有的子線程並且相互同步它們各自的管道。

在不同的linux系統之間管道的行爲是否一致 ?

同樣,這取決於什麼語言,但通常是肯定的。曾聽說過POSIX?這是標準(至少對於Linux來說,Windows是它自己的東西)。

是否管道的行爲取決於 上我使用的外殼或我 配置的方式?

這是進入更多的灰色地帶。答案應該是是否定的,因爲外殼本質上應該是進行系統調用。然而,直到那一刻,所有的東西都可以爭奪。

中我應該 會問

你問你有系統有了深入瞭解顯示問題的任何其他問題。繼續研究並專注於你將要工作的級別(shell,C等)。儘管如此,你會學到更多東西。

+0

在有統計的管道()的內容偷看並非在所有平臺上可靠。 – 2008-12-13 06:53:05

+1

如果管道緩衝區填滿,寫入結束可能會阻塞 - 它不是很大。 – 2008-12-13 06:53:39

+1

跨機器管道是不存在的?最接近的方法可能是一個套接字,但這不同於管道。 – 2008-12-13 06:54:35

4

這都是基於類UNIX系統;我不熟悉最新版本的Windows的特定行爲。

好像接收端會阻止等待輸入,我期望,但發送端塊有時會等待某人從流中讀取?

是的,雖然在現代機器上它可能不會經常發生。管道有一箇中間緩衝區,可能潛在填滿。如果是這樣,管道的寫入側將確實阻塞。但是如果你仔細想想,沒有太多的文件足以冒這個風險。

如果我寫了一個eof到流,我可以繼續寫入該流,直到我關閉它?

恩,你是說像CTRL-D,0x04?當然,只要這樣設置流。即

506 # cat | od -c 
abc 
^D 
efg 
0000000 a b c \n 004 \n e f g \n       
0000012 

在命名管道和未命名管道行爲方面是否存在差異?

是的,但它們很微妙並且依賴於實施。最大的一點是你可以在另一端運行之前寫入命名管道;使用未命名的管道,文件描述符在fork/exec進程期間共享,所以沒有進程啓動的情況下無法訪問瞬態緩衝區。

這是否有關係我首先打開管道的哪一端?

沒有。

管道在不同的linux系統中的行爲是否一致?

在理由之內,是的。緩衝區大小等可能不同。

管道的行爲取決於我使用的shell還是我配置它的方式?

號當您創建一個管道,會發生什麼在幕後爲你的父進程(殼)創建了一個具有對文件描述符的管道,然後調用fork EXEC這樣的僞代碼:

家長

create pipe, returning two file descriptors, call them fd[0] and fd[1] 
fork write-side process 
fork read-side process 

寫邊

close fd[0] 
connect fd[1] to stdout 
exec writer program 

讀端

​​

有沒有我應該問任何其它疑問或問題,我應該知道,如果我想以這種方式使用的管道?

你想要做的每一件事情都會像這樣排成一行嗎?如果不是,你可能想考慮一個更一般的架構。但是,通過管道的「狹窄」界面進行交互的許多獨立過程的洞察力是理想的,這是一個很好的見解。

[更新:我首先反轉了文件描述符索引。他們現在正確,請參閱man 2 pipe。]

4

正如Dashogun和Charlie Martin指出的,這是一個很大的問題。他們答案的某些部分是不準確的,所以我也要回答。

我感興趣的是編寫單獨的程序模塊,這些模塊作爲獨立的線程運行,我可以用管道連接在一起。

請謹慎使用管道作爲單個進程的線程之間的通信機制。因爲您將在單個進程中讀寫管道的末端,所以您永遠不會收到EOF(零字節)指示。

如果你確實指的是進程,那麼這就是構建工具的經典Unix方法的基礎。許多標準的Unix程序都是從標準輸入中讀取,以某種方式進行轉換並將結果寫入標準輸出的過濾器。例如,tr,sortgrepcat都是過濾器,僅舉幾例。當您操作的數據允許時,這是一個很好的範例。並不是所有的數據操作都有利於這種方法,但有很多。

的動機是我可以完全獨立地編寫和測試每個模塊,甚至可以用不同的語言編寫它們,或者在不同的機器上運行不同的模塊。

好點。請注意,機器之間並沒有真正的管道機制,儘管您可以使用諸如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個字符輸入一個計數和換行符。當它檢測到寫入問題時(緩衝區已滿,因爲孩子沒有讀取任何東西),它會停止循環,寫入最終計數並殺死孩子。