少于 1 分钟阅读 次阅读

概要:通过示例代码逐行解析 fork 系统调用:fork 创建子进程、父子进程分支执行,以及父进程通过 wait 等待子进程退出实现同步。

int pid;
pid = fork();
if(pid > 0){
    printf("parent: child=%d\n", pid);
    pid = wait();
    printf("child %d is done\n", pid);
} else if(pid == 0){
    printf("child: exiting\n");
    exit();
} else {
    printf("fork error\n");
}

代码逐行解析

  1. 变量声明

    int pid;
    

    声明一个整型变量 pid,用于存储 fork() 系统调用的返回值。

  2. 创建子进程

    pid = fork();
    

    fork() 是系统调用,它会创建一个当前进程的副本(子进程)。调用后,系统中的进程数+1。

    • 在父进程中fork() 返回新创建的子进程的进程ID(PID,一个大于0的正整数)
    • 在子进程中fork() 返回 0
    • 如果创建失败fork() 返回 -1
  3. 根据 fork() 的返回值分流执行 这是一个典型的三路分支,对应上述三种情况。

    • 父进程执行路径 (pid > 0)

      if(pid > 0){
          printf("parent: child=%d\n", pid); // 1. 打印子进程的PID
          pid = wait();                       // 2. 调用wait(),阻塞等待任意一个子进程结束
          printf("child %d is done\n", pid);  // 3. 子进程结束后,打印其PID
      }
      
      1. 父进程打印它刚创建的子进程的ID。
      2. wait() 系统调用使父进程挂起(阻塞),直到它的一个子进程终止(本例中就是它刚创建的那个)。wait() 返回被终止的子进程的PID,并回收其资源(防止成为“僵尸进程”)。
      3. 父进程被唤醒,打印已结束的子进程PID。
    • 子进程执行路径 (pid == 0)

      else if(pid == 0){
          printf("child: exiting\n"); // 子进程打印信息
          exit();                     // 子进程调用exit()终止自己
      }
      
      1. 子进程打印一条信息。
      2. exit() 系统调用立即终止当前进程(子进程)。子进程的所有资源会被内核释放,并向父进程发送一个“终止信号”。注意:标准C库中 exit() 通常需要一个状态参数,如 exit(0)
    • 错误处理路径 (pid < 0,通常是-1)

      else {
          printf("fork error\n"); // 打印创建失败的错误信息
      }
      

可能的输出示例

由于进程调度顺序不确定,父进程的 printf 和子进程的 printf 输出可能交错,但逻辑顺序是确定的。

一种典型的输出顺序是:

parent: child=12345
child: exiting
child 12345 is done
  • 第一行是父进程在 wait() 之前打印的。
  • 第二行是子进程打印的。
  • 第三行是子进程结束后,父进程从 wait() 返回后打印的。

另一种可能的输出顺序是:

child: exiting
parent: child=12345
child 12345 is done

留下评论