2016-03-06 139 views
1

我有一個模擬tee命令的perl腳本,所以我可以將輸出寫入終端和日誌文件。它的工作原理是這樣的(錯誤檢查& c略)。perl父進程掛起等待子進程讀取stdin

$pid = open(STDOUT, '-|'); 
# Above is perl magic that forks a process and sets up a pipe with the 
# child's STDIN being one end of the pipe and the parent's STDOUT (in 
# this case) being the other. 
if ($pid == 0) 
{ 
    # Child. 
    # Open log file 
    while (<STDIN>) 
    { 
     # print to STDOUT and log file 
    } 
    #close log files 
    exit; 
} 
# parent 
open STDERR, '>&STDOUT'; 
# do lots of system("...") calls 
close STDERR; 
close STDOUT; 
exit; 

這有時會掛起,並不約而同如果你看一下進程和堆疊說的過程中,父母是懸在關閉的一個,等待孩子退出,而孩子掛讀點從一個文件(它必須是STDIN,因爲沒有其他文件)。

我對如何處理這件事感到茫然。如果你從一個沒有連接到控制檯的shell運行程序,這個問題似乎就會發生 - 在一個正常的shell中運行腳本工作正常 - 並且該腳本中最近更改的唯一代碼是添加打開/關閉文件只是爲了觸摸它(並且在腳本到達這個'tee'代碼之前)。

有沒有人有過這樣的問題,和/或有什麼我可以做的解決這個問題的建議?謝謝。

+0

這是_always_分離時發生嗎? STDOUT是否早於重定向 - 在cron-ed命令行或程序中? – zdim

+0

我看不出問題,對我而言,它無論如何都有效。我更新了自己想要的具體內容,並使用了我正在使用的確切代碼。我正在考慮緩衝/ STDIN越來越混亂。例如,最後的打印(最後一個緩衝區)不會被刷新。 – zdim

回答

0

好了,經過一些實驗似乎開幕STDOUT直接似乎是至少部分原因。我的代碼現在讀這樣的:

$pid = open($handle, '|-'); 
if ($pid == 0) 
{ 
    # Child. 
    # Open log file 
    while (<STDIN>) 
    { 
     # print to STDOUT and log file 
    } 
    #close log files 
    exit; 
} 
# parent 
open my $oldout, '>&STDOUT'; 
open my $olderr, '>&STDERR'; 
open STDOUT, '>&', $handle; 
open STDERR, '>&', $handle; 
# do lots of system("...") calls 
open STDOUT, '>&', $oldout; 
open STDERR, '>&', $olderr; 
close $handle or die "Log child exited unexpectedly: $!\n"; 
exit; 

其中,如果不出意外,看起來比較清爽(但仍比梅西耶我想,因爲我不知道該怎麼做,如果其中任何的DUP都有一個錯誤)。但是我仍然不清楚爲什麼在代碼中更早地打開和關閉句柄對這一點做出了很大的改變。

0

更新

我無法重現犯罪行爲。對我來說,它可以像終端和cron一樣運行。 (如果使用後臺運行,它將在進行fd 1,2時暫停)。考慮到控制檯重定向時出現的問題,這是我想嘗試的方法

1)在子代和父代中明確關閉緩衝

2)關閉STDIN while循環

3)陷阱SIGPIPE看到的東西是否變成了

*)是否有其它的代碼實際上得到絆倒後明確?


下面是對首先發布的解決方法的想法。


看來這個問題是發生了什麼STDOUT與重定向控制檯什麼孩子則可以或不可以用它做。我還沒有知道那是什麼,但同時我認爲,至少應該避免這個問題。

如果沒有tty,孩子可以將輸出意圖寫入STDOUT到另一個文件。此文件可以是父級的重定向(如果已知,附加到),也可以是父級稍後加入的另一個明確打開的文件。這是混亂,粗糙,它改變了設計,但它可能使孩子退出(不處理STDOUT這可能是問題的根源)。

或者,父母可以控制此操作,當沒有tty時打開記錄器子項,這將完全側重於情況,但是離設計更遠。

這些都不能解決問題,但它可能會避免它。


以供參考,這是表現爲從CMDLINE進出的cron的預期代碼

$| = 1; 
$logfile = '/path/logfile.out'; 

$pid = open(STDOUT, '|-'); 
if ($pid == 0) { 
    # $| = 1; 
    open $log_fh, '>', $logfile; 
    while (<STDIN>) { 
     print $_; 
     print $log_fh $_; 
    } 
    # close STDIN; 
    close $log_fh; 
    exit; 
} 
open STDERR, '>&STDOUT'; 

print "After the fork.\n"; 
warn "A warn --"; 

close STDERR; 
close STDOUT; 
exit; 
+0

當未連接到控制檯時,STDOUT將轉到文件。沒有STDIN,但它不使用或需要STDIN。另外,它不會在運行'script&'的背景中運行。當它發生在我身上時,它就像一個cron工作一樣運行。 –

+0

,打開電話分岔一個孩子,並創建一個管道,孩子得到STDIN。問題不是它掛起,因爲沒有STDIN,問題是當它到達主程序的出口時它會掛起(它可以在產生正確的所有生成之前產生大量的輸出) –

+0

孩子STDIN是來自父母的STDOUT。如果我沒有使用STDIN,那麼它根本就不起作用。 –