我需要创建共享内存还是只实现命名
信号量?
这两种方法都行得通。选择一个并使用它——尽管我个人更喜欢命名信号量,因为您不必处理内存分配和设置共享内存段。在我看来,创建和使用命名信号量的界面更加友好。
使用命名信号量,在您的示例场景中,会发生以下情况:
- 您使用
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;
}
您可以消除保持信号量名称在两个源文件之间同步的需要,方法是在公共头文件中定义信号量名称并将其包含在每个程序的代码中。
请注意,此示例中的错误处理并不理想(这只是说明性的),还有很大的改进空间。它只是为了确保您在决定更改此示例以满足您的需要时不会忘记进行适当的错误处理。