进程中的 fork()、wait() 和 exit() 功能

moh*_*n_m 3 c unix operating-system fork wait

我有一个 C 源代码,如下所示。

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

int main(void) {

   pid_t process_id;
   int status;

   if (fork() == 0) 
   {
       if (fork() == 0)
       {
           printf("A");
       } else {
           process_id = wait(&status);
           printf("B");
       }
   } else {
       if (fork() == 0)
       {
           printf("C");
           exit(0);
       }
       printf("D");
   }
   printf("0");
   return 0;   
}
Run Code Online (Sandbox Code Playgroud)

当我在终端中执行它时,我在这张图片中出现了一些输出:

上面编写的源代码的一些输出

我实际上很困惑这些输出是如何生成的。例如, D0A0~$ B0C 是如何生成的。

谁能解释一下这些输出是如何生成的,以及这段代码中 exit(0) 的功能?

Ale*_*lex 8

一般来说,如果你有这样的代码

if (fork() == 0) {
  printf("I'm a child\n");
} else {
  printf("I'm a parent\n");
}

printf("This part is common\n");
Run Code Online (Sandbox Code Playgroud)

然后 fork() 结果为零的 if 分支将在子进程中执行,非零分支将在父进程中执行。之后在两个进程中继续执行(仍然是异步的),因此子进程和父进程都将在 if 之后执行代码。我们可以用下图来图形化地表示它,显示将在每个分支中执行的代码:

                                       fork()
                                      /    \
             ------- parent ----------      ---------- child -----------
             |                                                         |
             |                                                         |
   printf("I'm a parent\n");                          printf("I'm a child\n");
   printf("This part is common\n");                   printf("This part is common\n");
Run Code Online (Sandbox Code Playgroud)

现在,让我们为您的代码制作相同的图表。在第一个分叉之后,您根据最上面的情况拆分执行,如果:

                                    fork()
                                   /    \
         --------- parent ---------      ---------- child -------------
         |                                                            |
         |                                                            |

      if (fork() == 0)                                 if (fork() == 0)
      {                                                {
        printf("C");                                      printf("A");
        exit(0);                                       } else {
      }                                                   process_id = wait(&status);
      printf("D");                                        printf("B");
                                                       }

      // Common code                                  // Common code
      printf("0");                                    printf("0");                
      return 0;                                       return 0;
Run Code Online (Sandbox Code Playgroud)

在父子进程中执行下一个分叉后,我们将得到以下树结构:

                                    fork()
                                   /    \
                ----  parent ------      ------ child ------
                |                                          |
              fork()                                     fork()
              /    \                                     /    \
--- parent ---      --- child ---          --- parent ---      --- child ----
|                               |          |                                 |
|                               |          |                                 |
printf("D");           printf("C");      process_id = wait(&status);      printf("A");
                       exit(0);          printf("B");
                       printf("D");

printf("0");           printf("0");      printf("0");                     printf("0");
return 0;              return 0;         return 0;                        return 0;
Run Code Online (Sandbox Code Playgroud)

注意 printf("D"); 出现在 parent-parent 和 parent-child 分支中,因为它实际上是这两个分支中if(fork()==0){}.

此时,所有 4 个进程都在异步执行。

  • 父-父进程打印“D”,然后打印“0”然后退出

  • 父子进程打印“C”然后退出

  • 子父进程等待其子进程完成,然后打印“B”,然后打印“0”并退出

  • 子进程打印“A”,然后“0”然后退出

如您所见,这些进程的输出几乎可以任意交错,唯一的保证是在子父进程打印“B0”之前,子子进程将打印“A0”。用于运行程序的 shell 将在主进程(即父-父进程)完成后重新获得控制权。但是,当控制权返回到 shell 时,仍然可能有其他进程在运行,因此在 shell 输出其命令提示符后可能会出现某些进程的输出。例如,以下事件链是可能的:

  • 父-父获得控制权。它打印“D0”并退出,控制权返回给外壳。

  • 子父进程获得控制权。它开始等待(阻塞)子进程。

  • child-child 进程获得控制权。它打印“A0”并退出。

  • 同时shell进程获得控制权并打印命令提示符“~$”

  • 子父进程获得控制权。由于子进程完成,它被解除阻塞,打印“B0”并退出。

  • 父子进程获得控制权,打印“C”并退出。

组合输出为“D0A0~$B0C”。它解释了您示例中的最后一行。