如何在C中创建自定义多个进程?

塞尔吉奥

我想请教各位C 编程方面的帮助。基本上我有fork()系统调用问题这是我的问题:我们有一个管理器进程,它必须创建POP_SIZE学生进程。在创建所有学生进程之前,经理进程和学生进程本身不能做任何其他事情。每个 Student Process 都通过以下方式标识:1) 其标识号(6 位整数) 2)在特定考试中获得的成绩(整数)

这是我设法编写的代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#define POP_SIZE 10

int main(int argc, char *argv[]){
pid_t firstFork;
int *status;
int numStudents = 0;
pid_t managerChild, managerParent;
pid_t students[POP_SIZE];
int studentStatus[POP_SIZE];


switch(firstFork = fork()){
    case -1:
        perror("Something wrong with fork()\n");
        break;
    case 0:
        managerChild = getpid();
        printf("Manager Child Process %d started\n", managerChild);
        printf("I have to create %d Student Processes\n", POP_SIZE);
        for(int i = 0; i < POP_SIZE; i++){
            switch(students[i] = fork()){
                case -1:
                    perror("Something wrong with FORK in Manager Child Process\n");
                    break;
                case 0:
                    printf("Created first Student Process PID: %d\n", getpid());
                    numStudents++;
                    break;
                default:
                    printf("Haven't created all Student Processes\n");
                    waitpid(managerChild, status, WUNTRACED | WNOHANG);
                    printf("%d Student Processes succesfully created\n", numStudents);
                    break;
            }
        }
        break;
    default:
        for(int i = 0; i < POP_SIZE; i++)
            wait(NULL);
}

}

我需要一些帮助来理解在我的代码中放置的位置wait(*status)waitpid(pid, *status, __options)函数以实现我上面指定的要求?此外,如何为每个进程分配和保持变量的存储?非常感谢你

名义动物

由于您将创建许多子进程,因此最好首先创建一个创建子进程的函数,并让它执行调用者指定的函数。让我们假设 ID 号和等级都是ints。然后,

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

/* Run func(id, grade) in a child process.
   Returns the child process PID if success,
   or -1 with errno set in case an error occurs.
*/
pid_t run_child(int id, int grade,
                int (*func)(int id, int grade))
{
    pid_t  p;

    p = fork();
    if (p == -1) {
        /* fork() failed; it set errno to indicate the error. */
        return -1;
    } else
    if (!p) {
        /* Run child process function. When it returns,
           have the child exit with that exit status. */
        exit(func(id, grade));
    } else {
        /* Parent process. p is positive. */
        return p;
    }
}

注意第三个参数是一个函数指针。我们使用函数名来指定它。该函数必须采用两个int参数(分别为 ID 和等级),并返回一个int. 例如:

/* Each child process runs this function.
*/
int child_process(int id, int grade)
{
    printf("Child: id = %d, grade = %d, PID = %d.\n", id, grade, (int)getpid());
    return EXIT_SUCCESS;
}

我们可以创建一个运行该函数的子进程child_pid = run_child(123456, 5, child_process);请注意如何使用函数名称来指定函数指针。标准 Cqsort()函数使用完全相同的机制来允许快速排序任何内容;调用者只需要指定一个函数,该函数可以比较要排序的数组中的两个元素。

我们将创造几个孩子,并同时收获他们。这意味着编写一个获取所有子进程的函数是有意义的,基本上是阻塞直到它们都退出。我们可能对其中至少一些的退出状态感兴趣,所以让我们将有趣的子进程 PID、保存状态的整数以及这些数组中的进程数作为参数传递:

/* Reap all child processes.
   If child_count > 0, child processes with PID in child_pid[]
   will have child_pid[] negated when reaped, with exit status saved
   in child_status.
   The function returns the number of child processes reaped.
*/
size_t reap_children(pid_t *child_pid, int *child_status, size_t child_count)
{
    size_t  reaped = 0;
    size_t  i;
    int     status;
    pid_t   p;

    while (1) {

        /* Reap a child process, if any. */
        p = wait(&status);
        if (p == -1) {
            /* errno == EINTR is not an error; it occurs when a
               signal is delivered to a hander installed without
               SA_RESTART flag.  This will not occur in this program,
               but it is good practice to handle that case gracefully. */
            if (errno == EINTR)
                continue;

            /* errno set by wait(). */
            return reaped;
        }

        /* Another child process was reaped. */
        reaped++;

        /* If the reaped child was one of the interesting ones,
           negate its pid and save the exit status. */
        for (i = 0; i < child_count; i++) {
            if (child_pid[i] == p) {
                child_pid[i] = -p;
                child_status[i] = status;
                break;
            }
        }
    }
}

请注意,p = wait(&status)收割子进程。这意味着如果一个或多个子进程已经退出,它会选择其中一个,并返回其 PID,退出状态保存到&status. 如果剩下的所有子进程仍在运行,则调用将等到其中至少一个退出。如果没有更多的子进程,它返回-1errno设置为ECHILD

如果使用了信号处理程序,wait()也可以-1使用errnoset to返回EINTR,如果信号被传递到安装时没有带SA_RESTART标志的信号处理程序sigaction()许多程序员放弃了这个检查(因为“它永远不会发生”),但我确实喜欢包含那个检查,因为它很容易,并且确保在我的代码中添加信号处理不会在以后咬我。我也经常这样做。(我的意思是添加信号处理。)

当相应的子进程被收割时,我们否定 pid 的原因很简单:它允许我们轻松检测哪些子进程被收割。(POSIX 表示所有进程 ID 都是正数,并且pid_t是有符号类型。否定 PID 也是一种常用技术;请参见例如waitpid()。)

如果我们想获得一个特定的子进程,我们会使用waitpid(). 例如,

    pid_t  child, p; /* wait for 'child'. */
    int    status;

    do {
        p = waitpid(child, &status, 0);
        if (p == -1) {
            if (errno == EINTR)
                continue;
            break;
        }
    } while (p != child);
    if (p == child) {
        /* Reaped 'child', status in 'status'. */
    } else {
        /* Error: failed to reap 'child'. See 'strerror(errno)'. */
    }

请注意,在 POSIX/Unix 术语中,“子进程”仅指由该进程创建的进程;不是“孙子”,由子进程创建的进程。

我更喜欢编写我的进程来从命令行接收参数。如果没有参数被指定,或-h--help指定,将显示一个简短的帮助(“用法”); 这在 POSIX 和 Unix 命令行工具中极为常见,因此非常直观。

以下main()将一个或多个ID:grade作为命令行参数。对于每一个,它创建一个子进程,并让它运行child_process()具有指定 ID 和等级函数。主程序然后将它们全部收割,并描述每个子进程的退出状态。

int main(int argc, char *argv[])
{
    pid_t  child_pid[argc];
    int    child_status[argc];
    int    count, i, n, arg, id, grade, status;
    char   dummy;

    if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
        fprintf(stderr, "\n");
        fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
        fprintf(stderr, "       %s ID:GRADE [ ID:GRADE ]*\n", argv[0]);
        fprintf(stderr, "\n");
        return EXIT_SUCCESS;
    }

    status = EXIT_SUCCESS;
    count = 0;

    for (arg = 1; arg < argc; arg++) {
        if (sscanf(argv[arg], "%d:%d %c", &id, &grade, &dummy) == 2) {
            child_pid[count] = run_child(id, grade, child_process);
            if (child_pid[count] == -1) {
                fprintf(stderr, "Cannot fork a child process: %s.\n", strerror(errno));
                status = EXIT_FAILURE;
            } else
                count++;
        } else {
            fprintf(stderr, "%s: Not a valid ID:GRADE specification.\n", argv[arg]);
            status = EXIT_FAILURE;
        }
    }

    if (count < 0) {
        fprintf(stderr, "No running child processes.\n");
        return EXIT_FAILURE;
    }

    n = reap_children(child_pid, child_status, count);
    printf("Reaped %d child processes.\n", n);

    for (i = 0; i < count; i++) {
        if (child_pid[i] < 0) {
            printf("Child process %d (%d of %d)", (int)(-child_pid[i]), i + 1, count);

            if (WIFEXITED(child_status[i])) {                   
                if (WEXITSTATUS(child_status[i]) == EXIT_SUCCESS)
                    printf(" exited with success (EXIT_SUCCESS), %d.\n", EXIT_SUCCESS);
                else
                if (WEXITSTATUS(child_status[i]) == EXIT_FAILURE)
                    printf(" exited with failure (EXIT_FAILURE), %d.\n", EXIT_FAILURE);
                else
                    printf(" exited with status %d.\n", WEXITSTATUS(child_status[i]));
            } else
            if (WIFSIGNALED(child_status[i])) {
                printf(" died from signal %d.\n", WTERMSIG(child_status[i]));
            } else {
                printf(" died from unknown causes.\n");
            }

        } else {
            printf("Child process %d (%d of %d) was lost!\n", (int)child_pid[i], i + 1, count);
        }
    }

    return status;
}

如果将上述内容另存为example.c,则可以使用 eg 将其编译为example

gcc -Wall -O2 example.c -o example

如果你然后跑说

./example 100001:1 100002:5 100003:3 21532:4

输出将类似于

Child: id = 100002, grade = 5, PID = 1260.
Child: id = 100001, grade = 1, PID = 1259.
Child: id = 100003, grade = 3, PID = 1261.
Child: id = 21532, grade = 4, PID = 1262.
Reaped 4 child processes.
Child process 1259 (1 of 4) exited with success (EXIT_SUCCESS), 0.
Child process 1260 (2 of 4) exited with success (EXIT_SUCCESS), 0.
Child process 1261 (3 of 4) exited with success (EXIT_SUCCESS), 0.
Child process 1262 (4 of 4) exited with success (EXIT_SUCCESS), 0.

请注意,初始Child:行可以按任何顺序排列,因为子进程基本上是并行运行的。每个子进程一启动就运行,所以这个例子不是对 OP 要求的复制和粘贴答案。


如果您想尝试复杂的流程层次结构,我建议使用 Graphviz 将它们可视化。例如,dot-kids.c

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

static void reap_all(void)
{
    pid_t  p;
    int    status;

    while (1) {
        p = wait(&status);
        if (p == -1) {
            if (errno == EINTR)
                continue;
            if (errno == ECHILD)
                return;

            fprintf(stderr, "Process %d: reap_all(): %s.\n", (int)getpid(), strerror(errno));
            return;
        }

        printf("    \"%d\" -> \"%d\" [ color=\"#ff0000\" ];\n", (int)p, (int)getpid());

        if (WIFEXITED(status)) {
            if (WEXITSTATUS(status) == EXIT_SUCCESS)
                printf("    \"%d\" [ label=\"%d\" ];\n", (int)p, (int)p);
            else
                printf("    \"%d\" [ label=\"%d (exit %d)\" ];\n", (int)p, (int)p, WEXITSTATUS(status));
        } else
        if (WIFSIGNALED(status))
            printf("    \"%d\" [ label=\"%d (signal %d)\" ];\n", (int)p, (int)p, WTERMSIG(status));
        else
            printf("    \"%d\" [ label=\"%d (lost)\" ];\n", (int)p, (int)p);

        fflush(stdout);
    }
}

static pid_t run_child(int (*child)(int depth, int width), int depth, int width)
{
    pid_t  p;

    fflush(stdout);
    fflush(stderr);

    p = fork();
    if (p == -1) {
        fprintf(stderr, "Process %d: Cannot fork: %s.\n", (int)getpid(), strerror(errno));
        return -1;
    } else
    if (!p) {
        exit(child(depth, width));
    } else {
        printf("    \"%d\" -> \"%d\" [ color=\"#0000ff\" ];\n", (int)getpid(), (int)p);
        fflush(stdout);
        return p;
    }
}

int child(int depth, int width)
{
    if (depth > 0) {
        while (width > 0)
            run_child(child, depth - 1, width--);
        reap_all();
    }
    return EXIT_SUCCESS;
}

int main(int argc, char *argv[])
{
    int  depth, width, i;
    char dummy;

    if (argc != 3 || !strcmp(argv[1], "-h") || !strcmp(argv[2], "--help")) {
        fprintf(stderr, "\n");
        fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
        fprintf(stderr, "       %s depth width | dot -Tx11\n", argv[0]);
        fprintf(stderr, "\n");
        return EXIT_SUCCESS;
    }

    if (sscanf(argv[1], " %d %c", &depth, &dummy) != 1 || depth < 0) {
        fprintf(stderr, "%s: Invalid depth.\n", argv[1]);
        return EXIT_FAILURE;
    }
    if (sscanf(argv[2], " %d %c", &width, &dummy) != 1 || width < 1) {
        fprintf(stderr, "%s: Invalid width.\n", argv[2]);
        return EXIT_FAILURE;
    }

    printf("digraph {\n");
    printf("    \"%d\" [ shape=\"box\", label=\"%d\" ];\n", (int)getpid(), (int)getpid());
    fflush(stdout);

    for (i = 0; i < width; i++)
        run_child(child, depth, width - 1);

    reap_all();
    printf("}\n");
    return EXIT_SUCCESS;
}

使用例如编译它

gcc -Wall -O2 dot-kids.c -o dot-kids

并使用例如运行

./dot-kids 1 3 | dot -Tx11

要查看类似于Graphviz点图其中数字是进程 ID的进程图,蓝色箭头显示哪个进程创建了哪个进程,红色箭头显示了哪个进程收获了哪个进程。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

如何在C ++中创建自定义整数序列

来自分类Dev

如何在C#中创建自定义事件

来自分类Dev

如何在(C#)中创建自定义光标

来自分类Dev

如何在C#中创建自定义事件

来自分类Dev

如何在Leaflet中定义多个自定义图标?

来自分类Dev

如何在Angularjs的单个模块中创建多个自定义过滤器

来自分类Dev

如何在Angularjs的单个模块中创建多个自定义过滤器

来自分类Dev

在 C++ 中创建自定义类的多个实例

来自分类Dev

如何在jbpm6中使用用户任务功能创建自定义的特定于域的进程?

来自分类Dev

如何在ActiveAdmin gem中创建自定义页面

来自分类Dev

如何在dart中创建自定义元素?

来自分类Dev

如何在Django中创建自定义选择字段?

来自分类Dev

如何在Android中创建自定义导航抽屉

来自分类Dev

如何在Laravel 4中创建自定义外观

来自分类Dev

如何在Drupal 7中创建自定义块?

来自分类Dev

如何在AngularJs服务中创建自定义事件

来自分类Dev

如何在Moodle中创建自定义表单?

来自分类Dev

如何在android中创建自定义渐变?

来自分类Dev

如何在AMPL中创建自定义函数?

来自分类Dev

如何在SpriteKit中创建自定义混合模式

来自分类Dev

如何在Nginx中创建自定义位置?

来自分类Dev

如何在Powershell中创建自定义数组?

来自分类Dev

如何在angular中创建自定义指令

来自分类Dev

如何在Python中创建自定义分组视图?

来自分类Dev

如何在Shopify中创建自定义Web挂钩

来自分类Dev

如何在RMarkdown中创建自定义块?

来自分类Dev

如何在Flutter中创建自定义日历

来自分类Dev

如何在Flutter中创建自定义列表?

来自分类Dev

如何在Material TextInputLayout中创建自定义MultiAutoCompleteTextView?

Related 相关文章

热门标签

归档