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

dot*_*ian 12 c posix semaphore ipc

我需要创建两个子进程,每个子进程调用execvpater被分叉,可执行文件在它们之间共享POSIX信号量.

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

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

  1. 分叉子进程使用相同的信号量吗?
  2. 如何使用共享内存在进程之间共享信号量

但我对如何继续实施感到困惑.

Fil*_*ves 18

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

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

使用命名信号量,在您的示例场景中,这是发生的事情:

  • 您可以使用创建和初始化父进程中的信号量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;
}
Run Code Online (Sandbox Code Playgroud)

注意,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;
}
Run Code Online (Sandbox Code Playgroud)

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

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