使用 fork() 和 execlp() 在 C 中创建进程层次结构

Using fork() and execlp() to create process hierarchy in C

我必须使用 fork() 和 execlp() 来创建和注释给定的进程层次结构:

当每个进程都应该被分叉以反映这种层次结构时,我无法全神贯注,再加上不可协商的 execlp() 的使用,它取代了当前的进程映像。

这是我设法想出的(请原谅非常非 DRY 代码,我对这些概念不熟悉):

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

#define oops(m) {perror(m); exit(EXIT_FAILURE);}

int main() {
    pid_t pid1_1, pid1_2, pid1_1_1, pid1_1_2, pid1_2_1, pid1_2_2;

    pid1_1 = fork();

    if (pid1_1 < 0) {
        oops("Fork Failed!");
    }

    // child 1.1
    if (pid1_1 == 0) {
        printf("I am the child %d\n", getpid());
        if (execlp("./iam", "iam", "1.1", NULL) < 0)
        oops("Execlp Failed!");

    } else {
        // grandchild 1.1.1
        pid1_1_1 = fork();
        if (pid1_1_1 < 0) {
            oops("Fork Failed!");
        }
        if (pid1_1_1 == 0) {
            printf("I am the grandchild %d\n", getpid());
            if (execlp("./iam", "iam", "1.1.1", NULL) < 0)
            oops("Execlp Failed!");
        }
        //grandchild 1.1.2
        pid1_1_2 = fork();
        if (pid1_1_2 < 0) {
            oops("Fork Failed!");
        }
        if (pid1_1_2 == 0) {
            printf("I am the grandchild %d\n", getpid());
            if (execlp("./iam", "iam", "1.1.2", NULL) < 0)
            oops("Execlp Failed!");
        }
    }

    pid1_2 = fork();

    if (pid1_2 < 0) {
        oops("Fork Failed!");
    }
    // child 1.2
    if (pid1_2 == 0) {
        printf("I am the child %d\n", getpid());
        if (execlp("./iam", "iam", "1.2", NULL) < 0)
        oops("Execlp Failed!");
    } else {
        // grandchild 1.2.1
        pid1_2_1 = fork();
        if (pid1_2_1 < 0) {
            oops("Fork Failed!");
        }
        if (pid1_2_1 == 0) {
            printf("I am the grandchild %d\n", getpid());
            if (execlp("./iam", "iam", "1.2.1", NULL) < 0)
            oops("Execlp Failed!");
        }
        // grandchild 1.2.2
        pid1_2_2 = fork();
        if (pid1_2_2 < 0) {
            oops("Fork Failed!");
        }
        if (pid1_2_2 == 0) {
            printf("I am the grandchild %d\n", getpid());
            if (execlp("./iam", "iam", "1.2.2", NULL) < 0)
            oops("Execlp Failed!");
        }
    }

    // pid > 0 ==> must be parent
    printf("I am the parent %d\n", getpid());
    /* parent will wait for the child to complete */
    if (waitpid(-1, NULL, 0) < 0)
        printf("-1 from wait() with errno = %d\n", errno);

    printf("Child terminated; parent exiting\n");
    exit(EXIT_SUCCESS);
}

我的输出显示此层次结构设置不正确。例如,手动单步执行 gdb 并完成 1.2 的 PID 会终止整个进程树(此时应完整保留 1.1 子树)。

对于我在逻辑上复制此流程层次结构时哪里出错的任何建议,我们将不胜感激。谢谢!

Any suggestions for where I'm going wrong with logically replicating this process hierarchy would be really appreciated.

在程序开始时检查这部分代码:

pid1_1 = fork();

这将派生一个子进程。在此之后你正在做:

    if (pid1_1 == 0) {
        printf("I am the child %d\n", getpid());
        if (execlp("./iam", "iam", "1.1", NULL) < 0)
        ......

这意味着,现在子进程映像将被另一个进程映像替换。
根据您显示的图片,一个进程应该在调用 execlp() 之前分叉 2 个子进程,如果它是给定进程树中的父进程。您的代码的以下部分存在类似问题。

I cannot wrap my head around when each process should be forked in order to reflect this hierarchy, .....

仔细观察进程树,你会发现它是一棵完美的二叉树,每个内部节点都有 2 个子节点,所有叶节点都在同一级别。

也就是说,每个进程都应该创建 2 子进程,然后调用 execlp(),一旦达到给定的高度(在您的情况下为 2),没有子进程应该进一步分叉。

我将向您展示如何创建进程层次结构,您可以添加 execlp() 调用以用其他进程映像替换当前进程映像。

add to that the non-negotiable use of execlp() which replaces the current process image.

我相信,这里的当前进程是指子进程的分叉进程,这也包括最顶层的进程(相当于树中的根)。

要将进程层次结构创建为完美二叉树,您可以这样做:

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

int main (int argc, char *argv[]) {

    int height;

    if (argc != 2) {
        printf ("Invalid number of arguments, exiting..\n");
        exit (0);
    }

    height = atoi (argv[1]);

    if (height < 0) {
        printf ("Invalid input.\n"); // error handling can be better
        exit (0);
    }

    printf ("Parent process, my pid = %d, height = %d\n", getpid(), height);

    for (int i = 0; i < height; ++i) {
        printf ("\nMy pid : %d, current height of tree : %d, forking..\n", getpid(), i);

        pid_t pid = fork();

        if (pid == -1) { 
            printf ("Fork failed\n");
        } else if (pid == 0) {
            printf ("My pid = %d, [my parent : %d], I am child 1..\n", getpid(), getppid());

            // this sleep is for sequenced output, otherwise it's not needed
            // sleeping for 1 second 
            sleep (1);
            continue;
        }
   
        pid = fork();

        if (pid == -1) { 
            printf ("Fork failed\n");
        } else if (pid == 0) {
            printf ("My pid = %d, [my parent : %d], I am child 2..\n", getpid(), getppid());

            // this sleep is for sequenced output, otherwise it's not needed
            // sleeping for 1 second 
            sleep (1);
            continue;
        }

        // break the loop as the current process is done with forking 2 child process
        break;
    }

    // ADD execlp call here 

    // This part of code is to just show you the hierarchy.
    // If you add execlp call above then part is not needed.
    while (wait(NULL) > 0);
    printf ("pid %d : I am EXITING\n", getpid());

    // added sleep for sequenced output, otherwise it's not needed
    sleep (1);
    return 0;
}

用法:./a.out <height_of_process_tree>

输出:

# ./a.out 0            
Parent process, my pid = 50807, height = 0
pid 50807 : I am EXITING

# ./a.out 1
Parent process, my pid = 50808, height = 1

My pid : 50808, current height of tree : 0, forking..
My pid = 50809, [my parent : 50808], I am child 1..
My pid = 50810, [my parent : 50808], I am child 2..
pid 50810 : I am EXITING
pid 50809 : I am EXITING
pid 50808 : I am EXITING

# ./a.out 2
Parent process, my pid = 50811, height = 2

My pid : 50811, current height of tree : 0, forking..
My pid = 50812, [my parent : 50811], I am child 1..
My pid = 50813, [my parent : 50811], I am child 2..

My pid : 50812, current height of tree : 1, forking..

My pid : 50813, current height of tree : 1, forking..
My pid = 50814, [my parent : 50812], I am child 1..
My pid = 50815, [my parent : 50813], I am child 1..
My pid = 50816, [my parent : 50812], I am child 2..
My pid = 50817, [my parent : 50813], I am child 2..
pid 50814 : I am EXITING
pid 50815 : I am EXITING
pid 50816 : I am EXITING
pid 50817 : I am EXITING
pid 50812 : I am EXITING
pid 50813 : I am EXITING
pid 50811 : I am EXITING