从子进程到控制台的fork()和STDOUT/STDERR

mpe*_*ers 6 linux console perl fork

我正在编写一个程序,它会分叉多个子进程,我希望所有这些子进程能够在输出乱码STDERRSTDOUT没有输出的情况下写入行.我没有做任何花哨的事情,只是发出以新行结束的行(至少在我的理解中,这将是Linux的原子操作).从perlfaq它说:

主进程和后台进程("子进程")共​​享相同的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: $!";
Run Code Online (Sandbox Code Playgroud)

那么,我做错了什么呢?它所暗示的管道示例是什么样的?有没有更好的方法将多个进程的输出协调到控制台?

Pau*_*bel 9

写入文件句柄对于STDOUT和STDIN来说不是原子的.像fifos这样的事情有特殊情况,但这不是你目前的情况.

当它说重新打开STDOUT意味着"创建一个新的STDOUT实例"这个新实例与父实例不同.这是你可以在你的系统上打开多个终端而不是让所有STDOUT到达同一个地方的方法.

管道解决方案将通过管道(如shell中的|)将子节点连接到父节点,并且您需要将父节点从管道中读出并复用输出本身.父级将负责从管道读取并确保它不会同时交错管道的输出和指定给父级STDOUT的输出.这里有一个例子,书面记录这里的管道.

一个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;
}
Run Code Online (Sandbox Code Playgroud)

看看孩子如何不写入stdout,而是使用管道向父母发送消息,父母用stdout写作.一定要看看,因为我省略了诸如关闭不需要的文件句柄之类的东西.