2010-10-06 25 views
5

我正在編寫一個程序,它分叉多個子進程,我希望所有這些子進程都能夠寫入STDERRSTDOUT輸出出現亂碼。我沒有做任何奇怪的事情,只是發出以新行結束的行(至少在我的理解中是Linux的原子操作)。來自perlfaq它說:從子進程fork()和STDOUT/STDERR到控制檯

主進程和背景之一(「子」進程)共享相同的STDIN,STDOUT和STDERR文件句柄。如果兩者都試圖一次訪問它們,可能會發生奇怪的事情。您可能想要關閉或重新打開這些孩子。你可以通過打開管道來解決這個問題(見公開),但在某些系統上,這意味着子進程無法超越父進程。

它說我應該「關閉或重新打開」這些孩子的文件句柄。關閉很簡單,但「重新打開」是什麼意思?我已經試過這樣的事情從我孩子的過程中,它不工作(輸出仍然得到亂碼):

open(SAVED_STDERR, '>&', \*STDERR) or die "Could not create copy of STDERR: $!"; 
close(STDERR); 

# re-open STDERR 
open(STDERR, '>&SAVED_STDERR') or die "Could not re-open STDERR: $!"; 

所以,我在做什麼錯呢?它所暗示的管道示例是什麼樣的?有沒有更好的方法來協調來自多個進程的輸出到控制檯?

回答

9

寫入一個文件句柄是不是原子對於STDOUT和STDIN。像fifos這樣的特殊情況,但這不是你現在的情況。

當它說重新打開標準輸出這意味着什麼是「創建一個新的標準輸出輸出實例」這個新的實例是不一樣的父母。這就是爲什麼你可以在你的系統上打開多個終端,而不是把所有的STDOUT都放到同一個地方。

管道解決方案將通過管道將子項連接到父項(如|在shell中一樣),您需要讓父項從管道中讀出並複用輸出本身。父節點負責從管道中讀取數據,並確保它不會同時交叉管道輸出和輸出到父節點STDOUT的輸出。有一個例子和編寫here管道。

一個SNIPPIT:

use IO::Handle; 

pipe(PARENTREAD, PARENTWRITE); 
pipe(CHILDREAD, CHILDWRITE); 

PARENTWRITE->autoflush(1); 
CHILDWRITE->autoflush(1); 

if ($child = fork) { # Parent code 
    chomp($result = <PARENTREAD>); 
    print "Got a value of $result from child\n"; 
    waitpid($child,0); 
} else { 
    print PARENTWRITE "FROM CHILD\n"; 
    exit; 
} 

如何看孩子不寫入stdout,而是使用管道將消息發送給家長,誰不寫其標準輸出。一定要看看,因爲我省略了關閉不需要的文件句柄的事情。

+0

我結束了與IO ::管道和AnyEvent管道和IO選擇,但似乎工作。謝謝 – mpeters 2010-10-07 17:33:53

1

雖然這並沒有幫助你的困難,但我花了很長時間才找到一種方法來啓動一個子進程,該進程可以由父進程寫入並直接發送子進程的stderr和stdout到屏幕上(這解決了當你試圖從兩個不同的FD讀取而不使用某些喜歡的選擇時你可能會遇到的討厭的阻塞問題)。

一旦我想通了,解決辦法是微不足道的

my $pid = open3(*CHLD_IN, ">&STDERR", ">&STDOUT", 'some child program'); 
# write to child 
print CHLD_IN "some message"; 
close(CHLD_IN); 
waitpid($pid, 0); 

一切從「一些孩子計劃」將被排放到標準輸出/標準錯誤,你可以簡單的通過寫CHLD_IN泵數據,並相信它如果孩子的緩衝區已滿,將會阻止。對於父程序的調用者,它們看起來都像stderr/stdout。