当我输出输出时,为什么我的分叉程序的输出不同?

时间:2021-01-31 14:04:04

I was looking at some simple code on fork, and decided to try it out for myself. I compiled and then ran it from inside Emacs, and got a different output to that output produced from running it in Bash.

我在fork上看了一些简单的代码,并决定自己尝试一下。我编译然后从Emacs内部运行它,并获得一个不同的输出到通过在Bash中运行它产生的输出。

#include <unistd.h>
#include <stdio.h>

int main() {
  if (fork() != 0) {
    printf("%d: X\n", getpid());
  }

  if (fork() != 0) {
    printf("%d: Y\n", getpid());
  }

  printf("%d: Z\n", getpid());
}

I compiled it with gcc, and then ran a.out from inside Emacs, as well as piping it to cat, and grep ., and got this.

我使用gcc编译它,然后从Emacs内部运行a.out,并将它管道传输到cat和grep。并得到了它。

2055: X
2055: Y
2055: Z
2055: X
2058: Z
2057: Y
2057: Z
2059: Z

2055:X 2055:Y 2055:Z 2055:X 2058:Z 2057:Y 2057:Z 2059:Z

This isn't right. Running it just from Bash I get (which I expected)

这不对。只是从Bash运行它我得到(我预期)

2084: X
2084: Y
2084: Z
2085: Y
2085: Z
2087: Z
2086: Z

2084:X 2084:Y 2084:Z 2085:Y 2085:Z 2087:Z 2086:Z

edit - missed some newlines

编辑 - 错过了一些换行符

What's going on?

这是怎么回事?

4 个解决方案

#1


11  

The order in which different processes write their output is entirely unpredictable. So the only surprise is that sometimes the "X" print statement sometimes happens twice.

不同进程编写输出的顺序完全不可预测。所以唯一令人惊讶的是,有时“X”打印声明有时会发生两次。

I believe this is because sometimes at the second fork(), an output line including "X" is in an output buffer, needing to be flushed. So both processes eventually print it. Since getpid() was already called and converted into the string, they'll show the same pid.

我相信这是因为有时在第二个fork(),包含“X”的输出行在输出缓冲区中,需要刷新。所以这两个过程最终打印出来。由于getpid()已被调用并转换为字符串,因此它们将显示相同的pid。

I was able to reproduce multiple "X" lines, but if I add fflush(stdout); just before the second fork(), I always only see one "X" line and always a total of 7 lines.

我能够重现多条“X”线,但如果我添加fflush(stdout);就在第二个fork()之前,我总是只看到一条“X”线,总共只有7条线。

#2


8  

I think I know what's going on. The stdio buffering will be different when output is a tty versus when it's a pipe or a file. The child processes inherit the parent buffers. When they're flushed, you can get double output.

我想我知道发生了什么事。当输出是tty而不是管道或文件时,stdio缓冲将是不同的。子进程继承父缓冲区。当他们被冲洗时,你可以获得双倍输出。

If you add

如果你添加

fflush(stdout);

right after each printf() call, you'll see what I mean.

在每次printf()调用之后,你会明白我的意思。

The interesting thing is that it's different when standard output is a tty device. It may be that the library knows what that means, and flushes after each line break, or something like that.

有趣的是,当标准输出是tty设备时,它会有所不同。可能是库知道这意味着什么,并在每次换行后刷新,或类似的东西。

#3


6  

So I imagine you are wondering why you are getting more than one "X"?

所以我想你会想知道为什么你会得到不止一个“X”?

This is because buffered output is being flushed twice.

这是因为缓冲输出被刷新两次。

When you pipe a program's output, the stdio library recognizes that your output is not a terminal, and it switches to block buffering instead of line buffering. Consequently, there isn't yet any output when the process forks and so now both parent and child have pending output.

当您管道程序的输出时,stdio库会识别出您的输出不是终端,并且它会切换到块缓冲而不是行缓冲。因此,当进程分叉时,父进程和子进程都有待处理输出时,还没有任何输出。

#4


3  

If you have used stdout at all before forking, you must call fflush(stdout) before fork() (and likewise for any other output FILEs you use). Failure to do so results in undefined behavior. The effect you're seeing comes from stdout being line-buffered when it's connected to a terminal, but fully buffered when it's connected to a pipe. This is not required, but recommended by the standards (POSIX).

如果你在分叉之前已经使用了stdout,你必须在fork()之前调用fflush(stdout)(对于你使用的任何其他输出文件也是如此)。如果不这样做会导致未定义的行为。您看到的效果来自stdout在连接到终端时进行行缓冲,但在连接到管道时完全缓冲。这不是必需的,但是标准(POSIX)推荐。

#1


11  

The order in which different processes write their output is entirely unpredictable. So the only surprise is that sometimes the "X" print statement sometimes happens twice.

不同进程编写输出的顺序完全不可预测。所以唯一令人惊讶的是,有时“X”打印声明有时会发生两次。

I believe this is because sometimes at the second fork(), an output line including "X" is in an output buffer, needing to be flushed. So both processes eventually print it. Since getpid() was already called and converted into the string, they'll show the same pid.

我相信这是因为有时在第二个fork(),包含“X”的输出行在输出缓冲区中,需要刷新。所以这两个过程最终打印出来。由于getpid()已被调用并转换为字符串,因此它们将显示相同的pid。

I was able to reproduce multiple "X" lines, but if I add fflush(stdout); just before the second fork(), I always only see one "X" line and always a total of 7 lines.

我能够重现多条“X”线,但如果我添加fflush(stdout);就在第二个fork()之前,我总是只看到一条“X”线,总共只有7条线。

#2


8  

I think I know what's going on. The stdio buffering will be different when output is a tty versus when it's a pipe or a file. The child processes inherit the parent buffers. When they're flushed, you can get double output.

我想我知道发生了什么事。当输出是tty而不是管道或文件时,stdio缓冲将是不同的。子进程继承父缓冲区。当他们被冲洗时,你可以获得双倍输出。

If you add

如果你添加

fflush(stdout);

right after each printf() call, you'll see what I mean.

在每次printf()调用之后,你会明白我的意思。

The interesting thing is that it's different when standard output is a tty device. It may be that the library knows what that means, and flushes after each line break, or something like that.

有趣的是,当标准输出是tty设备时,它会有所不同。可能是库知道这意味着什么,并在每次换行后刷新,或类似的东西。

#3


6  

So I imagine you are wondering why you are getting more than one "X"?

所以我想你会想知道为什么你会得到不止一个“X”?

This is because buffered output is being flushed twice.

这是因为缓冲输出被刷新两次。

When you pipe a program's output, the stdio library recognizes that your output is not a terminal, and it switches to block buffering instead of line buffering. Consequently, there isn't yet any output when the process forks and so now both parent and child have pending output.

当您管道程序的输出时,stdio库会识别出您的输出不是终端,并且它会切换到块缓冲而不是行缓冲。因此,当进程分叉时,父进程和子进程都有待处理输出时,还没有任何输出。

#4


3  

If you have used stdout at all before forking, you must call fflush(stdout) before fork() (and likewise for any other output FILEs you use). Failure to do so results in undefined behavior. The effect you're seeing comes from stdout being line-buffered when it's connected to a terminal, but fully buffered when it's connected to a pipe. This is not required, but recommended by the standards (POSIX).

如果你在分叉之前已经使用了stdout,你必须在fork()之前调用fflush(stdout)(对于你使用的任何其他输出文件也是如此)。如果不这样做会导致未定义的行为。您看到的效果来自stdout在连接到终端时进行行缓冲,但在连接到管道时完全缓冲。这不是必需的,但是标准(POSIX)推荐。