我有3个进程,我想使用系统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。但我没有得到正确的输出。请帮助摆脱这个问题?
哪个sempahore好?系统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()
的情况下执行新的O_EXCL
,然后等待初始化完成。他们可以通过用semctl()
轮询IPC_STAT
,观察信号量的otime
变为非零来实现,这将在初始化线程第一次执行semop()
时发生。第三,您的代码中有多个小怪异之处,包括
您请求一个5成员的信号量集,但只能使用3。
信号量集的成员从0开始编号,但是您使用的最低信号量编号是1。
除非使用
IPC_PRIVATE
作为信号量键,通常是通过ftok()
而不是使用固定的键ID来获取键。这有助于避免按键碰撞。给定后,
semctl
的第四个参数应该是union
(根据文档,您必须定义自己),但是您正在传递int
。如果这看起来像您期望的那样有效,那仅仅是因为您很幸运。使用信号量完成最后一个过程后,应将其删除。您可以通过对
IPC_RMID
执行semctl()
操作来执行此操作,或者在必要时通过从Shell运行适当的ipcrm
命令来执行此操作。您应该检查提供一个的每个函数调用的返回码,除非您真的不在乎它是否成功。
关于c - 使用3进程的System V信号量进行同步,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/51086334/