首页 > 解决方案 > 使用 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 信号量?我是同步过程的新手。请我帮忙!

提前致谢。

标签: cprocesssynchronizationsemaphore

解决方案


你似乎有几个问题,无论是在战略上还是在实施上。

首先,通过 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应该是 a union(你必须自己定义,与文档一致),但你传递的是ints。如果这似乎像你预期的那样工作,那只是因为你很幸运。
  • 在使用信号量完成最后一个过程后,您应该将其删除。您可以通过使用 执行该IPC_RMID操作,或者,如有必要,在事后通过从 shellsemctl()运行适当的命令来执行此操作。ipcrm
  • 您应该检查每个提供返回码的函数调用的返回码,除非您真的不关心它是否成功。

推荐阅读