在多个进程之间共享POSIX信号量

时间:2015-08-25 13:31:04

标签: c posix ipc semaphore

我需要创建两个子进程,每个子进程调用execvp ater,并且可执行文件在它们之间共享POSIX信号量。

我是否需要创建共享内存或仅实现命名信号量?

我从以下链接得到了两个答案:

  1. Do forked child processes use the same semaphore?
  2. How to share semaphores between processes using shared memory
  3. 但我对如何继续实施感到困惑。

2 个答案:

答案 0 :(得分:16)

  

我是否需要创建共享内存或仅实现命名   信号量?

任何一种方法都可行。选择一个并继续使用它 - 虽然我个人更喜欢命名信号量,因为您不必处理内存分配和设置共享内存段。在我看来,创建和使用命名信号量的界面更加友好。

使用命名信号量,在您的示例场景中,会发生以下情况:

  • 使用sem_open(3)在父进程中创建和初始化信号量。给它一个众所周知的名称,儿童过程将知道;此名称用于查找系统中的信号量。
  • 关闭父级中的信号量,因为它不会使用它。
  • 分叉并执行
  • 取消信号量与sem_unlink(3)的关联。这必须完成一次;在哪里(任何具有对信号量对象的引用的进程都可以执行它)并不重要。如果其他进程仍然打开信号量,则可以取消链接信号量:仅当所有其他进程都已关闭时,信号量才会被销毁,但请记住,名称会立即删除,因此新进程将无法找到打开信号量。

子进程使用众所周知的名称调用sem_open(3)来查找并获取对信号量的引用。使用信号量完成一个过程后,您需要使用sem_close(3)关闭它。

以下是我刚才描述的一个例子。父进程创建一个命名信号量,并且forks +执行2个子进程,每个进程查找并打开信号量,使用它来相互同步。

它假定父分叉并执行./sem_chld二进制文件。请记住,信号量的名称必须以正斜杠开头,后跟一个或多个不是斜杠的字符(请参阅man sem_overview)。在此示例中,信号量的名称为/semaphore_example

以下是父流程的代码:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <semaphore.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>

#define SEM_NAME "/semaphore_example"
#define SEM_PERMS (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)
#define INITIAL_VALUE 1

#define CHILD_PROGRAM "./sem_chld"

int main(void) {

    /* We initialize the semaphore counter to 1 (INITIAL_VALUE) */
    sem_t *semaphore = sem_open(SEM_NAME, O_CREAT | O_EXCL, SEM_PERMS, INITIAL_VALUE);

    if (semaphore == SEM_FAILED) {
        perror("sem_open(3) error");
        exit(EXIT_FAILURE);
    }

    /* Close the semaphore as we won't be using it in the parent process */
    if (sem_close(semaphore) < 0) {
        perror("sem_close(3) failed");
        /* We ignore possible sem_unlink(3) errors here */
        sem_unlink(SEM_NAME);
        exit(EXIT_FAILURE);
    }

    pid_t pids[2];
    size_t i;

    for (i = 0; i < sizeof(pids)/sizeof(pids[0]); i++) {
        if ((pids[i] = fork()) < 0) {
            perror("fork(2) failed");
            exit(EXIT_FAILURE);
        }

        if (pids[i] == 0) {
            if (execl(CHILD_PROGRAM, CHILD_PROGRAM, NULL) < 0) {
                perror("execl(2) failed");
                exit(EXIT_FAILURE);
            }
        }
    }

    for (i = 0; i < sizeof(pids)/sizeof(pids[0]); i++)
        if (waitpid(pids[i], NULL, 0) < 0)
            perror("waitpid(2) failed");

    if (sem_unlink(SEM_NAME) < 0)
        perror("sem_unlink(3) failed");

    return 0;
}

请注意,在两个孩子终止后调用sem_unlink(3);虽然这不是必需的,但如果之前调用它,则父进程之间会出现争用条件,取消链接信号量,并启动和打开信号量。但是,一般情况下,只要您知道所有必需的进程都已打开信号量并且没有新进程需要查找它,您就可以取消链接。

以下是sem_chld的代码,它只是一个小玩具程序,用于显示共享信号量的使用情况:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <semaphore.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>

#define SEM_NAME "/semaphore_example"
#define ITERS 10

int main(void) {
    sem_t *semaphore = sem_open(SEM_NAME, O_RDWR);
    if (semaphore == SEM_FAILED) {
        perror("sem_open(3) failed");
        exit(EXIT_FAILURE);
    }

    int i;
    for (i = 0; i < ITERS; i++) {
        if (sem_wait(semaphore) < 0) {
            perror("sem_wait(3) failed on child");
            continue;
        }

        printf("PID %ld acquired semaphore\n", (long) getpid());

        if (sem_post(semaphore) < 0) {
            perror("sem_post(3) error on child");
        }

        sleep(1);
    }

    if (sem_close(semaphore) < 0)
        perror("sem_close(3) failed");

    return 0;
}

您可以通过在公共头文件中定义信号量名并将其包含在每个程序的代码中来消除在两个源文件之间保持信号量名称同步的需要。

请注意,在此示例中错误处理并不理想(仅仅是说明性的),还有很大的改进空间。当您决定更改此示例以满足您的需求时,确保您不会忘记执行正确的错误处理。

答案 1 :(得分:0)

共享内存方法也可以在这里工作,这里只有父进程必须初始化共享内存。 这个代码中似乎有一个bug而不是两个子代,父函数会在这里fork 3子进程。

中应该有break语句
    if (pids[i] == 0) {
        if (execl(CHILD_PROGRAM, CHILD_PROGRAM, NULL) < 0) {
            perror("execl(2) failed");
            exit(EXIT_FAILURE);
        }
     break; //this is required
  }