开发者

Why is the output of my forking program different when I pipe its output?

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.

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

2055: X

2055: Y

2055: Z

2055: X

2058: Z

2057: Y

2057: Z

2059: Z

This isn't ri开发者_开发知识库ght. Running it just from Bash I get (which I expected)

2084: X

2084: Y

2084: Z

2085: Y

2085: Z

2087: Z

2086: Z

edit - missed some newlines

What's going on?


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.

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.

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.


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.

If you add

fflush(stdout);

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

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.


So I imagine you are wondering why you are getting more than one "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.


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

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜