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

发布于 2024-09-26 13:20:16 字数 709 浏览 2 评论 0原文

我正在编写一个分叉多个子进程的程序,我希望所有这些子进程都能够向 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: $!";

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

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 STDERR and STDOUT without 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:

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.

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

临风闻羌笛 2024-10-03 13:20:16

对于 STDOUT 和 STDIN,写入文件句柄不是原子操作。对于 fifos 这样的事情有一些特殊情况,但这不是您当前的情况。

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

管道解决方案将通过管道(如 shell 中的 | )将子级连接到父级,并且您需要让父级从管道中读取并多路复用输出本身。父级将负责从管道中读取数据,并确保它不会同时将管道的输出与发往父级 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;
}

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

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

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.

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 here of pipes.

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.

女皇必胜 2024-10-03 13:20:16

虽然这对你的乱码没有帮助,但我花了很长时间才找到一种方法来启动一个可以由父进程写入的子进程,并将子进程的 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。

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).

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文