2010-02-26 51 views
6

請看下面的代碼:爲什麼我的父進程沒有看到孩子的輸出,直到它退出?

use IO::File; 
$| = 1; 
my ($handle, $pid) = myPipe(); 
if ($pid == 0) { 
    print "$$"; 
    sleep 5; 
    exit; 
} 

print "child: ".<$handle>."\n"; 

sub myPipe { 
    my $handle = new IO::File(); 
    my $pid = open($handle, "-|"); 
    return ($handle, $pid); 
} 

在這種情況下,「孩子:」進程啓動後消息未出現,持續5秒。如果我從分叉的孩子中刪除睡眠呼叫,則立即打印。爲什麼分叉的孩子必須退出管道才能沖洗父母?

回答

11

有兩個問題。首先,子進程緩衝其輸出;其次,父進程正在使用<>運算符,該運算符會阻塞,直到有完整的行可用,或者直到文件結束。

因此,要獲得您所期望的結果的一種方法是讓子進程寫入後立即關閉其輸出流:

if ($pid == 0) { 
    print "$$"; 
    close STDOUT; 
    sleep 5; 
    exit; 
} 

另一種方式是一個新行添加到子過程的輸出,然後沖洗流:

if ($pid == 0) { 
    print "$$\n"; 
    STDOUT->flush; # "close STDOUT;" will work too, of course 
    sleep 5; 
    exit; 
} 

的沖洗是必要的,因爲管是(通常)緩衝,而不是行緩衝如連接到終端的流通常是這樣。

第三種方法是設置子進程的輸出流自動刷新:

if ($pid == 0) { 
    $| = 1; 
    print "$$\n"; 
    sleep 5; 
    exit; 
} 
+0

感謝您的徹底解答。後來我意識到使用<>會需要換行符。 – dromodel 2010-02-28 15:01:02

2

沖洗管道不會在任何固定的時間表上發生。您可以通過退出子進程(您現在正在執行的操作)或明確調用flush來強制刷新管道。您可以通過執行以下任何導致你的手柄在Perl刷新:

  • 添加\n孩子的消息,年底將(通常)導致管道沖洗
  • 設置$| 1 ,這導致當前選擇的文件句柄自動刷新
  • 使用IO::Handle並且調用$handle->flush
  • 使用IO::Handle和設置$handle->autoflush = 1
+2

沖洗管道沒有由內核來處理。緩衝是一個用戶級功能! – 2010-02-26 18:00:05

3

在某些(大多數?)系統,管道使用默認的I/O緩衝。把

$handle->autoflush(1); 

聲明在你的myPipe函數中。

但即使關閉了緩衝區,Perl仍然不刷新,除了換行符之後。所以你可能也希望你的子進程在輸出中包含一個換行符。


更新:測試代碼(Cygwin的,perl的5.10.0,情況因人而異),我看到的問題是缺乏對孩子的輸出換行的,不在於是否自動沖洗明確開啓時管道被創建。

+1

這並不完全正確。如果autoflush處於打開狀態,則線程緩衝(在每個換行符後刷新),如果autoflush關閉並且句柄打開爲tty,並且塊緩衝(當緩衝區時刷新),則句柄是非緩衝的(每次打印後刷新) ,通常爲幾kB,否則爲滿)。 – hobbs 2010-02-26 18:03:17

+1

回覆:更新 - 請看Sean的回答。換行符不是因爲緩衝,而是因爲父*中的readline /'<>'操作在它返回之前需要讀取一個換行符* - 除了EOF以外:) – hobbs 2010-02-26 18:24:04

+0

@hobbs - 感謝您的澄清。在這個特定的例子中,在父輸入中'<$handle>'的調用不會返回,直到輸入中出現換行符或輸入被關閉。所以換行符仍然是問題,但與我想象中的理由稍有不同。我想可以用'$ /'做一些時髦的事情。 – mob 2010-02-26 18:25:32

相關問題