Parent - 两个 Child 相互交流的正确方式

Parent - Two Child correct way to communicate with each other

我有一个小程序,其中有一个parent进程两个child进程 .首先,parent 进程通过管道将数据发送到它的 children (childA, childB)。为了更清楚,我有一个指针数组,其中包含指向结构(Rabbit)的元素。 parent 进程过滤此数组并将过滤后的数据发送到 childA 和 childB 相同,只是此数据会有所不同。然后 childA 和 childB 对其数据进行处理并将其发送回 parent 进程。我必须用 pipes 来实现它。这个代码片段有什么问题,我该如何解决这个问题?

int pipefd_a[2];
int pipefd_b[2];
pid_t child_a, child_b;

if (pipe(pipefd_a) == -1) {
    perror("Error during opening pipe A!");
    exit(EXIT_FAILURE);
}
if (pipe(pipefd_b) == -1) {
    perror("Error during opening pipe B!");
    exit(EXIT_FAILURE);
}

child_a = fork();

if (child_a == 0) {    // Child A
    close(pipefd_a[1]);
    Rabbit rabbit;
    while (read(pipefd_a[0], &rabbit, sizeof(Rabbit))) {
        // Do something
    }
    close(pipefd_a[0]);
} else {
    child_b = fork();
    
    if (child_b == 0) {    // Child B
        close(pipefd_b[1]);
        Rabbit rabbit;
        while (read(pipefd_b[0], &rabbit, sizeof(Rabbit))) {
            // Do something
        }
        close(pipefd_b[0]);
    } else {
        Rabbit** rabbits_a = (Rabbit**)malloc(sizeof(Rabbit*) * size);
        ...
        // Filter, count_a will be the size of the filtered array
        close(pipefd_a[0]);
        for (unsigned i = 0; i < count_a; ++i) {
            Rabbit rabbit = {rabbits_a[i]->name, rabbits_a[i]->district, rabbits_a[i]->part_count};
            write(pipefd_a[1], &rabbit, sizeof(Rabbit));
        }
        ...
        // The same for B
        ...
        fflush(NULL);
        wait(NULL);
    }
}

乍一看,您似乎走在了正确的轨道上,但是您在子进程中遗漏了一些代码,无法将处理后的数据写回管道,以便父进程可以获取这些数据过程。此外,您的 wait() 调用可能应该是在您调用 fork() 之后父进程中的第一个调用,因为您无法保证子进程将在父进程之前 运行。此外,您可能希望设置某种循环结构,以便 wait 直到两个子进程都终止,然后再尝试从它们的管道中读取数据。

因此,您的程序结构可能如下所示:

// #include <sys/stat.h>
#include <unistd.h>
// ...
// ... assuming there's code you didn't show that writes the initial data to the pipes to get it into the children processes...
child_a = fork();

if (child_a == 0) {
    // Read from pipe, do stuff, and write to pipefd_a[1] (so you can't close it)
} else {

    child_b = fork();

    if (child_b == 0) {
        
        // read from pipe, do stuff, and write to pipefd_b[1]
    } else {
        // Wait for both child processes to end
        pid_t pids[] = {child_a, child_b};
        pid_t returned_pid;
        
        int i;
        for (i = 0; i < 2; i++) {

            // the wait() call will return the pid of the process that
            // caused wait() to terminate. Since you forked off two processes
            // wait could either return child_a or child_b, and we
            // want to make sure both processes are finished prior to
            // trying to read stuff from their pipes, hence the outer
            // for loop

            // Update: I realized that if the process is already dead, 
            // then the do-while loop just runs forever, so I'm throwing in a check 
            // to make sure the process is alive before starting the loop
            struct stat dummy;
            char buf[70];

            // Every running process on a UNIX system has a corresponding 
            // ID number and a corresponding subdirectory within the /proc 
            // directory, so if the directory doesn't exist, the process doesn't exist either
            // sprintf(buf, "/proc/%d", pids[i]);

            // ---------------------------------------
            // Edited again because I'm dumb and forgot 
            // /proc doesn't exist on macOS (which is what I'm on). 
            // If you're there, then you can do something like this, which I'll explain below.

            sprintf(buf, "ps -e | awk '{ print  }' | grep %d", pids[i]);

            int result = system(buf);
            
            // If on Linux you can  do...
            /* 
            if ( stat(buf, &dummy) < 0 ) {
                // Process is dead, continue
                continue;
            } */


            // If on a UNIX system (mac or Linux)...
            if (result != 0) {
                // Process is dead, continue
                continue;
            }
                        
            do {
                returned_pid = wait(NULL);
            } while (returned_pid != pids[i]);
        }

        // At this point, both child processes have terminated, so NOW
        // we can read from the pipes

        // Read from pipefd_a[0]
        
        // Read from pipefd_b[0]
    }
}

那么,这个字符串到底是什么:`ps -e | awk '{ 打印 $1 }' | grep %d`

这是一串三个 shell 命令:

  1. ps -e - 这将打印有关所有当前 运行ning 进程的信息。这包括进程 ID
  2. awk '{ print }' - 这个 运行 是一个 awk 脚本,它只打印给定字符串中的第一个字段,其中字段由空格分隔
  3. grep %d - 这将在给定文件(在本例中为标准输入)中搜索提供的 PID(因为这是格式字符串,%d 被替换为进程 ID)

然后,system命令运行整个字符串。 return 值为 0 表示命令字符串已成功执行,这意味着在当前 运行ning 进程列表中找到了进程 ID。

无论如何,我希望你能从这篇文章中得到一些有价值的信息post。