使用3个进程的System V信号量进行同步

Synchronization using System V semaphore of 3 process

我有 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。但我没有得到正确的输出。请问有人可以帮助解决这个问题吗?

哪个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 信号量最有问题的方面之一。

解决这个问题的一种方法是使用一个启动器来设置和初始化信号量集,然后才启动实际使用它的三个进程(none 然后需要对其进行初始化) .

如果您可以依赖最初不存在的信号量集(这对您来说是有问题的,因为您使用固定键并且从不删除信号量集)那么您的进程可以另外使用 O_EXCL 标志当他们打电话给 semget() 时到 O_CREAT。这只会在其中一个进程中成功,然后该进程可以负责初始化信号量。其他的执行一个没有O_EXCL的新的semget(),然后等待初始化完成。他们可以通过用 IPC_STAT 轮询 semctl() 来做到这一点,观察信号量的 otime 变为非零,这将在初始化线程第一次执行 semop() 时发生。 =33=]

第三,你的代码中有多个小问题,包括

  • 您请求一个包含 5 个成员的信号量集,但只使用了 3 个。
  • 信号量集的成员从 0 开始编号,但您使用的最小信号量编号是 1。
  • 除非您使用 IPC_PRIVATE 作为信号量密钥,否则通常通过 ftok() 获取密钥而不是使用固定的密钥 ID。这有助于避免键冲突。
  • 如果给定,semctl 的第四个参数应该是 union(您必须自己定义,与文档一致),但您传递的是 ints .如果这看起来像您预期的那样有效,那只是因为您很幸运。
  • 最后一个进程处理完信号量后,您应该将其删除。您可以通过使用 semctl() 执行 IPC_RMID 操作来做到这一点,或者,如果需要,事后通过 运行 来自 shell 的适当的 ipcrm 命令。
  • 您应该检查 每个 函数调用的 return 代码,除非您真的不关心它是否成功。