#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);
}
我对这部分代码完全感到困惑。谁能解释我的工作原理? 另外,我也不知道是哪个过程(孩子/父母)打印。
答案 0 :(得分:1)
fork()
功能不常见;除非失败,否则它将返回两次,在两个不同(但密切相关)的过程中的每个过程中返回一次。如果失败,则返回-1
(在原始过程中,必不可少)。如果成功,它将在子进程中返回0
,而在原始(父)进程中返回子进程的PID,该PID永远不会为0
(或负数)。
您应该学习如何编写代码。在使用fork()
的代码的上下文中,通常有效的方法是打印PID(进程ID)和PPID(父进程ID),以帮助消除哪个进程打印什么内容的歧义。对于有关哪个进程将打印的问题的简短答案是,两个进程将打印来自第一个printf()
调用的数据,而四个进程将打印来自第二个printf()
调用的数据–除非您通过管道传输编程到另一个程序(例如cat
),在这种情况下,某些进程似乎多次打印数据。 (另请参见printf()
anomaly after 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);
}
main()
末尾的return语句返回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
$
在第一次运行中,初始(父)进程具有PID26226。它进行分叉,其子进程为26227。父进程被告知其子PID,因此它递增x
并递减{{1} };然后执行y
语句,其中格式字符串以printf()
开头,将值1:
打印为11,将x
打印为9。在此运行中,父进程再次派生创建它的第二个子节点使用PID26228。父节点再次递增y
并递减x
,并执行y
语句,其中格式字符串以printf()
开始,然后再进行其他操作。然后,它到达2:
循环并等待其子级之一死亡。
然后,第一个子进程(26227)执行wait()
语句,其中格式字符串以printf()
开头,但是1:
和x
的值均保持为10。然后到达第二个分支,并使用PID 26229创建自己的子进程(原始进程的孙进程)。
第二个子项26228具有(x,y)值(11,9),因为这些是分叉时的值,因此当执行格式字符串开头的y
语句时,它将打印这些值与printf()
。
在第一个子派生之后,它会被告知其孩子的PID,因此它将递增2:
并递减x
,并打印值11和9。
第二个孩子退出,其状态由原始进程报告。现在,孙子进程执行y
语句,其中格式字符串以printf()
pid 2:1. Since the value in
x was 0 twice, the values in
y`开头的值仍保持为10。然后退出。
第一个孩子可以报告其孩子退出,然后退出。父进程报告第一个孩子退出了,并且也退出了。
总体而言,有and
输出的一个副本,PID
的两个副本和1:
的4个副本(加上三个“退出子项”报告)。
第二次运行,输出通过管道传递到2:
,表明输出已完全缓冲,而不是行缓冲,因此进程在退出时刷新写入的数据,而不是在打印换行符时刷新。这就是为什么有4份介绍性cat
输出和4份PID = 26230
输出副本的原因。仍然只有3个“儿童退出”报告。
像这样打印PID信息,对于理解代码很有帮助。重要的是要认识到输出顺序不是固定的。仅仅由于调度算法以及计算机上同时发生的其他事情,不同的运行可能会产生不同的序列(与不同的PID编号完全不同)。
答案 1 :(得分:0)
这里
pid = fork();
fork()
通过复制调用过程来创建新过程,并且首先将子过程pid 返回给父级,因此
if (pid != 0) { } /* 2345 != 0 i.e parent process, lets assume pid returned is 2345 */
获取正确的结果,即父进程,然后将0
返回到子进程,因此下一步看起来像
if (pid != 0) { /* 0 != 0 .. child process */ }
在fork()的手册页中
返回值 成功后,子进程的 PID将在父进程中返回,然后 该孩子返回0 。如果失败,则在父级中返回-1, 没有创建子进程,并且已正确设置了errno。
另外,fork()
的返回类型应该是pid_t
而不是int
类型。正确的是
pid_t pid = fork();