我有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/

10-15 00:24