2011-02-22 41 views
1

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

# 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 

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

+0

我想我可以解释你看到的奇怪的印刷。你的'sleep(2)`和`printf(“\ n”)`可以是`fflush(0)`。 – nmichaels 2011-02-22 19:51:05

+3

在`printf`修复了什么之后添加`fflush(stdout);`?这可能是stdio缓冲区被复制到子进程中,并且只能在那里打印。 – 2011-02-22 19:51:39

+0

是的!添加fflush可以解决多重打印问题。我还删除了底部的\ n打印以避免多余的空白行。另外,如下所述,我必须在流程打印行之后放置一个fflush(在for循环内)。 – 2011-02-22 20:15:00

回答

3

首先,在if for循环,你想让它不表现。请记住,在fork之后,它会在父进程中返回子PID,并在子进程中返回0。因此,在循环中,第一个fork为父项中的child_1_pid分配一个值,并继续到第二个子句。孩子没有输入if,而是继续下一个循环迭代。第二个条款也是如此。所以只有主流程才能够进入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只在新行(\n),IIRC上刷新。所以在fork没有被刷新之后,缓冲区中仍然有东西存在,并且每个子节点运行printf("\n");并因此刷新缓冲区内容。

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

2

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

printf(“Level \ tProcs \ tParent \ tChild1 \ tChild2 \ nNo。\ tID \ tID \ tID \ tID”);

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

1

替换:

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"); 
0

阅读2.5.1这里:

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

。请注意,后叉(),有两个手柄,其中前存在一个。应用程序应确保如果两个句柄都可以被访问,它们都处于另一个可能首先成为活动句柄的状态。应用程序应准备一个fork(),就像它是活动句柄的更改一样。 (如果进程的一个执行的唯一动作是exec函数或_exit一个()(未退出()),手柄是从来没有在这个过程中访问。)

这意思是说,在致电fork之前,您应该在fork之后的任何流中打电话fflush,这两个流打算在两个过程中使用。