【问题标题】:Why does fflush() affect the output of forked processes?为什么 fflush() 会影响分叉进程的输出?
【发布时间】:2015-11-26 16:06:37
【问题描述】:

我正在尝试学习 UNIX 编程,遇到了一个关于 fork() 的问题,我无法解释以下 2 个程序的输出。

我知道fork() 创建了一个与当前正在运行的进程相同的进程,但是它从哪里开始呢?例如,如果我有下面这两个程序,输出会是什么,它是如何工作的?

#include<stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main (int argc, char **argv)
{
    int retval;
    printf ("This is most definitely the parent process\n");
    // now here fork will create a child process 
    // i need to know from which line child process starts execution 

    retval = fork ();
    printf ("Which process printed this?\n");

    return (0);
}

上面的程序和下面的程序有什么区别 关于子进程执行:

#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>

int main (int argc, char **argv)
{
    int retval;
    printf ("This is most definitely the parent process\n");

    fflush (stdout);
    // how does fflush change the output of above program and why ?
    // even though no string operations are being used
    retval = fork ();
    printf ("Which process printed this?\n");

    return (0);
 }

我认为他们都应该打印:

This is most definitely the parent process 
Which process printed this? 
Which process printed this? 

但第一个是打印:

This is most definitely the parent process 
Which process printed this? 
This is most definitely the parent process 
Which process printed this? 

【问题讨论】:

  • 你为什么不试着告诉我们输出?
  • 我已经编辑了帖子。感谢您的建议!
  • 您是否尝试过阅读手册页? fork(2)?

标签: c operating-system buffer fork system-calls


【解决方案1】:

执行将在 fork 调用时(或之后)继续。您可以使用返回值来检查您是父进程还是子进程:

返回值
成功时在父进程中返回子进程的PID,返回0 在孩子。失败时,在父进程中返回-1,不创建子进程, 并且 errno 设置得当。

(来源:man fork

例如,如果您有以下程序:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main(int argc, char **argv) {
    printf("Foo.\n");
    int retval = fork();
    printf("Bar from %s (%d).\n", (retval == 0) ? "child" : "parent", retval);
    return 0;
}

输出会是这样的:

Foo.
Bar from parent (18464).
Bar from child (0).

...假设输出是行缓冲的。

【讨论】:

  • fflush 如何改变两个程序的输出?
  • 不应该,如果你的输出是行缓冲的。如果你不确定它会不会,总是调用 fflush 并没有什么坏处。见stackoverflow.com/q/2530663/182402
  • 我不知道行缓冲是什么意思..在 ubuntu 中是否启用了行缓冲?
  • 您的终端可能使用了行缓冲。如果您将程序的输出通过管道传输到另一个程序,它可能会被完全缓冲,而不是行缓冲。如果您想了解更多信息,请阅读man setbuf,否则只需添加fflush(0) 即可。
【解决方案2】:

我知道 fork() 创建了与当前相同的进程 正在运行的进程,但它从哪里开始?

如果fork(2) 成功(即不返回-1),它将从调用fork(2) 的行开始。 fork(2)返回两次:子进程返回0,父进程返回正数C,其中C是新生子进程ID。

您看到 这肯定是父进程两次的原因与 stdio 的缓冲有关。用户空间缓冲区中的 Stdio 缓冲区输出仅在某些条件发生时才被刷新(例如,缓冲区已满)。缓冲模式决定了何时以及如何刷新缓冲区。

通常,如果输出被写入交互设备,例如终端(或伪终端),stdio 是line-buffered,这意味着当找到换行符或@时刷新缓冲区987654327@ 被调用。

OTOH,如果输出被重定向到文件或其他非交互设备(例如,输出被重定向到管道),stdio 是完全缓冲的,这意味着只刷新缓冲区当它们变满或调用 fflush(3) 时。

因此,如果没有fflush(3),在终端设备中执行代码将打印:

This is most definitely the parent process
Which process printed this?
Which process printed this?

这是预期的。然而,如果你通过cat(1) 传递它,你会看到这个(或其他一些变体,取决于执行顺序):

This is most definitely the parent process
Which process printed this?
This is most definitely the parent process
Which process printed this?

这是因为输出在重定向到管道时是完全缓冲的。字符串This is most definitely the parent process 不足以填充和刷新缓冲区,因此当父级分叉时,子级(获取父级内存空间的副本)将获得输出缓冲区的副本,其中已经包含字符串@ 987654334@。所以这两个进程最终都会打印该字符串。

如果你总是在 fork 之前调用fflush(3),这不会发生,因为当父内存空间复制到子时缓冲区是空的。

【讨论】:

    【解决方案3】:

    当调用fork() 时,有三种可能的返回条件。

    请阅读 fork() 的手册页

    三个返回条件是

    -1 -- the fork() failed
     0 -- the child is executing
    some positive number -- the pid of the child, the parent is executing
    

    代码需要是这样的:

    pid_t pid;
    
    pid = fork();
    if ( 0 > pid ) 
    { // then handle error
    }
    else if ( 0 == pid )
    { // then child executing
    }
    else // if ( 0 < pid )
    { // then parent executing
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-05-10
      • 1970-01-01
      • 1970-01-01
      • 2022-11-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多