2016-03-06 3 views
1

У меня есть скрипт perl, который эмулирует команду tee, поэтому я могу получить вывод, записанный на терминал и файл журнала. Он работает примерно так (ошибка проверки & 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, потому что другого файла нет).

Я скорее затрудняюсь с тем, как с этим бороться. Проблема возникает, если вы запускаете программу из оболочки, которая не подключена к консоли. Работа скрипта в нормальной оболочке работает нормально, и единственный фрагмент кода, который недавно изменился в этом скрипте, - это добавление открывать/закрывать файл, чтобы просто коснуться его (и до того, как скрипт доберется до этого кода «tee»).

У кого-нибудь были проблемы, подобные этому раньше, и/или есть предложение относительно того, что я могу сделать, чтобы исправить это? Благодарю.

+0

Выполняется ли это при удалении? 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; 

который, если ничего другого, выглядит чище (но все еще грязнее, чем хотелось бы, как я не знаю, что делать, если какой-либо из этих дубликатов имеет ошибку). Но я до сих пор неясно, почему открытие и закрытие дескриптора намного раньше в коде сделало такую ​​разницу с этим битом.

0

Update

Я не могу воспроизвести поведение нарушителя. Для меня это работает как ожидалось как от терминала, так и от cron. (Если он задан, он будет заблокирован при переходе на fd 1,2.) Учитывая, что проблема возникает при перенаправлении консоли, это то, что я хотел попробовать.

1) Отключить буферизацию явно как в дочернем, так и в родительском

2) Закрыть STDIN явно после того, как время цикла

3) Ловушка SIGPIPE увидеть превращается ли что-то до

*) Является ли на самом деле получить споткнулся другой кусок кода вверх?


Ниже приведены мысли об обходных решениях, опубликованных вначале.


кажется, что проблема с тем, что происходит с STDOUT с перенаправлены консолями и с тем, что ребенок может тогда и не может с ним делать. Я еще не знаю, что это такое, но тем временем я думаю, что что-то в следующих строках должно, по крайней мере, уклоняться от проблемы.

Ребенок может написать вывод, предназначенный для STDOUT, еще одному файлу, если нет tty. Этот файл может быть перенаправлением родителя (если известно, добавлено) или другим явно открытым файлом, к которому позже присоединился родитель. Это грязный, грубый, и он меняет дизайн, но он может позволить ребенку выйти (не имея отношения к STDOUT, который может быть источником проблемы).

В качестве альтернативы родительский элемент может управлять этим, открывая дочерний элемент журнала иначе, если нет tty, что полностью перешагнет ситуацию, но отходит еще дальше от дизайна.

Ничего из этого не решает проблему, но это может избежать этого.


Для справки, это код, который ведет себя, как и ожидалось от CmdLine и из хрон

$| = 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

childs STDIN is STDOUT от родителя. Это не сработало бы вообще, если бы я не использовал STDIN –

Смежные вопросы