进程未按预期顺序执行; fork() 和 waitpid()

Processes not executing in expected order; fork() and waitpid()

我写了一个调用 fork() 两次的程序,生成了三个 children 和一个 parent。

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

int main() {
    int child1 = fork();
    int child2 = fork();

    // Check for error
    if (child1 < 0 || child2 < 0) {
        fprintf(stderr, "A fork failed :(\n");
        exit(1);
    }

    // Parent Process
    if (child1 > 0 && child2 > 0) {
        waitpid(child1, NULL, 0);
        waitpid(child2, NULL, 0);
        printf("I'm the parent\n");
    }
    // Child B -- CONFUSION AREA --
    else if (child1 > 0 && child2 == 0) {
        waitpid(child1, NULL, 0); // Not waiting for process to finish
        printf("I'm child B\n");
    }
    // Child A
    else if (child1 == 0 && child2 > 0) {
        waitpid(child2, NULL, 0);
        printf("I'm child A\n");
    }
    // Child C
    else if (child1 == 0 && child2 == 0) {            
        printf("I'm child C\n");
    }

    return 0;
}

我正在尝试打印出来

I'm child C
I'm child A
I'm child B
I'm the parent

但相反,程序打印出

I'm child B
I'm child C
I'm child A
I'm the parent

我很困惑为什么 child B 不等待 child1 进程完成?

我画了一个流程树试图理解发生了什么,我的理解是它看起来像:

/*

         Parent (15543)
         child1 = 15544;
         child2 = 15545
           /       \
          /         \
         /           \
        A (15544)     B (15545)
     child1 = 0;      child1 = 15544;
     child2 = 15546;  child2 = 0;
        /
       /
      /
     C (15546)
     child1 = 0;
     child2 = 0;


*/

当子进程 B 调用 waitpid() 时调用失败并且 returns -1 因为父进程已经在等待子进程 A,你不能两次获取子进程。

对于您想要的行为,最好使用进程间信号量来同步您的进程执行。尝试包含 semaphore.h 并查看 sem_init().

的文档

当您执行第一个 fork() 时,两个进程(parent 和 child)都会转到第二个 fork() 并生成您在树。这就是您在输出中看到 grandchild 的原因。你以为只有两个fork被执行,所以应该有两个children,但是你没有意识到第二个fork()实际上是由两个进程执行的:parent 和第一个 fork() 调用的 child

顺便说一句,内核以任意顺序安排进程,因此您可以预期输出行中的顺序不同。 Child B 不能 waitpid() 因为它没有启动任何 child (它是你树中的 child B,它有 parent 得到的 pid child A,但是 child A 不是它的 child,所以 waitpid() 给你一个错误,你不检查,所以打印完成)你假设这是一个 parent,因为其中一个 child 变量 > 0,但他继承了从其 parent 初始化的那个变量,因为它没有执行 fork() 调用。