我是否需要创建共享内存或仅实现命名信号量?
我从以下链接得到两个答案:
> Do forked child processes use the same semaphore?
> 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)在父进程中创建和初始化信号量.给它一个众所周知的名称,儿童过程将知道;此名称用于查找系统中的信号量.
>关闭父级中的信号量,因为它不会使用它.
>分叉并执行
>使用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; }
您可以通过在公共头文件中定义信号量名称并将其包含在每个程序的代码中来消除在两个源文件之间保持信号量名称同步的需要.
请注意,在此示例中错误处理并不理想(仅仅是说明性的),还有很大的改进空间.当您决定更改此示例以满足您的需求时,确保您不会忘记执行正确的错误处理.