首页 > 解决方案 > fork 和 pid (if (pid != 0)) 如何在这段代码中工作?

问题描述

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
    int pid;
    int x, y;

    x = 10;
    y = 10;
    pid = fork();

    if (pid != 0)
    {
       x++;
       y--;
    }

    printf("x = %i y = %i\n", x, y);

    pid = fork();

    if (pid != 0)
    {
       x++;
       y--;
    }
    printf("x = %i y = %i\n", x, y);

    return (0);
}

我完全被这部分代码弄糊涂了。谁能解释一下它是如何工作的?我也无法理解打印哪个进程(子/父)。

标签: cforkpid

解决方案


fork()功能异常;它返回两次,在两个不同(但密切相关)的进程中的每一个中返回一次,除非它失败。-1如果失败(在原始过程中,必然),它会返回。如果成功,则0在子进程中返回,而在原始(父)进程中返回子进程的PID,从不0(或为负)。

您应该学习检测代码。在使用 的代码的上下文中fork(),打印 PID(进程 ID)和 PPID(父进程 ID)通常是有效的,以帮助区分哪个进程打印什么。关于打印哪个进程的问题的简短回答是,两个进程打印第一次printf()调用的数据,四个进程打印第二次printf()调用的数据——除非您将程序的输出通过管道传输到另一个(例如cat),其中在某些情况下,某些进程似乎多次打印数据。(参见printf()后面的异常fork()。)

让我们检测您的代码:

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

int main(void)
{
    printf("PID = %d\n", (int)getpid());

    int x = 10;
    int y = 10;

    int pid = fork();
    if (pid != 0)
    {
       x++;
       y--;
    }
    printf("1: x = %i y = %i (PID %d, PPID %d)\n",
           x, y, (int)getpid(), (int)getppid());

    pid = fork();
    if (pid != 0)
    {
       x++;
       y--;
    }
    printf("2: x = %i y = %i (PID %d, PPID %d)\n",
           x, y, (int)getpid(), (int)getppid());

    int status;
    int corpse;
    while ((corpse = wait(&status)) > 0)
    {
        printf("%d: child %d exited with status 0x%.4X\n",
               (int)getpid(), corpse, status);
    }

    return (getpid() % 16);
}

最后的 return 语句在main()16 次中返回非零退出状态 15 次,只是为了让事情变得更有趣。

示例运行(./fork43从 构建的程序fork43.c)——一个没有管道,一个有管道:

$ ./fork43
PID = 26226
1: x = 11 y = 9 (PID 26226, PPID 23612)
2: x = 12 y = 8 (PID 26226, PPID 23612)
1: x = 10 y = 10 (PID 26227, PPID 26226)
2: x = 11 y = 9 (PID 26228, PPID 26226)
2: x = 11 y = 9 (PID 26227, PPID 26226)
26226: child 26228 exited with status 0x0400
2: x = 10 y = 10 (PID 26229, PPID 26227)
26227: child 26229 exited with status 0x0500
26226: child 26227 exited with status 0x0300
$ ./fork43 | cat
PID = 26230
1: x = 11 y = 9 (PID 26230, PPID 23612)
2: x = 11 y = 9 (PID 26233, PPID 26230)
PID = 26230
1: x = 10 y = 10 (PID 26232, PPID 26230)
2: x = 10 y = 10 (PID 26234, PPID 26232)
PID = 26230
1: x = 10 y = 10 (PID 26232, PPID 26230)
2: x = 11 y = 9 (PID 26232, PPID 26230)
26232: child 26234 exited with status 0x0A00
PID = 26230
1: x = 11 y = 9 (PID 26230, PPID 23612)
2: x = 12 y = 8 (PID 26230, PPID 23612)
26230: child 26233 exited with status 0x0900
26230: child 26232 exited with status 0x0800
$

在第一次运行中,初始(父)进程的 PID 为 26226。它分叉,其子进程为 26227。父进程被告知其子 PID,因此它递增x和递减y;然后它执行printf()格式字符串开始处的语句,将1:值打印x为 11 和y9。在此运行中,父进程再次创建第二个 PID 为 26228 的子进程。父进程再次递增x和递减y,并执行printf()格式字符串2:在其他任何事情发生之前开始。然后它到达wait()循环并等待它的一个孩子死亡。

然后第一个子进程 (26227) 执行printf()格式字符串开始处的语句,但和1:的值均未更改为 10。然后它到达第二个分叉并创建自己的子进程(原始进程的孙子进程),PID 为 26229。xy

第二个孩子 26228 具有 (x, y) 值 (11, 9),因为这些是分叉时的值,因此它在执行printf()格式字符串以 . 开头的语句时打印这些值2:

在第一个孩子分叉后,它被告知其孩子的 PID,因此它递增x和递减y,打印值 11 和 9。

第二个子进程退出,其状态由原始进程报告。孙子进程现在执行printf()格式字符串以2:1. Since the value inpid was 0 twice, the values inx andy` 开头的语句,在 10 处仍保持不变。然后退出。

第一个孩子可以报告它的孩子退出,然后自己退出。父进程报告第一个子进程退出并且也退出。

总体而言,有一份PID输出副本、两份1:和 4 份2:(加上三个“子退出”报告)。

第二次运行,输出通过管道传输到cat,显示输出是完全缓冲的,而不是行缓冲的,因此进程在退出时刷新写入的数据,而不是在打印换行符时刷新。这就是为什么有 4 个介绍性PID = 26230输出副本和 4 个1:输出副本的原因。仍然只有 3 个“孩子退出”报告。

像这样打印 PID 信息有助于理解代码。重要的是要认识到输出的顺序不是固定的。仅仅因为调度算法以及计算机上同时发生的其他事情,不同的运行可能会产生不同的序列(与不同的 PID 编号完全不同)。


推荐阅读