使用 fork() 的 C 程序的行为

Behavior of a C program using fork()

给定以下代码,我必须检查它的行为,即产生了多少进程。

#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char*argv[])
{
    int pid, i , j;
    pid_t t;
    for (i = 0; i < 2; i++) {
        pid = getpid();
        for (j = 0 ; j < i+2; j++){
            t = fork();
            if (t != 0) {
                t = fork();
                break;
            }
        }
        if (pid != getpid())
            break;
    }

    printf("I am the process %d and my father is the process %d\n", getpid(), getppid());

    while(wait(NULL)>0) {}

    return 0;
}

编译执行后输出结果如下:

I am the process 3749 and my father is the process 2254

I am the process 3750 and my father is the process 3749

I am the process 3751 and my father is the process 3749

I am the process 3752 and my father is the process 3749

I am the process 3754 and my father is the process 3749

I am the process 3753 and my father is the process 3750

I am the process 3755 and my father is the process 3750

I am the process 3756 and my father is the process 3752

I am the process 3757 and my father is the process 3752

I am the process 3758 and my father is the process 3756

I am the process 3759 and my father is the process 3756

这意味着相应的树进程如下所示:

                      2254
                       |
                   -- 3749 ----
                  /   |   \    \
                3750 3751 3752 3754
               /   \      /  \
            3753  3755  3756 3757
                       /   \
                     3758  3759

如果算上初始工序,一共产生了11道工序。

我预计会生成比 11 个更多的进程;无论如何,我能够理解除最远的叶子(3758 和 3759)之外的所有进程的生成。

程序中有两个for循环,一个嵌套在另一个循环中。由于每个循环内有两个 break 语句,我猜一个或两个循环应该在它们自然结束之前的某个时间点结束。

有人可以确认此代码的行为吗?

提前致谢

这些程序更多的是说明你了解分叉是如何工作的,希望你不要在多个嵌套循环中编写分叉程序。

他们跟踪的方式是在纸上模拟机器,但每次模拟货叉时,您都会复制模拟,将 fork() 的 return 代码设置为零, 并将另一个副本的 return 代码复制到另一个进程的 pid.

让我重新格式化您的程序以使其更容易

    int pid;
    int i;
    int j;
    pid_t t;

将保存我们的进程的“当前值”。我们还将用“IP ==>”

指示指令指针位置
    int pid;
    int i;
    int j;
    pid_t t;
    IP ===>
    for (i = 0; i < 2; i++) {
        pid = getpid();
        for (j = 0 ; j < i+2; j++){
            t = fork();
            if (t != 0) {
                t = fork();
                break;
            }
        }
        if (pid != getpid())
            break;
    }

下一步(PID 1)

    === PROCESS 1 ====
    int pid;
    int i; (set to 0)
    int j;
    pid_t t;

    for (i = 0; i < 2; i++) {
    IP ===>
        pid = getpid();
        for (j = 0 ; j < i+2; j++){
            t = fork();
            if (t != 0) {
                t = fork();
                break;
            }
        }
        if (pid != getpid())
            break;
    }

下一步(PID 1)

    === PROCESS 1 ====
    int pid (set to 1  // fake pid, but easy to count)
    int i; (set to 0)
    int j;
    pid_t t;

    for (i = 0; i < 2; i++) {
        pid = getpid();
    IP ===>
        for (j = 0 ; j < i+2; j++){
            t = fork();
            if (t != 0) {
                t = fork();
                break;
            }
        }
        if (pid != getpid())
            break;
    }

下一步(流程 1)

    === PROCESS 1 ====
    int pid (set to 1)
    int i; (set to 0)
    int j; (set to 0)
    pid_t t;

    for (i = 0; i < 2; i++) {
        pid = getpid();
        for (j = 0 ; j < i+2; j++){
    IP ===>
            t = fork();
            if (t != 0) {
                t = fork();
                break;
            }
        }
        if (pid != getpid())
            break;
    }

下一步(PID 1)

    === PROCESS 1 ====
    int pid (set to 1)
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 2, parent process)

    for (i = 0; i < 2; i++) {
        pid = getpid();
        for (j = 0 ; j < i+2; j++){
            t = fork();
    IP ===>
            if (t != 0) {
                t = fork();
                break;
            }
        }
        if (pid != getpid())
            break;
    }
    === PROCESS 2 ===
    int pid (set to 1)
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 0, child process)

    for (i = 0; i < 2; i++) {
        pid = getpid();
        for (j = 0 ; j < i+2; j++){
            t = fork();
    IP ===>
            if (t != 0) {
                t = fork();
                break;
            }
        }
        if (pid != getpid())
            break;
    }

下一步(过程 1)

    === PROCESS 1 ====
    int pid (set to 1)
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 2, parent process)

    for (i = 0; i < 2; i++) {
        pid = getpid();
        for (j = 0 ; j < i+2; j++){
            t = fork();
            if (t != 0) {  // t == 2 in this process, so enter the block
   IP ===>
                t = fork();
                break;
            }
        }
        if (pid != getpid())
            break;
    }
    === PROCESS 2 ===
    int pid (set to 1)
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 0, child process)

    for (i = 0; i < 2; i++) {
        pid = getpid();
        for (j = 0 ; j < i+2; j++){
            t = fork();
    IP ===>
            if (t != 0) {
                t = fork();
                break;
            }
        }
        if (pid != getpid())
            break;
    }

下一步(PID 1)

    === PROCESS 1 ====
    int pid (set to 1)
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 2, parent process)

    for (i = 0; i < 2; i++) {
        pid = getpid();
        for (j = 0 ; j < i+2; j++){
            t = fork();
    IP ===>
            if (t != 0) {
                t = fork();
                break;
            }
        }
        if (pid != getpid())
            break;
    }
    === PROCESS 2 ===
    int pid (set to 1)
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 0, child process)

    for (i = 0; i < 2; i++) {
        pid = getpid();
        for (j = 0 ; j < i+2; j++){
            t = fork();
    IP ===>
            if (t != 0) {
                t = fork();
                break;
            }
        }
        if (pid != getpid())
            break;
    }

下一步(过程 1)

    === PROCESS 1 ====
    int pid (set to 1)
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 2, parent process)

    for (i = 0; i < 2; i++) {
        pid = getpid();
        for (j = 0 ; j < i+2; j++){
            t = fork();
            if (t != 0) {  
   IP ===>
                t = fork();
                break;
            }
        }
        if (pid != getpid())
            break;
    }
    === PROCESS 2 ===
    int pid (set to 1)
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 0, child process)

    for (i = 0; i < 2; i++) {
        pid = getpid();
        for (j = 0 ; j < i+2; j++){
            t = fork();
    IP ===>
            if (t != 0) {
                t = fork();
                break;
            }
        }
        if (pid != getpid())
            break;
    }

下一步(过程 1)

    === PROCESS 1 ====
    int pid (set to 1)
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 3, parent process)

    for (i = 0; i < 2; i++) {
        pid = getpid();
        for (j = 0 ; j < i+2; j++){
            t = fork();
            if (t != 0) {  
                t = fork();
   IP ===>
                break;
            }
        }
        if (pid != getpid())
            break;
    }
    === PROCESS 2 ===
    int pid (set to 1)
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 0, child process)

    for (i = 0; i < 2; i++) {
        pid = getpid();
        for (j = 0 ; j < i+2; j++){
            t = fork();
    IP ===>
            if (t != 0) {
                t = fork();
                break;
            }
        }
        if (pid != getpid())
            break;
    }
    === PROCESS 3 ====
    int pid (set to 1)
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 0, child of PID 1)

    for (i = 0; i < 2; i++) {
        pid = getpid();
        for (j = 0 ; j < i+2; j++){
            t = fork();
            if (t != 0) {  
                t = fork();
   IP ===>
                break;
            }
        }
        if (pid != getpid())
            break;
    }

下一步(过程 1)

    === PROCESS 1 ====
    int pid (set to 1)
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 3, parent process)

    for (i = 0; i < 2; i++) {
        pid = getpid();
        for (j = 0 ; j < i+2; j++){
            t = fork();
            if (t != 0) {  
                t = fork();
                break;
            }
        }
    IP ===>
        if (pid != getpid())
            break;
    }
    === PROCESS 2 ===
    int pid (set to 1)
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 0, child process)

    for (i = 0; i < 2; i++) {
        pid = getpid();
        for (j = 0 ; j < i+2; j++){
            t = fork();
    IP ===>
            if (t != 0) {
                t = fork();
                break;
            }
        }
        if (pid != getpid())
            break;
    }
    === PROCESS 3 ====
    int pid (set to 1)
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 0, child of PID 1)

    for (i = 0; i < 2; i++) {
        pid = getpid();
        for (j = 0 ; j < i+2; j++){
            t = fork();
            if (t != 0) {  
                t = fork();
    IP ===>
                break;
            }
        }
        if (pid != getpid())
            break;
    }

下一步(过程 1)

    === PROCESS 1 ====
    int pid (set to 1)
    int pid = 1;
    int i; (set to 0)
    int j; (set to 1, due to j++)
    pid_t t; (set to 3, parent process)

    for (i = 0; i < 2; i++) {
        pid = getpid();
        for (j = 0 ; j < i+2; j++){
            t = fork();
            if (t != 0) {  // t == 2 in this process, so enter the block
                t = fork();
                break;
            }
        }
        if (pid != getpid())  // pid == 1, getpid == 1. so skip break
            break;
    IP ===>
    }
    === PROCESS 2 ===
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 0, child process)

    for (i = 0; i < 2; i++) {
        pid = getpid();
        for (j = 0 ; j < i+2; j++){
            t = fork();
    IP ===>
            if (t != 0) {
                t = fork();
                break;
            }
        }
        if (pid != getpid())
            break;
    }
    === PROCESS 3 ====
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 0, child of PID 1)

    for (i = 0; i < 2; i++) {
        pid = getpid();
        for (j = 0 ; j < i+2; j++){
            t = fork();
            if (t != 0) {  // t == 2 in this process, so enter the block
                t = fork();
    IP ===>
                break;
            }
        }
        if (pid != getpid())
            break;
    }

下一步(过程 1)

    === PROCESS 1 ====
    int pid (set to 1)
    int pid = 1;
    int i; (set to 1)
    int j; (set to 1, due to j++)
    pid_t t; (set to 3, parent process)

    for (i = 0; i < 2; IP ===> i++) {
        pid = getpid();
        for (j = 0 ; j < i+2; j++){
            t = fork();
            if (t != 0) {  // t == 2 in this process, so enter the block
                t = fork();
                break;
            }
        }
        if (pid != getpid())  // pid == 1, getpid == 1. so skip break
            break;

    }
    === PROCESS 2 ===
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 0, child process)

    for (i = 0; i < 2; i++) {
        pid = getpid();
        for (j = 0 ; j < i+2; j++){
            t = fork();
    IP ===>
            if (t != 0) {
                t = fork();
                break;
            }
        }
        if (pid != getpid())
            break;
    }
    === PROCESS 3 ====
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 0, child of PID 1)

    for (i = 0; i < 2; i++) {
        pid = getpid();
        for (j = 0 ; j < i+2; j++){
            t = fork();
            if (t != 0) {  // t == 2 in this process, so enter the block
                t = fork();
    IP ===>
                break;
            }
        }
        if (pid != getpid())
            break;
    }

下一步(过程 1)

    === PROCESS 1 ====
    int pid (set to 1)
    int pid = 1;
    int i; (set to 1)
    int j; (set to 1, due to j++)
    pid_t t; (set to 3, parent process)

    for (i = 0; IP ===> i < 2;  i++) { // 1 is less than 2 so enter loop block
        pid = getpid();
        for (j = 0 ; j < i+2; j++){
            t = fork();
            if (t != 0) {  // t == 2 in this process, so enter the block
                t = fork();
                break;
            }
        }
        if (pid != getpid())  // pid == 1, getpid == 1. so skip break
            break;

    }
    === PROCESS 2 ===
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 0, child process)

    for (i = 0; i < 2; i++) {
        pid = getpid();
        for (j = 0 ; j < i+2; j++){
            t = fork();
    IP ===>
            if (t != 0) {
                t = fork();
                break;
            }
        }
        if (pid != getpid())
            break;
    }
    === PROCESS 3 ====
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 0, child of PID 1)

    for (i = 0; i < 2; i++) {
        pid = getpid();
        for (j = 0 ; j < i+2; j++){
            t = fork();
            if (t != 0) {  // t == 2 in this process, so enter the block
                t = fork();
    IP ===>
                break;
            }
        }
        if (pid != getpid())
            break;
    }

下一步(过程 1)

    === PROCESS 1 ====
    int pid (set to 1)
    int pid = 1;
    int i; (set to 1)
    int j; (set to 1)
    pid_t t; (set to 3, parent process)

    for (i = 0; i < 2;  i++) {
    IP ===>
        pid = getpid();
        for (j = 0 ; j < i+2; j++){
            t = fork();
            if (t != 0) {  // t == 2 in this process, so enter the block
                t = fork();
                break;
            }
        }
        if (pid != getpid())  // pid == 1, getpid == 1. so skip break
            break;

    }
    === PROCESS 2 ===
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 0, child process)

    for (i = 0; i < 2; i++) {
        pid = getpid();
        for (j = 0 ; j < i+2; j++){
            t = fork();
    IP ===>
            if (t != 0) {
                t = fork();
                break;
            }
        }
        if (pid != getpid())
            break;
    }
    === PROCESS 3 ====
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 0, child of PID 1)

    for (i = 0; i < 2; i++) {
        pid = getpid();
        for (j = 0 ; j < i+2; j++){
            t = fork();
            if (t != 0) {  // t == 2 in this process, so enter the block
                t = fork();
    IP ===>
                break;
            }
        }
        if (pid != getpid())
            break;
    }

下一步(过程 1)

    === PROCESS 1 ====
    int pid (set to 1, again)
    int pid = 1;
    int i; (set to 1)
    int j; (set to 1)
    pid_t t; (set to 3)

    for (i = 0; i < 2;  i++) {
        pid = getpid();
    IP ===>
        for (j = 0 ; j < i+2; j++){
            t = fork();
            if (t != 0) {  // t == 2 in this process, so enter the block
                t = fork();
                break;
            }
        }
        if (pid != getpid())  // pid == 1, getpid == 1. so skip break
            break;

    }
    === PROCESS 2 ===
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 0, child process)

    for (i = 0; i < 2; i++) {
        pid = getpid();
        for (j = 0 ; j < i+2; j++){
            t = fork();
    IP ===>
            if (t != 0) {
                t = fork();
                break;
            }
        }
        if (pid != getpid())
            break;
    }
    === PROCESS 3 ====
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 0, child of PID 1)

    for (i = 0; i < 2; i++) {
        pid = getpid();
        for (j = 0 ; j < i+2; j++){
            t = fork();
            if (t != 0) {  // t == 2 in this process, so enter the block
                t = fork();
    IP ===>
                break;
            }
        }
        if (pid != getpid())
            break;
    }

下一步(过程 1)

    === PROCESS 1 ====
    int pid (set to 1)
    int pid = 1;
    int i; (set to 1)
    int j; (set to 0)
    pid_t t; (set to 3)

    for (i = 0; i < 2;  i++) {
        pid = getpid();
        for (j = 0 ; j < i+2; j++){
    IP ===>
            t = fork();
            if (t != 0) {  // t == 2 in this process, so enter the block
                t = fork();
                break;
            }
        }
        if (pid != getpid())  // pid == 1, getpid == 1. so skip break
            break;

    }
    === PROCESS 2 ===
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 0, child process)

    for (i = 0; i < 2; i++) {
        pid = getpid();
        for (j = 0 ; j < i+2; j++){
            t = fork();
    IP ===>
            if (t != 0) {
                t = fork();
                break;
            }
        }
        if (pid != getpid())
            break;
    }
    === PROCESS 3 ====
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 0, child of PID 1)

    for (i = 0; i < 2; i++) {
        pid = getpid();
        for (j = 0 ; j < i+2; j++){
            t = fork();
            if (t != 0) {  // t == 2 in this process, so enter the block
                t = fork();
    IP ===>
                break;
            }
        }
        if (pid != getpid())
            break;
    }

等等。一旦您使流程 1 脱离循环(并等待其 children 完成),您将对流程 2 执行相同的过程,直到它脱离循环并等待其 children 完成。

这个练习的重点是你是否可以模拟一些令人困惑的过程代码。很明显,由于顶层进程将循环两次,并且永远不会跳出循环,因此您将在其下获得 4 个进程。当您练习单步执行其他过程时,您将开始看到它们实现的模式。

就个人而言,这类问题需要大量的工作,而不是那种让你成为更好的程序员的工作;但是,它会证明你是否理解一个程序是用它的当前状态分叉的(注意 pid 在许多 children 中是错误的,因为它在分叉后没有更新),如果你完全理解, 你可以模拟程序到它的最终完成。

当然,我们使用计算机来自动处理无聊的事情(像这样),所以你可能想像这样想出一个新的日志行:

 void printState(int me, int pid, int i, int j, int t) {
     printf("PID %d: pid = %d, i = %d, j = %d, t = %d\n");
 }

然后是运行

   printState(getpid(), pid, i, j, t);
   for (i = 0; i < 2; i++) {
        printState(getpid(), pid, i, j, t);
        pid = getpid();
        printState(getpid(), pid, i, j, t);
        for (j = 0 ; j < i+2; j++){
            printState(getpid(), pid, i, j, t);
            t = fork();
            printState(getpid(), pid, i, j, t);
            if (t != 0) {
                printState(getpid(), pid, i, j, t);
                t = fork();
                printState(getpid(), pid, i, j, t);
                break;
            }
        }
        if (pid != getpid())
            printState(getpid(), pid, i, j, t);
            break;
    }

并收集输出,按它们的 self-reported pid 编号(getpid(),而不是 pid 变量)对它们进行排序

这是基于@Edwin Buck(谢谢Edwin!)解释的程序模拟的进程创建过程

                    1
                    |
               ---- 2 -----
              /   /   \    \
             3   4     5    6
           /   \      /  \
          7     8    9   10
                   /   \
                  11   12

进程 #1:初始进程的父进程。

流程#2:初始流程。这是唯一满足 pid = getpid() 的进程。

进程 #3:生成于 i = 0,j = 0。

进程 #4:生成于 i = 0,j = 0。

进程 #7:生成于 i = 0,j = 1。

进程 #8:生成于 i = 0,j = 1。

进程 #5:生成于 i = 1,j = 0。

进程 #6:生成于 i = 1,j = 0。

进程 #9:生成于 i = 1,j = 1。

进程 #10:生成于 i = 1,j = 1。

进程 #11:生成于 i = 1,j = 2。

进程 #12:生成于 i = 1,j = 2。