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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3876492/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 23:36:38  来源:igfitidea点击:

fork() and STDOUT/STDERR to the console from child processes

linuxperlconsolefork

提问by mpeters

I'm writing a program that forks multiple child processes and I'd like for all of these child processes to be able to write lines to STDERRand STDOUTwithout the output being garbled. I'm not doing anything fancy, just emitting lines that end with a new line (that, at least in my understanding would be an atomic operation for Linux). From perlfaq it says:

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

Both the main process and the backgrounded one (the "child" process) share the same STDIN, STDOUT and STDERR filehandles. If both try to access them at once, strange things can happen. You may want to close or reopen these for the child. You can get around this with opening a pipe (see open) but on some systems this means that the child process cannot outlive the parent.

主进程和后台进程(“子”进程)共享相同的 STDIN、STDOUT 和 STDERR 文件句柄。如果两者都尝试同时访问它们,就会发生奇怪的事情。您可能希望为孩子关闭或重新打开这些。您可以通过打开管道(参见 open)来解决这个问题,但在某些系统上,这意味着子进程不能比父进程存活时间更长。

It says I should "close or reopen" these filehandles for the child. Closing is simple, but what does it mean by "reopen"? I've tried something like this from within my child processes and it doesn't work (the output still gets garbled):

它说我应该为孩子“关闭或重新打开”这些文件句柄。关闭很简单,但“重新打开”是什么意思?我已经在我的子进程中尝试过这样的事情,但它不起作用(输出仍然会出现乱码):

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

So, what am I doing wrong with this? What would the pipe example it alludes to look like? Is there a better way to coordinate output from multiple processes together to the console?

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

采纳答案by Paul Rubel

Writes to a filehandle are NOTatomic for STDOUT and STDIN. There are special cases for things like fifos but that's not your current situation.

写入文件句柄对于 STDOUT 和 STDIN不是原子的。像 fifos 这样的东西有一些特殊情况,但这不是你目前的情况。

When it says re-open STDOUT what that means is "create a new STDOUT instance" This new instance isn't the same as the one from the parent. It's how you can have multiple terminals open on your system and not have all the STDOUT go to the same place.

当它说重新打开 STDOUT 时,这意味着“创建一个新的 STDOUT 实例”这个新实例与来自父级的实例不同。这就是您如何在系统上打开多个终端,而不是让所有 STDOUT 都转到同一个地方。

The pipe solution would connect the child to the parent via a pipe (like | in the shell) and you'd need to have the parent read out of the pipe and multiplex the output itself. The parent would be responsible for reading from the pipe and ensuring that it doesn't interleave output from the pipe and output destined to the parent's STDOUT at the same time. There's an example and writeup hereof pipes.

管道解决方案将通过管道(如外壳中的 | )将子级连接到父级,并且您需要让父级从管道中读出并多路复用输出本身。父级将负责从管道中读取并确保它不会同时将来自管道的输出和发往父级 STDOUT 的输出交错。这里有一个例子,书面记录这里的管道。

A 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;
}

See how the child doesn't write to stdout but rather uses the pipe to send a message to the parent, who does the writing with its stdout. Be sure to take a look as I omitted things like closing unneeded file handles.

看看孩子如何不写入标准输出而是使用管道向父母发送消息,父母使用其标准输出进行写入。请务必查看,因为我省略了关闭不需要的文件句柄之类的内容。

回答by EJ Campbell

While this doesn't help your garbleness, it took me a long time to find a way to launch a child-process that can be written to by the parent process and have the stderr and stdout of the child process sent directly to the screen (this solves nasty blocking issues you may have when trying to read from two different FD's without using something fancy like select).

虽然这对你的乱码没有帮助,但我花了很长时间才找到一种方法来启动一个可由父进程写入的子进程,并将子进程的 stderr 和 stdout 直接发送到屏幕(这解决了在尝试从两个不同的 FD 读取而不使用像 select 这样的花哨的东西时可能遇到的讨厌的阻塞问题。

Once I figured it out, the solution was trivial

一旦我想通了,解决方案就很简单了

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

Everything from "some child program" will be emitted to stdout/stderr, and you can simply pump data by writing to CHLD_IN and trust that it'll block if the child's buffer fills. To callers of the parent program, it all just looks like stderr/stdout.

来自“某个子程序”的所有内容都将发送到 stdout/stderr,您可以通过写入 CHLD_IN 来简单地抽取数据,并相信它会在子缓冲区填满时阻塞。对于父程序的调用者来说,它看起来就像 stderr/stdout。