在此代码中fork和pid(if(pid!= 0))如何工作?

时间:2018-12-25 20:24:33

标签: c fork pid

#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);
}

我对这部分代码完全感到困惑。谁能解释我的工作原理? 另外,我也不知道是哪个过程(孩子/父母)打印。

2 个答案:

答案 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();