2010-10-06 177 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。