【问题标题】:fork() causes column headers to print for each processfork() 导致为每个进程打印列标题
【发布时间】:2011-02-22 19:45:13
【问题描述】:

我正在使用 fork() 编写一个简单的 C 程序来创建进程的二叉树。我能够获得我需要的所有输出(进程的 pid、它的父进程和它的两个子进程)。不幸的是,每个分叉的进程都希望打印出列标题。如何确保标头的 printf 只执行一次?

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

int main(int argc, char *argv[]){

//Declarations
int i;
int child_1_pid, child_2_pid;
int num_levels = atoi(argv[1]);

//Output banners
//execlp("/bin/echo", "echo", "Level\tProcs\tParent\tChild1\tChild2\nNo.\tID\tID\tID\tID", (char *) NULL);
//if(getpid() > 0)
printf("Level\tProcs\tParent\tChild1\tChild2\nNo.\tID\tID\tID\tID");

//Creates binary tree of processes
for(i = 0; i < num_levels; i++){
    if((child_1_pid = fork()) && (child_2_pid = fork())){
        printf("\n%d\t%d\t%d\t%d\t%d", i, getpid(), getppid(), child_1_pid, child_2_pid);
        sleep(2); //prevents parent from terminating before child can get ppid (parent's pid)
        break; //why?       
    }   
}//end for

printf("\n"); //EXPLAIN ME!!
exit(0);
}//end main

还有一些代码(确实是错误检查),但我真正的问题是输出横幅部分下的 printf 执行多次,给出这样的输出(但正确对齐):

Level Procs   Parent  Child1  Child2
No.   ID  ID  ID  ID
No.   ID  ID  ID  ID
No.   ID  ID  ID  ID
No.   ID  ID  ID  ID
No.   ID  ID  ID  ID
No.   ID  ID  ID  ID
No.   ID  ID  ID  ID
0 30796   24743   30797   30798
1 30797   30796   30799   30800
1 30798   30796   30801   30802

我尝试了一些想法(包括那些在横幅部分注释掉的想法),但似乎没有任何效果,而且大多数“修复”使问题变得更糟!

【问题讨论】:

  • 我想我可以解释一下您看到的奇怪印刷。您的sleep(2)printf("\n") 可能是fflush(0)s。
  • printf 之后添加fflush(stdout); 可以解决任何问题吗?可能是 stdio 缓冲区被复制到子进程中并且只打印在那里。
  • 是的!添加 fflush 确实可以解决多打印问题。我还删除了底部的 \n 打印以避免过多的空白行。此外,如下所述,我必须在进程打印行之后(在 for 循环内)放置一个 fflush。

标签: c fork printf


【解决方案1】:

首先,for 循环中的 if 不会像您希望的那样运行。请记住,在 fork 之后,它在父进程中返回子 PID,在子进程中返回 0。所以在循环内部,第一个 fork 为 parent 中的 child_1_pid 分配一个值,然后继续到第二个子句。孩子没有进入if,而是继续下一个for循环迭代。第二个子句也是如此。所以只有主进程应该能够进入if 的主体,但没有子进程。我想知道为什么输出结果不一样。

所以要得到你的“二叉树”,你实际上应该有这个:

// COMPLETELY UNTESTED
for(i = 0; i < num_levels; i++){
    if (!(child_1_pid = fork()) || !(child_2_pid = fork())) {
        printf("\n%d\t%d\t%d\t%d\t%d", i, getpid(), getppid(), child_1_pid, child_2_pid);
        // A child process, go on to next iteration.
        continue;
    }

    // A parent process. Wait for children, then stop.
    if (child_1_pid) wait();
    if (child_2_pid) wait();
    break;
}

横幅的奇怪输出与流的刷新有关。通常, fprintf 仅在 IIRC 换行符 (\n) 上刷新。所以在fork之后缓冲区中还有东西还没有被刷新,每个孩子都运行printf("\n");从而刷新缓冲区内容。

解决方案是在第一个 printf 的末尾添加一个“\n”,或者在 for 循环之前调用 fflush(stdout);

【讨论】:

  • 在 for 循环之前需要 fflush 是完全正确的。我还必须在横幅打印声明之后添加一个。感谢您提供有关 printf 和刷新换行符的提示......这让事情变得更清楚了。
  • 这将创建一个不平衡的二叉树,因为只有第一次调用 fork() 的父进程由于短路而第二次调用 fork()。这可能是也可能不是您的意图。
  • @SiegeX:你是对的。使用 | 而不是 || 应该可以解决此问题。
【解决方案2】:

这里有一些东西可以尝试,虽然我对这些东西有点生疏。在您打印横幅的行中:

printf("Level\tProcs\tParent\tChild1\tChild2\nNo.\tID\tID\tID\tID");

可能是\n 之后的所有内容都留在了输出缓冲区中,所以当每个孩子分叉时它仍然存在。尝试在 printf 的末尾添加另一个 \n,并在循环内从 printf 的开头删除 \n

【讨论】:

  • 我认为这是正确的想法。上面的答案提到 printf 在 \n 上刷新,所以你似乎是正确的。
  • @John Smith 是的。不过,@DarkDust 的答案肯定比我更彻底。
【解决方案3】:

替换:

printf("Level\tProcs\tParent\tChild1\tChild2\nNo.\tID\tID\tID\tID");

与:

puts("Level\tProcs\tParent\tChild1\tChild2\nNo.\tID\tID\tID\tID");

替换:

printf("\n%d\t%d\t%d\t%d\t%d", i, getpid(), getppid(), child_1_pid, child_2_pid);

与:

printf("%d\t%d\t%d\t%d\t%d\n", i, getpid(), getppid(), child_1_pid, child_2_pid);

删除:

printf("\n");

【讨论】:

    【解决方案4】:

    在此处阅读 2.5.1:

    http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html

    请注意,在 fork() 之后,存在两个句柄,而之前存在一个句柄。应用程序应确保,如果两个句柄都可以访问,则它们都处于另一个可以首先成为活动句柄的状态。应用程序应为 fork() 做好准备,就像它是活动句柄的更改一样。 (如果其中一个进程执行的唯一操作是 exec 函数或 _exit()(不是 exit())之一,则永远不会在该进程中访问句柄。)

    这意味着,在调用fork 之前,您应该在fork 之后打算在两个进程中使用的任何流上调用fflush

    【讨论】:

      猜你喜欢
      • 2011-10-28
      • 2015-12-24
      • 1970-01-01
      • 2019-09-10
      • 2020-11-24
      • 2012-03-08
      • 2010-12-12
      • 1970-01-01
      相关资源
      最近更新 更多