【问题标题】:What is fork() doing here?fork() 在这里做什么?
【发布时间】:2022-01-16 14:39:11
【问题描述】:

所以我想弄清楚这段代码在做什么:

int k = 5;
 if(fork() == fork()) ++k; else --k;
 printf("%d\n", k);

困扰我的是我收到不同的输出:

4
4
4
6
4
4
4
4
4
6
4
4
4
6
4
6
4
4

我添加了一个 PID 检测器,所以我可以看到所有进程的 ID:

int main ()
{
   int k = 5;
    if(fork() == fork()) ++k; else --k;
    printf("%d\n", k);
    int pid;
    pid = fork();
    if(pid == 0)
    {
         printf("\n Process id : %d",getpid());
         printf("\n Parrent process id : %d\n" ,getppid());
    }

    return 0;
}

但有时我会得到输出:

Process id : 9472
 Parrent process id : 1413

 Process id : 9474
 Parrent process id : 1413

 Process id : 9471
 Parrent process id : 1413

 Process id : 9473
 Parrent process id : 1413

或:

 Process id : 9557
 Parrent process id : 1413

 Process id : 9556
 Parrent process id : 1413

 Process id : 9558
 Parrent process id : 9554

 Process id : 9559
 Parrent process id : 9553

请让我明白这里发生了什么。

【问题讨论】:

  • Re “困扰我的是我收到不同的输出:”:操作系统不保证进程执行的顺序。因此,来自各种子进程和父进程的输出以不同的顺序出现。而且,由于您的程序不会等待子进程完成,因此有时父进程会在其子进程打印之前退出,然后子进程会重新连接到替代原始父进程的另一个进程。
  • 如果你do like this,也许更容易看到发生了什么
  • 那里有些可疑之处,@EricPostpischil。如果进程A 的父进程在A 本身终止之前终止,那么A 应该由进程1 继承,而不是其他进程。
  • 您的“PID 检测器”fork()s 再次,然后报告(仅)来自这些额外分叉的子进程的 PID。以这种方式报告 PPID 的进程都不是早先打印值 k 的进程——它们是这些进程的子进程。这似乎是一件奇怪的事情,我不确定你认为它在告诉你什么。
  • @TedLyngmo 非常感谢,我想我现在明白了一点!

标签: c linux process fork pid


【解决方案1】:

我为孩子们将您的原始代码扩展为wait,这样您就可以确保获得所有输出并添加更多输出。

你有两个forks,所以总共会有四个进程。

只有在这些进程之一中,fork() == fork() 才会成为 true。这是当这两个fork()s 中的第一个被调用返回到子进程时,下一个fork() 被调用并且也返回到子进程。

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

const char *foo(pid_t a, pid_t b) {
    if (a == b) return "child,child";
    if (a && b) return "parent,parent";
    if (a) return "parent,child";
    return "child,parent";
}

int main() {
    int k = 5;
    pid_t p1, p2;

    if ((p1 = fork()) == (p2 = fork())) {
        ++k;
    } else {
        --k;
    }

    printf("%d %s\tppid=%d  pid=%d\n", k, foo(p1, p2), getppid(), getpid());

    int wstatus;
    if (p1) wait(&wstatus);
    if (p2) wait(&wstatus);
}

示例输出。请注意,由于所有进程同时运行,因此顺序可能会发生变化。

6 child,child   ppid=2  pid=4
4 parent,child  ppid=1  pid=3
4 child,parent  ppid=1  pid=2
4 parent,parent ppid=0  pid=1

这里的第一个进程1 创建23,在创建2 的情况下我们得到fork() == fork()

    1
   / \
  2   3
 /
4

【讨论】:

  • 非常感谢@TedLyngmo,但我不确定我是否理解 ppid=0 是谁,初始过程是 1 不是吗?那么谁是0?
  • @Jorj2014 不客气!我在Compiler Explorer 中运行代码,在该容器中,0 是“系统”进程,1 是您运行的程序的第一个进程。
【解决方案2】:

困扰我的是我收到不同的输出:

代码无条件呈现fork()s 两次。在第一个fork 成功后,原来有两个进程。 两个都执行第二个fork,所以,假设所有fork调用成功,最终会有四个进程。

成功时,fork() 将子 PID 返回给原始进程,将 0 返回给子进程。失败时,它返回 -1 给父级(并且没有子级)。如果事实上两个forks 都成功了,那么所有生成的进程的PID 都会不同,所以fork() == fork() 将评估为true 的唯一情况是在第一个孩子的孩子中,其中两个fork() 调用都会有返回 0。仅在该过程中,k 将递增;在其他进程中会递减。

进程不共享内存,因此增量和减量分别反映在各个进程的k 的单独副本中。由于没有对各种进程的运行顺序进行控制,原则上它们可以以任何顺序打印它们的值k。这涵盖了三个观察到的输出。

但是对于只打印三个k 值的情况呢?最可能的解释是第一个fork() 成功,但在其中一个结果过程中第二个失败,返回-1。如果在原始进程中发生fork() 失败,那么第二个fork() 的成功实例仍将产生一个进程,其中fork() == fork() 评估为真。另一方面,如果fork 失败发生在第一个孩子中,那么fork() == fork() 中的任何进程都不会评估为真。这将解释两个三输出结果。


就 PID 和 PPID 而言,似乎让您感到困惑的一件事是某些进程的父进程终止的影响。除了初始进程之外的进程总是有一个活进程作为其父进程。如果进程P 的原始父进程在P 本身终止之前终止,则分配不同的父进程; 在符合 POSIX 的系统上,这将是进程 1

由于您的程序没有做任何事情来确保父进程比其子进程更长寿,因此您有时会看到一些原始父进程在其子进程之前终止的影响。这解释了 PPID 模式的变化。但是,您似乎在一个环境(可能是某种容器)中运行您的程序,该环境(可能是某种容器)在新父级的分配细节方面不符合 POSIX,这在某种程度上掩盖了该问题的根源。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-01-28
    • 2012-03-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多