c - 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);
}
我完全被这部分代码弄糊涂了。谁能解释一下它是如何工作的?我也无法理解打印哪个进程(子/父)。
解决方案
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 和y
9。在此运行中,父进程再次创建第二个 PID 为 26228 的子进程。父进程再次递增x
和递减y
,并执行printf()
格式字符串2:
在其他任何事情发生之前开始。然后它到达wait()
循环并等待它的一个孩子死亡。
然后第一个子进程 (26227) 执行printf()
格式字符串开始处的语句,但和1:
的值均未更改为 10。然后它到达第二个分叉并创建自己的子进程(原始进程的孙子进程),PID 为 26229。x
y
第二个孩子 26228 具有 (x, y) 值 (11, 9),因为这些是分叉时的值,因此它在执行printf()
格式字符串以 . 开头的语句时打印这些值2:
。
在第一个孩子分叉后,它被告知其孩子的 PID,因此它递增x
和递减y
,打印值 11 和 9。
第二个子进程退出,其状态由原始进程报告。孙子进程现在执行printf()
格式字符串以2:1. Since the value in
pid was 0 twice, the values in
x and
y` 开头的语句,在 10 处仍保持不变。然后退出。
第一个孩子可以报告它的孩子退出,然后自己退出。父进程报告第一个子进程退出并且也退出。
总体而言,有一份PID
输出副本、两份1:
和 4 份2:
(加上三个“子退出”报告)。
第二次运行,输出通过管道传输到cat
,显示输出是完全缓冲的,而不是行缓冲的,因此进程在退出时刷新写入的数据,而不是在打印换行符时刷新。这就是为什么有 4 个介绍性PID = 26230
输出副本和 4 个1:
输出副本的原因。仍然只有 3 个“孩子退出”报告。
像这样打印 PID 信息有助于理解代码。重要的是要认识到输出的顺序不是固定的。仅仅因为调度算法以及计算机上同时发生的其他事情,不同的运行可能会产生不同的序列(与不同的 PID 编号完全不同)。
推荐阅读
- json - 如何将两个列表从 JSON 转换为 Pandas DataFrame?
- c# - ASP.NET MVC 为 sql 表中的每个新 ID 创建一个新表
- jupyter-notebook - 为多个 ipywidgets 创建一个观察处理程序
- python - 损失函数中的 Keras batch_dot 维度问题
- javascript - 检测丢失的字符字形,以及 Firefox 的带有十六进制代码点的小盒子
- javascript - 在较小的显示器上使图像不以全分辨率加载
- python - 如何在 python (Pycharm) 中读取 Hjson 文件?
- python-3.x - 从熊猫添加数据时如何为谷歌工作表动态设置“范围”
- python - __future__ 语句如何知道新 Python 版本的语法?
- linux - Docker 端口映射是主机到容器还是容器到主机?