分叉后同步N个同级进程

时间:2018-12-11 23:41:04

标签: c unix synchronization fork ipc

我很难同步N个子进程,以等待每个进程到达某个特定点。 我已经尝试过信号量和信号,但是我无法解决。

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <fcntl.h>
#include <semaphore.h>
#include <sys/wait.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/msg.h>

#define NUM_KIDS 4

void handle(int signum);

int main(int argc, char const *argv[])
{
    sem_t* sem;
    sem = sem_open("/ok", O_CREAT, 0);
    signal(SIGUSR1, handle);

    for(int i = 0; i < NUM_KIDS; i++) {
        switch(fork()) {
            case 0:
                fprintf(stderr, "ready %d from %d\n", getpid(), getppid());
                /* i would like that each child stop here untill everyone is ready */
                for(int j = 0; j < 10; j++) 
                fprintf(stderr, "lot of stuff\n");
                exit(0);
            break;
            default:
                /* unleashing the kids when everyone is ready */
                wait(NULL);
                fprintf(stderr, "OK\n");

            break;
        }
    }
    return 0;
}

void handle(int signum) {;}

我相信输出应该是(一旦孩子同步)

ready ... from xxx
ready ... from xxx
ready ... from xxx
ready ... from xxx
...lots of stuff... 10 times
...lots of stuff... 10 times
...lots of stuff... 10 times
...lots of stuff... 10 times

1 个答案:

答案 0 :(得分:2)

同步

有一个简单的技巧:

  • 在派生任何东西之前创建一个管道。
  • 让孩子们分别关闭管道的写端。
  • 要同步子级时让它们从管道中读取。
  • 在启动子级时,让父级关闭管道的两端。
  • 释放子级时,让子级关闭管道的读取端,以便释放资源。
  • 孩子们现在做“他们的事”(长大,产生产出,死亡)。
  • 父级现在等待子级死亡(在Unix上使用进程时这是一种病态的业务)。

如果操作正确,则子级将同时获得EOF(读取零字节),因为不再有任何进程可以写入管道。 (这就是为什么让孩子在进行同步read()之前关闭管道的写端很重要的原因。)

如果您希望父母知道孩子们已经准备就绪,请在分叉任何东西之前创建两个管道。父进程关闭第二个管道的写端,然后从读端进行读取。孩子们在进入第一个管道的read()调用之前,都关闭了管道的两端。当所有子项都已关闭管道的写端时,父进程将获得EOF,因此它知道所有子项都已启动,至少到关闭第二个管道为止。然后,父级可以关闭第一个管道以释放子级(并关闭第二个管道的读取端)。

不要等待太久!

您正在等待交换机的default子句,这是不正确的。在等待之前,您需要启动所有四个子进程-否则它们将永远无法同步。等待时,您需要在(新)循环中进行等待。并且,在调试时,应添加打印语句以标识父进程中发生的事情。例如,您将打印退出进程的状态及其PID:

int corpse;
int status;
while ((corpse = wait(&status)) > 0)
    printf("%d: child %d exited with status 0x%.4X\n", (int)getpid(), corpse, status);

工作代码

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

#define NUM_KIDS 4

int main(void)
{
    int p_pipe[2];
    int c_pipe[2];
    char c;
    if (pipe(p_pipe) != 0 || pipe(c_pipe) != 0)
    {
        fprintf(stderr, "Oops: failed to create pipes\n");
        return 1;
    }

    for (int i = 0; i < NUM_KIDS; i++)
    {
        switch (fork())
        {
        case 0:
            fprintf(stderr, "ready %d from %d\n", (int)getpid(), (int)getppid());
            close(p_pipe[0]);
            close(p_pipe[1]);
            close(c_pipe[1]);
            read(c_pipe[0], &c, 1);
            close(c_pipe[0]);
            for (int j = 0; j < 10; j++)
                fprintf(stderr, "lot of stuff\n");
            return NUM_KIDS + i;
        case -1:
            fprintf(stderr, "failed to fork child %d\n", i+1);
            return 1;
        default:
            break;
        }
    }

    close(p_pipe[1]);
    read(p_pipe[0], &c, 1);
    printf("%d: %d children started\n", (int)getpid(), NUM_KIDS);
    close(c_pipe[0]);
    close(c_pipe[1]);

    int corpse;
    int status;
    while ((corpse = wait(&status)) >= 0)
        printf("%d: child %d exited with status 0x%.4X\n", (int)getpid(), corpse, status);
    return 0;
}

样品运行

ready 81949 from 81948
ready 81950 from 81948
ready 81951 from 81948
ready 81952 from 81948
81948: 4 children started
lot of stuff
lot of stuff
lot of stuff
lot of stuff
…lines omitted for brevity…
lot of stuff
lot of stuff
lot of stuff
lot of stuff
81948: child 81951 exited with status 0x0600
81948: child 81952 exited with status 0x0700
81948: child 81950 exited with status 0x0500
81948: child 81949 exited with status 0x0400