【问题标题】:Behavior of a C program using fork()使用 fork() 的 C 程序的行为
【发布时间】:2022-01-11 04:22:14
【问题描述】:

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

#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 语句,我猜想一个或两个循环应该在其自然结束之前的某个时间点结束。

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

提前谢谢你

【问题讨论】:

  • 我在您出色的流程树形图中看到了 12 个流程。你的意思是 12 而不是 11?
  • 是的@Edwin Buck 我的意思是12个进程,如果我们算上初始进程的父亲的话。谢谢

标签: c process fork


【解决方案1】:

这类程序更多是为了说明你理解分叉是如何工作的,我希望你永远不要在多个嵌套循环中编写一个分叉程序,并带有中断。

他们跟踪这一点的方法是在纸上模拟机器,但每次模拟分叉时,你都会复制模拟,将一个副本的 fork() 的返回码设置为零,另一个的返回码复制到其他进程的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 从其循环中取出(并等待其子进程完成),您对进程 2 执行相同的过程,直到它脱离循环并等待其子进程完成。

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

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

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

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

并收集输出,按照他们自己报告的 pid 编号(getpid(),而不是 pid 变量)对它们进行排序

【讨论】:

  • 非常感谢@Edwin Buck 的精彩回答。
  • 正如我所说,我了解大部分进程创建,但我仍然看不到创建的两个最外层进程或叶子的意义(我的进程树中的#3758 和#3759)。内部循环应用了两次 fork(),第一次是通用的 fork(),第二次仅用于父进程。最外面的两个叶子是根据内部循环模式创建的,但为什么其余的孙进程也不产生叶子呢?谢谢
  • @The_Student 理解 fork 的工作原理和模拟上面的过程是有区别的。只有正确模拟上述过程才能充分解释为什么要创建这些最后的过程。我想您模拟了其中的一些,但不是全部,并且您正在做出涵盖许多(如果不是大多数)过程的逻辑猜测。请提供除“显示”以外的更多详细信息,我可以帮助检查错误(或至少更好地理解您的问题)。
  • 完全是@Edwin Buck,只有跟踪每个过程才能弄清楚整个事情。我已经制作了一个包含所有 i 和 j 的进程树,是的,产生了 11 个进程,两个最外部的叶子由 i = 1 和 j = 2 生成(它们是唯一满足该条件的进程)。您的回答对于帮助我模拟整个代码具有决定性意义。再次非常感谢你。问候
  • 我会尽快发布提到的流程树。
【解决方案2】:

这是基于@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 处产生。

【讨论】:

    猜你喜欢
    • 2020-09-03
    • 1970-01-01
    • 1970-01-01
    • 2017-09-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-21
    相关资源
    最近更新 更多