如何在C语言中创建共享内存中的信号量?

10

我的任务是创建两个不同的C文件,然后使用信号量进行进程同步(我同时运行两个C文件)。

我的主要问题是:如果我想在这两个进程(即C文件的可执行文件)中访问信号量,我需要在共享内存中创建信号量。同时我需要创建二进制信号量。

由于这是我的第一个程序,有没有人能建议一下如何开始呢?

我已经能够创建和使用共享内存,在线程中使用了信号量。我也观看了一些YouTube上的讲座,但找不到合适的解决方案。


你使用的操作系统是什么?如果像Linux一样,它具有可在进程之间使用的信号量,那就直接使用它。 - Scott Hunter
我正在UBUNTU上进行这个操作。 - Abhishek Gangwar
@AbhishekGangwar - 你是在问,具体怎么让第二个进程知道共享内存的ID吗? - Amrith Krishna
我能够通过它们的名称在进程之间访问命名计数信号量。 但是,为了创建二进制信号量,我们使用互斥锁,而互斥锁不在进程之间共享,因此我正在寻找一种将计数信号量限制为二进制信号量的方法。 - Abhishek Gangwar
2个回答

9

跨进程信号量是操作系统特定的操作。

大多数情况下,您可以通过虚拟路径(也称为信号量名称)在一个进程中创建信号量。如果权限设置正确,则可以使用相同的虚拟路径在另一个进程中打开该信号量。即使这些虚拟路径看起来很熟悉,它们通常不是真实的文件系统路径。

在基于POSIX/System V的系统上,通常有两个选项。这两个选项之间的区别在这个答案中解释得非常清楚。

System V信号量

可以使用semget()获得的基于路径的信号量:

#include <sys/types.h>
#include <sys/ipc.h>    
#include <sys/sem.h>

int sem;
int sem_id = 1;
key_t key;

key = ftok("/virtualpathtosemaphore", 1);
// create a new semaphore
sem = semget(key, 1, IPC_CREAT);
// use sem = semget(key, 1, 0); to attach to an existing semaphore
// flags also contain access rights, to take care to set them appropriately

// increment semaphore
struct sembuf semopinc = {
  .sem_num = 0,
  .sem_op = 1,
  .sem_flg = 0
};    
semop(sem, &semopinc, 1);

/* decrement semaphore, may block */
struct sembuf semopdec = {
  .sem_num = 0,
  .sem_op = -1,
  .sem_flg = 0
};   
semop(sem, &semopdec, 1);

请注意,清理信号量非常重要,因为System V信号量会一直存在直到明确删除。当一个进程在没有清理它的信号量的情况下崩溃时,这就成了一个问题(例如FreeBSD带有一个实用程序ipcrm来删除悬空的System V IPC对象)。
POSIX信号量
实际上,这些实现不太广泛,所以请检查您的内核是否支持它们。通过sem_open()获得这些的命名版本。
#include <semaphore.h>

sem_t *sem;
sem = sem_open("/nameofsemaphore", O_CREAT, permissions, 0);
// use sem = sem_open("/nameofsemaphore", 0) to open an existing semaphore

/* increment semaphore */
sem_post(sem);

/* decrement semaphore */
sem_wait(sem);
编辑:命名的 POSIX 信号量需要使用 sem_destroy()sem_unlink() 进行销毁和取消链接。从 sem_init() 获得的未命名信号量将在最后一次关闭时被销毁。请注意,可以安全地取消已打开的信号量的链接,它将在最后一次关闭时被销毁。 Windows

Windows 有自己的信号量 API:通过 CreateSemaphore() 创建信号量。

Windows 使用与 POSIX 相同的 命名技巧,但具有不同的名称空间约定。

HANDLE hSem;
hSem = CreateSemaphore(NULL, 0, LONG_MAX, _T("Local\\PathToMySemaphore");

// Use OpenSemaphore() to attach to an existing semaphore

// increment semaphore:
ReleaseSemaphore(hSem, 1, NULL);

// decrement semaphore
WaitForSingleObject(hSem, 0);

不要忘记在修改上述示例时添加错误检查。此外,注意我故意忽略了权限以简化代码。请不要忘记添加相关标志。
除此之外,您还可以(在真正的信号量到来之前通常如此做)将文件锁用作二进制互斥体的形式。

当最后一个拥有信号量句柄的进程退出时,POSIX信号量会被隐式销毁。但实际上并非如此,它们可以在进程退出后继续存在。类似于SysV中的IPC_RMID,有一个显式的sem_unlink()函数用于销毁POSIX信号量。 - Sergei Kulik

6
您说您正在使用Ubuntu GNU/Linux,因此...

请使用命名信号量!

#include <semaphore.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>

// On first process (the one that creates the semaphore)

char semaphoreName[1 + 6 + 1];
semaphoreName[0] = '/';
semaphoreName[1 + snprintf(&semaphore[1], 6 + 1, "%d", getpid())] = '/0';

sem_t *sem = sem_open(semaphoreName, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR, 0);

// On second process

sem_t *sem = sem_open(semaphoreName, O_RDWR);

命名信号量可用 谢谢 你能帮我制作一个可以在不同进程之间使用的二进制信号量吗? - Abhishek Gangwar
你是指未命名信号量吗?当然,你只需要在共享内存区域内声明一个 sem_t,然后执行 sem_init(&theSemaphoreInSharedMemory, 1, 0)。第二个参数 1 非常重要(请参阅 sem_init(3))。 - 3442
如何在共享内存区域中声明一个未命名的信号量。以及如何从计数信号量创建严格的二进制信号量。 - Abhishek Gangwar
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - 3442
要在共享内存段中声明一个 sem_t,请执行以下操作:/* shmPtr是指向共享内存区域基址的指针 */ sem_t *sem = (sem_t*)shmPtr; void *usableSharedMemory = (char*)shmPtr + sizeof(sem_t);。关于二进制信号量,它们没有标准化,您应该创建自己的(请参见https://dev59.com/VGs05IYBdhLWcg3wDttn#7478825)。 - 3442

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接