【问题标题】:Trouble understanding fork() and process trees无法理解 fork() 和进程树
【发布时间】:2023-03-25 15:48:01
【问题描述】:

根据下面的代码,我一直在尝试制作一个进程树。起初我认为它类似于对称的二叉树,但不再这么认为。

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

int main() {
    int status;
    pid_t i;

    i = fork();
    wait(&status);
    printf("a; i = %d\n", i);

    i = fork();
    wait(&status);
    printf("b; i = %d\n", i);

    i = fork();
    wait(&status);
    printf("c; i = %d\n", i);

    i = fork();
    wait(&status);
    printf("d; i = %d\n", i);

    return 0;
}

此外,运行此代码时,输​​出量是我最初预测的 2 倍。例如,d 打印了 16 次,但我认为它只会打印 8 次。任何更深入的解释都会有所帮助。

【问题讨论】:

  • 你有 2**(number of fork()) 进程打印你的最后一行。这不清楚你问什么。
  • 是的,我想我只是对如何组织树感到困惑。比如,初始父节点是否有四个子节点从它分支出来,或者它是两个子节点,并且每次调用 fork() 时每个子节点都会再创建两个
  • 你应该知道fork()从父级复制几乎所有这包括输出缓冲,一个应该在fork()之前使用fflush(stdout),在你的孩子中使用等待也没有意义,所以你应该做if (i != 0) { wait(&amp;status); } 你还必须检查你的函数调用中的错误。
  • 为了帮助您理解,请在每个printf() 语句中包含PID 和PPID。另外,捕获并报告wait()每次调用时返回的值(死子PID)(当然也包括PID和PPID信息);您可以决定是否报告status,但将其初始化为零以防wait() 调用失败(因为没有孩子)。一般来说,在父级从wait() 调用返回之前,子级会继续。

标签: c process operating-system fork


【解决方案1】:

为了帮助您理解,请在每个 printf() 语句中包含 PID 和 PPID。另外,捕获并报告wait()每次调用时返回的值(死子PID);您可以决定是否报告status,但将其初始化为零以防(何时)wait() 调用失败,因为没有孩子。一般来说,在父级从 wait() 调用返回之前,子级会继续。

或者,更好的是,编写一个可以为您处理详细信息的日志记录函数。 例如,考虑以下代码:

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

static void print_info(const char *tag, int corpse, int status, int child)
{
    int pid = getpid();
    int ppid = getppid();
    printf("PID %5d, PPID %5d: %s (corpse: %5d, status 0x%.4X), child %5d\n",
            pid, ppid, tag, corpse, status, child);
}

int main(void)
{
    int pid = getpid();
    int ppid = getppid();
    printf("Initial PID %5d, PPID %5d:\n", pid, ppid);

    int status = 0;
    int child = fork();
    int corpse = wait(&status);
    print_info("a", corpse, status, child);

    child = fork();
    corpse = wait(&status);
    print_info("b", corpse, status, child);

    child = fork();
    corpse = wait(&status);
    print_info("c", corpse, status, child);

    child = fork();
    corpse = wait(&status);
    print_info("d", corpse, status, child);

    return 0;
}

一个示例运行给了我:

Initial PID  7357, PPID 20754:
PID  7358, PPID  7357: a (corpse:    -1, status 0x0000), child     0
PID  7359, PPID  7358: b (corpse:    -1, status 0x0000), child     0
PID  7360, PPID  7359: c (corpse:    -1, status 0x0000), child     0
PID  7361, PPID  7360: d (corpse:    -1, status 0x0000), child     0
PID  7360, PPID  7359: d (corpse:  7361, status 0x0000), child  7361
PID  7359, PPID  7358: c (corpse:  7360, status 0x0000), child  7360
PID  7362, PPID  7359: d (corpse:    -1, status 0x0000), child     0
PID  7359, PPID  7358: d (corpse:  7362, status 0x0000), child  7362
PID  7358, PPID  7357: b (corpse:  7359, status 0x0000), child  7359
PID  7363, PPID  7358: c (corpse:    -1, status 0x0000), child     0
PID  7364, PPID  7363: d (corpse:    -1, status 0x0000), child     0
PID  7363, PPID  7358: d (corpse:  7364, status 0x0000), child  7364
PID  7358, PPID  7357: c (corpse:  7363, status 0x0000), child  7363
PID  7365, PPID  7358: d (corpse:    -1, status 0x0000), child     0
PID  7358, PPID  7357: d (corpse:  7365, status 0x0000), child  7365
PID  7357, PPID 20754: a (corpse:  7358, status 0x0000), child  7358
PID  7366, PPID  7357: b (corpse:    -1, status 0x0000), child     0
PID  7367, PPID  7366: c (corpse:    -1, status 0x0000), child     0
PID  7368, PPID  7367: d (corpse:    -1, status 0x0000), child     0
PID  7367, PPID  7366: d (corpse:  7368, status 0x0000), child  7368
PID  7366, PPID  7357: c (corpse:  7367, status 0x0000), child  7367
PID  7369, PPID  7366: d (corpse:    -1, status 0x0000), child     0
PID  7366, PPID  7357: d (corpse:  7369, status 0x0000), child  7369
PID  7357, PPID 20754: b (corpse:  7366, status 0x0000), child  7366
PID  7370, PPID  7357: c (corpse:    -1, status 0x0000), child     0
PID  7371, PPID  7370: d (corpse:    -1, status 0x0000), child     0
PID  7370, PPID  7357: d (corpse:  7371, status 0x0000), child  7371
PID  7357, PPID 20754: c (corpse:  7370, status 0x0000), child  7370
PID  7372, PPID  7357: d (corpse:    -1, status 0x0000), child     0
PID  7357, PPID 20754: d (corpse:  7372, status 0x0000), child  7372

你可以追查数据,看到4个fork()调用有24个进程(每次调用之后,有2个进程,之前有1个进程,所以在1个之后调用有 2 个进程;在 2 次调用之后,有 4 个进程;依此类推),并且子进程在父进程继续之前退出(因此,在此示例中,PID 7357 是最后一个打印 a 标记的,和b 标签、c 标签和d 标签)。

当您在跟踪流程树时遇到困难时,请使用与此类似的打印技术来帮助您更好地了解正在发生的事情。

【讨论】:

  • @Stargateur:谢谢,但我仔细选择了int 作为类型,因为它简化了代码并适用于现存的 POSIX 系统——即使是像 AIX 这样的具有 8 位 PID 值的系统。
  • pid_t 不能在这些系统上工作?或者可能不符合 C99 标准?我认为除非问题涉及特定系统,否则答案应该使用尽可能“最新”的代码,这仍然只是我所说的一个小细节,你的答案,你的选择;)
  • 是的,pid_t 有效;但它无法打印。 printf() 中没有格式字母保证对 POSIX 定义的 pid_tuid_tgid_t(或 id_t)中的任何一个都是正确的。因此,要打印这些值,您必须使用已知格式说明符强制转换为某些已知类型。我选择使用已知类型 int(理论上它可能会与具有 16 位 int 类型和 32 位 pid_t 的系统相冲突,但这对我来说是可以接受的风险)。当然是 YMMV。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-07
  • 2012-04-29
  • 1970-01-01
  • 1970-01-01
  • 2015-04-27
  • 2020-02-11
相关资源
最近更新 更多