【问题标题】:Recursive process tree output is different when forwarded via pipe通过管道转发时,递归进程树输出不同
【发布时间】:2016-08-28 07:10:08
【问题描述】:

我检查了应该递归创建进程树的同一段代码:

void proc_recurse(int layer)
{
    if(layer<=0) return;
    switch(fork()){
    case 0:
        proc_recurse(layer-1);
        printf("%d: child layer = %d\n", getpid(), layer);
        break;
    case -1:
        printf(stderr,"%d: fork failed\n", getpid());
        break;
    default:
        printf("%d: parent \n", getpid());
        break;
    }
}

int  main (int argc, char *argv[]) {
    int i;
    int n = atoi(argv[1]);
    printf("I'm grandParent %d n = %d \n", getpid(), n);

    proc_recurse(n);
}

显然,使用管道时结果完全不同。我可以观察到fork 返回到代码开始的位置(注意在输出中重复出现的“grandParent”消息)。谁能向我解释这种差异的性质。

【问题讨论】:

    标签: c macos unix recursion fork


    【解决方案1】:

    问题在于缓冲。如果您在printfs 之后立即添加对fflush(stdout) 的调用,问题就会消失:

    void proc_recurse(int layer)
    {
        if(layer<=0) return;
        switch(fork()){
        case 0:
            proc_recurse(layer-1);
            printf("%d: child layer = %d\n", getpid(), layer);
            break;
        case -1:
            fprintf(stderr,"%d: fork failed\n", getpid());
            break;
        default:
            printf("%d: parent \n", getpid());
            break;
        }
        fflush(stdout);
    }
    
    int  main (int argc, char *argv[]) {
        int i;
        int n = atoi(argv[1]);
        printf("I'm grandParent %d n = %d \n", getpid(), n);
        fflush(stdout);
        proc_recurse(n);
    }
    

    试运行:

    $ ./a.out 5 | less
    I'm grandParent 7124 n = 5 
    7124: parent 
    7126: parent 
    7126: child layer = 5
    7127: parent 
    7127: child layer = 4
    7127: child layer = 5
    7128: parent 
    7128: child layer = 3
    7128: child layer = 4
    7128: child layer = 5
    7129: parent 
    7130: child layer = 1
    7130: child layer = 2
    7130: child layer = 3
    7130: child layer = 4
    7130: child layer = 5
    7129: child layer = 2
    7129: child layer = 3
    7129: child layer = 4
    7129: child layer = 5
    

    问题在于stdout 在向终端执行输出时通常没有缓冲或行缓冲。但是,一旦您通过管道输出输出,就会使用缓冲区(类似于 4K 缓冲区,但这取决于)。这意味着“grandParent”行立即在主进程的输出中给出,然后 fork 使子进程现在拥有包含该行的缓冲区的副本,并且当它们写入时最后执行输出,你最终会得到重复的内容。

    这是使用fork 等编写程序时的常见错误。在执行fork 之前,您应该输出fflush 以避免此问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-21
      • 2021-12-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多