c - 使用 System V 信号量的 3 个进程进行同步
问题描述
我有 3 个进程,我想使用 system v 信号量进行同步。进程 1,2,3 将数据写入单个文件。
进程 1 将 A 写入 I,进程 2 将 a 写入 i,进程 3 将 1 写入 9。
输出我期待 Aa1Bb2Cc3Dd4Ee5Ff6Gg7Hh8Ii9。
过程 1
#include<sys/types.h>
#include<sys/sem.h>
#include<sys/ipc.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdio.h>
int main()
{
int id,ret,fd;
char i;
struct sembuf v;
id=semget(8, 5, IPC_CREAT | 0644);
if(id <0)
{
printf("wrong\n");
}
fd= open("sample1", O_RDWR | O_APPEND | O_CREAT,0644);
v.sem_num = 1;
v.sem_op = 0;
v.sem_flg = 0;
semctl(id, 1, SETVAL, 0);
semctl(id, 2, SETVAL, 0);
semctl(id, 3, SETVAL, 0);
for(i='A';i<='I';i++)
{
semop(id,&v,1);
semctl(id, 1, SETVAL, 1);
semctl(id, 2, SETVAL, 1);
semctl(id, 3, SETVAL, 1);
write(fd, &i, 1);
semctl(id, 2, SETVAL, 0);
semctl(id, 3, SETVAL, 1);
semctl(id, 1, SETVAL, 1);
}
printf("Done...\n");
}
过程 2
#include<sys/types.h>
#include<sys/sem.h>
#include<sys/ipc.h>
#include<fcntl.h>
#include<stdio.h>
#include<unistd.h>
int main()
{
int id,ret,fd;
char i;
struct sembuf v;
id=semget(8,5, IPC_CREAT | 0644);
if(id <0)
{
printf("wrong\n");
}
fd= open("sample1", O_RDWR | O_APPEND | O_CREAT, 0644);
v.sem_num = 2;
v.sem_op = 0;
v.sem_flg = 0;
for(i='a';i<='i';i++)
{
semop(id,&v,2);
semctl(id, 1, SETVAL, 1);
semctl(id, 2, SETVAL, 1);
semctl(id, 3, SETVAL, 1);
write(fd, &i, 1);
semctl(id, 3, SETVAL, 0);
semctl(id, 1, SETVAL, 1);
semctl(id, 2, SETVAL, 1);
}
printf("Done...\n");
}
过程 3
#include<sys/types.h>
#include<sys/sem.h>
#include<sys/ipc.h>
#include<fcntl.h>
#include<stdio.h>
#include<unistd.h>
int main()
{
int id,ret,fd;
char i;
struct sembuf v;
id=semget(8,5, IPC_CREAT | 0644);
if(id <0)
{
printf("wrong\n");
}
fd= open("sample1", O_RDWR | O_APPEND | O_CREAT,0644);
v.sem_num = 3;
v.sem_op = 0;
v.sem_flg = 0;
for(i='1';i<='9';i++)
{
semop(id,&v,3);
semctl(id, 1, SETVAL, 1);
semctl(id, 2, SETVAL, 1);
semctl(id, 3, SETVAL, 1);
write(fd, &i, 1);
semctl(id, 1, SETVAL, 0);
semctl(id, 2, SETVAL, 1);
semctl(id, 3, SETVAL, 1);
}
printf("Done...\n");
}
预期输出为 Aa1Bb2Cc3Dd4Ee5Ff6Gg7Hh8Ii9。但我没有得到正确的输出。请问有人可以帮助解决这个问题吗?
哪个信号灯好?系统 V 信号量还是 POSIX 信号量?我是同步过程的新手。请我帮忙!
提前致谢。
解决方案
你似乎有几个问题,无论是在战略上还是在实施上。
首先,通过 SysV 信号量控制程序/线程进度的常用基本机制是让要控制其进度的线程semop()
尝试减少信号量。
struct sembuf sb = { .semnum = 1, .sem_op = -1 };
int rval = semop(semid, &sb, 1);
// handle any error ...
这将阻塞,直到信号量的值足够大以继续进行(假设它永远不会低于零)。为了允许该线程继续进行,可能在不同进程中的某个其他线程将使用semop()
来增加相同的信号量:
struct sembuf sb = { .semnum = 1, .sem_op = 1 };
int rval = semop(semid, &sb, 1);
// handle any error ...
此外,这种方法意味着管理锁定信号量的线程(通过递减其值)会自动减少其他线程(或自身)锁定信号量的机会。当您安排它以使信号量值永远不会超过 1 时,这使得递减信号量函数很像锁定互斥锁,而递增一个函数就像解锁互斥锁一样。
请注意semctl()
,这些位中的任何一个都不涉及(尽管它将涉及预先设置信号量)。您应该将semctl()
其视为管理信号量的管理接口,而不是普通信号量操作的接口。
其次,信号量初始化有问题。它仅由进程 1 执行,但您没有提供任何内容来确保进程 1 将在其他进程开始尝试使用信号量集之前完成其初始化。事实证明,这是 SysV 信号量最有问题的方面之一。
解决这个问题的一种方法是使用一个启动器来设置和初始化信号量集,然后才启动实际使用它的三个进程(这些进程都不需要初始化它)。
如果您可以依赖最初不存在的信号量集(这对您来说是个问题,因为您使用固定键并且从不删除信号量集),那么您的进程O_EXCL
除了O_CREAT
调用semget()
. 这只会在其中一个进程中成功,然后该进程可以负责初始化信号量。其他的执行一个新的semget()
without O_EXCL
,然后等待初始化完成。他们可以通过轮询来做到这一点semctl()
,IPC_STAT
观察信号量otime
变为非零,这将在初始化线程第一次执行时发生semop()
。
第三,您的代码中有多个小问题,包括
- 您请求一个 5 成员信号量集,但只使用 3 个。
- 信号量集的成员从 0 开始编号,但您使用的最小信号量编号是 1。
- 除非您
IPC_PRIVATE
用作信号量密钥,否则通常通过获取密钥ftok()
而不是使用固定密钥 ID。这有助于避免键冲突。 - 当给定时,第四个参数
semctl
应该是 aunion
(你必须自己定义,与文档一致),但你传递的是int
s。如果这似乎像你预期的那样工作,那只是因为你很幸运。 - 在使用信号量完成最后一个过程后,您应该将其删除。您可以通过使用 执行该
IPC_RMID
操作,或者,如有必要,在事后通过从 shellsemctl()
运行适当的命令来执行此操作。ipcrm
- 您应该检查每个提供返回码的函数调用的返回码,除非您真的不关心它是否成功。
推荐阅读
- docker - Docker ENTRYPOINT 不执行 command/tox
- javascript - 根据另一个设置为只读的输入字段中的值自动填充输入文本字段
- unit-testing - 在 Windows 10 上运行“npm test”的问题
- docker - Kubernetes 微服务 Spring Cloud
- angular - Angular 6 - ngFor 中的 ngModel 正在更新数组中的每个项目
- c++ - 在这种处理指针和地址的场景中,我将如何继续 1 到 360 的“for 循环”?
- c++11 - 编译器计算不正确?
- sql - 从行到列的 SQL 结果
- jquery - 悬停在父项上显示隐藏的子项
- bash - 将数字添加到相邻列并根据匹配 bash awk 递增