fork () и STDOUT / STDERR на консоль от дочерних процессов - PullRequest
6 голосов
/ 07 октября 2010

Я пишу программу, которая разветвляет несколько дочерних процессов, и я хотел бы, чтобы все эти дочерние процессы могли записывать строки в STDERR и STDOUT без искажения вывода.Я не делаю ничего необычного, просто выдаю строки, заканчивающиеся новой строкой (по крайней мере, в моем понимании, это будет атомарная операция для Linux).От perlfaq он говорит:

И основной процесс, и фоновый процесс («дочерний» процесс) имеют одинаковые файловые дескрипторы STDIN, STDOUT и STDERR.Если оба пытаются получить к ним доступ сразу, могут произойти странные вещи.Вы можете закрыть или снова открыть их для ребенка.Вы можете обойти это, открыв канал (см. Open), но в некоторых системах это означает, что дочерний процесс не может пережить родительский.

Он говорит, что я должен «закрыть или открыть» эти файловые дескрипторы дляребенок.Закрытие просто, но что значит «открыть»?Я пробовал что-то подобное в моих дочерних процессах, и это не работает (вывод по-прежнему искажается):

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: $!";

Итак, что я делаю не так с этим?Как будет выглядеть пример трубы?Есть ли лучший способ координировать вывод из нескольких процессов вместе на консоль?

Ответы [ 2 ]

9 голосов
/ 07 октября 2010

Запись в файловый дескриптор НЕ атомная для STDOUT и STDIN.Существуют особые случаи для таких вещей, как fifos, но это не ваша текущая ситуация.

Когда он говорит, что снова откройте STDOUT, это означает «создать новый экземпляр STDOUT». Этот новый экземпляр отличается от родительского.Это то, как вы можете иметь несколько терминалов, открытых в вашей системе, и не позволить всем STDOUT идти в одно и то же место.

Канальное решение соединит дочерний элемент с родительским через канал (например, | в оболочке), и вам нужно будет прочитать родительский элемент из канала и мультиплексировать сам вывод.Родитель будет отвечать за чтение из канала и за то, чтобы он не чередовал вывод из канала и вывод, предназначенный для STDOUT родителя одновременно.Есть пример и запись здесь каналов.

Фрагмент:

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.Обязательно посмотрите, как я пропустил такие вещи, как закрытие ненужных файловых дескрипторов.

1 голос
/ 30 октября 2013

Хотя это не поможет вам разобраться, мне потребовалось много времени, чтобы найти способ запустить дочерний процесс, который может быть записан родительским процессом, и получить stderr и stdout дочернего процесса, отправленные непосредственно в экран (это решает неприятные проблемы с блокировками, которые могут возникнуть при попытке чтения с двух разных FD без использования чего-то необычного, например select).

Как только я понял, решение было тривиальным

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

Все из «какой-то дочерней программы» будет отправлено в stdout / stderr, и вы можете просто перекачать данные, записав их в CHLD_IN, и поверить, что они заблокируются, если заполнит дочерний буфер. Для тех, кто вызывает родительскую программу, все выглядит просто как stderr / stdout.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...