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

标签 c posix ipc semaphore

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

我需要创建共享内存还是只需要实现命名信号量?

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

  1. Do forked child processes use the same semaphore?
  2. How to share semaphores between processes using shared memory

但我对如何进行实现感到困惑。

最佳答案

Do I need to create a shared memory or just implement named semaphores?

这两种方法都行得通。选择一个并使用它——尽管我个人更喜欢命名信号量,因为您不必处理内存分配和设置共享内存段。在我看来,用于创建和使用命名信号量的界面更加友好。

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

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

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

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

它假定父进程 fork 并执行 ./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) 在两个 child 都终止后被调用;虽然这不是必需的,但如果在父进程取消链接信号量和两个子进程启动并打开信号量之前调用它,就会出现竞争条件。不过,一般来说,只要您知道所有必需的进程都已打开信号量并且没有新进程需要找到它,您就可以取消链接。

下面是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;
}

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

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

关于c - 在多个进程之间共享 POSIX 信号量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32205396/

相关文章:

c - 如何在C中保存字符串的最后一个字符?

c++ - 错误打印 posix_time

regex - 用于密码验证的 Oracle APEX POSIX 实现 REGEX

arm - 基于 remoteproc 的内核间通信

c - C 中的 while 循环验证输入是数字?

c - 尝试了解在迭代反转链表时列表如何被修改

c++ - Eclipse CDT 显示一些错误,但项目已成功构建

c - 查找内存缓冲区中任何未设置位的位置的快速方法

linux - Unix 域套接字如何区分多个客户端?

c# - .NET 中的进程间通信