fork() 导致为每个进程打印列标题

发布于 2024-10-18 09:54:51 字数 1442 浏览 1 评论 0原文

我正在编写一个简单的 C 程序,使用 fork() 创建进程的二叉树。我能够获得所需的所有输出(进程的 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 执行多次,给出如下输出(但正确对齐):

关卡过程父级子级1子级2
编号 ID ID ID ID ID
编号 ID ID ID ID ID
编号 ID ID ID ID ID
编号 ID ID ID ID ID
编号 ID ID ID ID ID
编号 ID ID ID ID ID
编号 ID ID ID ID ID
0 30796 24743 30797 30798
1 30797 30796 30799 30800
1 30798 30796 30801 30802

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

I'm writing a simple C program using fork() to create a binary tree of processes. I am able to get all the output I need (pid's of process, its parent, and its two children). Unfortunately, each forked process wants to print out the column headers. How do I make sure that the printf for the headers is executed only once?

# 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

There's some more code (error checking really), but my real problem is that the printf under the output banners section executes multiple times, giving output like this (but correctly aligned):

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

I've tried a few ideas (including those commented out under the banner section), but nothing seems to work and most "fixes" make the problem even worse!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

壹場煙雨 2024-10-25 09:54:51

首先,for 循环中的 if 的行为并不如您所愿。请记住,fork 后,它在父进程中返回子进程 PID,在子进程中返回 0。因此,在循环内部,第一个 fork 为父级中的 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 仅在换行符 (\n) 上刷新,IIRC。因此,在分叉之后,缓冲区中仍然有尚未刷新的内容,并且每个子进程都运行 printf("\n"); ,从而刷新缓冲区内容。

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

First, the if in the for-loop does not behave as you want it to. Remember that after the fork, it returns the child PID in the parent process and 0 in the child. So inside the loop, the first fork assigns a value to child_1_pid in the parent and continues to the second clause. The child does not enter the if but continues to the next for-loop iteration. The very same happens with the second clause. So only the main process should ever be able to enter the body of the if, but no child process. I wonder why the output suggests otherwise.

So to get your "binary tree", you should actually have this:

// 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;
}

The strange output of the banners has to do with flushing of streams. Normally, fprintf only flushed on newline (\n), IIRC. So there's still stuff in the buffer after the fork that has not been flushed yet, and each child runs printf("\n"); and thus flushes out the buffer content.

The solution is to either add a "\n" to the end of the very first printf, or call fflush(stdout); before the for loop.

盛夏尉蓝 2024-10-25 09:54:51

尽管我对这些东西有点生疏,但还是有一些可以尝试的东西。在打印横幅的行中:

printf("级别\tProcs\tParent\tChild1\tChild2\n编号\tID\tID\tID\tID");

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

Here's something to try, although I'm a little rusty with this stuff. In the line where you print out your banners:

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

It may be that everything after the \n is being left in the ouput buffer, so it's still there when each child is forked. Try adding another \n at the end of that printf, and removing the \n from the beginning of the printf inside the loop.

等待我真够勒 2024-10-25 09:54:51

替换

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

Replace:

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

With:

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

Replace:

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

With:

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

Remove:

printf("\n");
你的呼吸 2024-10-25 09:54:51

在这里阅读 2.5.1:

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

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

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

Read 2.5.1 here:

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

Note that after a fork(), two handles exist where one existed before. The application shall ensure that, if both handles can ever be accessed, they are both in a state where the other could become the active handle first. The application shall prepare for a fork() exactly as if it were a change of active handle. (If the only action performed by one of the processes is one of the exec functions or _exit() (not exit()), the handle is never accessed in that process.)

What this means is that, before calling fork you should call fflush on any streams that you intend to use in both processes after the fork.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文