如何使用信号量使子进程相互同步?

Posted

技术标签:

【中文标题】如何使用信号量使子进程相互同步?【英文标题】:How to synchronize child processes with each other using semaphores? 【发布时间】:2020-09-22 08:29:45 【问题描述】:

我有N 数量的孩子需要在循环中做一些工作,同时彼此同步。即,如果一个子进程处于其第 i 次迭代,则所有其他子进程都应处于第 i 次迭代。我需要将它们与信号量同步,但我找不到如何做到这一点。这是我写的代码:

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

void sem_signal(int semid, int val) 
    struct sembuf semaphore;
    semaphore.sem_num = 0;
    semaphore.sem_op = val;
    semaphore.sem_flg = 0;
    semop(semid, &semaphore, 1);


void sem_wait(int semid, int val) 
    struct sembuf semaphore;
    semaphore.sem_num = 0;
    semaphore.sem_op = (-1 * val);
    semaphore.sem_flg = 0;
    semop(semid, &semaphore, 1);


int main() 
    int sem_worker = semget(1, 1, 0700 | IPC_CREAT);
    semctl(sem_worker, 0, SETVAL, 0);
    int process_index = 0;
    int N = 4, pid;

    for (process_index = 0; process_index < N; process_index++) 
        pid = fork();
        if (pid == -1) 
            printf("ERROR: cannot fork!\n");
            return EXIT_FAILURE;
        
        if (pid == 0)
            break;
    
    if (pid!=0) // parent
        pause();

    else 
        int i = 0;
        while (i < 3) 
            printf("process %d: i: %d\n", process_index, i);
            sem_signal(sem_worker, 1); // increase the semaphore by one
            sem_wait(sem_worker, N);   // wait for all the other childs
            i += 1;
        
    

但是当我运行它时,它在第一次迭代后无法继续。

process 0: i: 0
process 1: i: 0
process 3: i: 0
process 2: i: 0
process 0: i: 1

我明白为什么会这样。这是因为其中一个进程使信号量为 0 并继续下一次迭代,但所有其他进程仍在等待。那么我应该如何编写代码来解决这个问题呢?

P.S:我从其他地方获取了 sem_signalsem_wait 函数,所以我不确定它是如何工作的,但我确信它们工作正常。例如,如果我在父进程中写入 sem_wait(my_sem, num_of_children) 以等待所有子进程,并在子进程完成时将 my_sem 增加 1,则它可以工作。

【问题讨论】:

您的代码没有任何意义。但是,您描述的是一个障碍,您应该能够找到可以帮助您解决此问题的资源。 好吧,我写这个只是为了证明我的目的,我知道它不会起作用。如果您在谈论main 以上的功能,我不是它们的作者 :D 感谢您的建议,我会看看它。 @EOF 这似乎是完全不同的概念。这是我的任务的一部分,据说“你必须使用信号量进行进程同步”所以我不能使用 barriers。你还有什么想法吗? 不,它不是不同的概念。如果您阅读例如的描述pthread_barrier_wait(),您会发现它正是您需要的。您可以从几个信号量合成一个屏障,请参阅此免费提供的book 【参考方案1】:

正如 cmets 中提到的,您可以使用信号量创建屏障并使用它来同步您的进程。您需要在共享内存中创建屏障并为信号量的pshared 参数设置一个非零值以在进程之间共享它:

#include <semaphore.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/shm.h>
#include <signal.h>
#include <unistd.h>


typedef struct 
    int n;
    int count;
    sem_t mutex;
    sem_t turnstile;
    sem_t turnstile2;
 barrier_t;

void init_barrier(barrier_t *barrier, int n)

    barrier->n = n;
    barrier->count = 0;
    sem_init(&barrier->mutex, 1, 1); // second parameter is pshared
    sem_init(&barrier->turnstile, 1, 0);
    sem_init(&barrier->turnstile2, 1, 0);


void phase1(barrier_t *barrier)

    sem_wait(&barrier->mutex);
    if (++barrier->count == barrier->n) 
        int i;
        for (i = 0; i < barrier->n; i++) 
            sem_post(&barrier->turnstile);
        
    
    sem_post(&barrier->mutex);
    sem_wait(&barrier->turnstile);


void phase2(barrier_t *barrier)

    sem_wait(&barrier->mutex);
    if (--barrier->count== 0) 
        int i;
        for (i = 0; i < barrier->n; i++) 
            sem_post(&barrier->turnstile2);
        
    
    sem_post(&barrier->mutex);
    sem_wait(&barrier->turnstile2);


void wait_barrier(barrier_t *barrier)

    phase1(barrier);
    phase2(barrier);


int shmid, KEYSHM=123456;

int main(int argc, char const* argv[]) 
    barrier_t* barrier;
    shmid = shmget(KEYSHM, sizeof(barrier_t), 0700 | IPC_CREAT);
    barrier = (barrier_t*) shmat(shmid, 0, 0);
    int N = 4;
    init_barrier(barrier, N);
    shmdt(barrier);


    int process_index, pid;

    for (process_index = 0; process_index < N; process_index++) 
        pid = fork();
        if (pid == -1) 
            printf("ERROR: cannot fork!\n");
            return EXIT_FAILURE;
        
        if (pid == 0)
            break;
    
    if (pid != 0) // parent
        pause();
    else 
        int i = 0;
        while (i < 3) 
            barrier = (barrier_t*) shmat(shmid, 0, 0);
            printf("process %d: i: %d\n", process_index, i);
            i += 1;
            wait_barrier(barrier);
            shmdt(barrier);
        

        if (process_index == 3)
            kill(getppid(), SIGKILL);
        
    

进程0:我:0 进程1:我:0 过程2:我:0 进程 3: i: 0 过程2:我:1 过程3:我:1 进程 0: i: 1 过程1:我:1 过程3:我:2 过程2:我:2 进程 0: i: 2 过程1:我:2

【讨论】:

以上是关于如何使用信号量使子进程相互同步?的主要内容,如果未能解决你的问题,请参考以下文章

深入详解Linux进程间通信之共享内存(Shared Memory)+信号量同步

深入详解Linux进程间通信之共享内存(Shared Memory)+信号量同步

进程同步与相互排斥:POSIX有名信号量

进程相互作用之信号量PV操作及其代码实现

[国嵌攻略][084][信号同步编程]

如何使子进程窗口在我的进程中显示为模态?