POSIX 信号量
POSIX 信号量是一种 POSIX 标准中定义的进程间同步和互斥的方法。它允许进程之间通过信号量来实现临界区的互斥访问,从而避免竞争条件和死锁等问题。
信号量的P、V操作:
- P 操作:用于申请信号量资源。如果信号量的值大于 0,则将信号量的值减 1,表示占用一个资源;如果信号量的值等于 0,则进程将被阻塞等待,直到信号量的值大于 0。
- V 操作:用于释放信号量资源。当一个进程占用资源完成后,使用 V 操作来释放资源,将信号量的值加 1。
- P 操作和 V 操作都是原子操作,确保在并发环境下资源能正确访问。
POSIX信号量有两种:有名信号量(Named Semaphores)和无名信号量(Unnamed Semaphores)。
有名信号量
有名信号量是通过一个 IPC 名字(一般以斜杠开头的路径名)进行进程间的同步。不同进程通过访问该名字来操作同一个信号量,从而实现对临界区的互斥访问。
IPC 名字:类似 "/mySem" 的字符串,当信号量创建成功后,会在 /dev/shm目录下创建一个特殊的文件。
创建、打开信号量
sem_open() 函数是用于创建或打开一个有名信号量,该函数定义如下:
#include <semaphore.h>
sem_t *sem_open(const char *name, int oflag,
mode_t mode, unsigned int value);
参数说明
- name:指定有名信号量的名称(为了跨平台,最好以斜杠开头)。
- oflag:指定打开有名信号量的标志。有以下取值:
- O_CREAT:如果信号量不存在,则创建它。
- O_EXCL:与 O_CREAT 一起使用,确保如果信号量已存在,不再创建。
- mode:指定信号量的权限。
- value:指定信号量的计数器初始值。
返回值
- 如果函数执行成功,返回指向信号量的指针,有名字的信号量以文件形式被创建在 /dev/shm 目录下。
- 如果函数执行失败,返回 SEM_FAILED。
二值信号量与计数信号量
根据参数 value 指定的值,可以将信号量分为二值信号量和计数信号量。
- 二值信号量:信号量的值只有 0 和 1,若资源不可用,信号量的值为0,若资源可用,则信号量的值为1。
- 计数信号量:信号量的值在0到一个大于1的限制值(POSIX指出系统的最大限制值至少要为32767)。该计数表示可用资源的个数。
关闭信号量
sem_close() 函数用于关闭一个已经打开的有名信号量,该函数定义如下:
#include <semaphore.h>
int sem_close(sem_t *sem);
参数说明
返回值
- 如果函数执行成功,返回 0。
- 如果发生错误,返回 -1。可以通过 errno 变量来获取具体的错误信息。
删除信号量文件
sem_unlink() 函数用于删除有名信号量资源,调用此函数后,会删除 /dev/shm 目录下的文件,其他进程无法再通过该名称来访问或打开这个信号量。该函数定义如下:
#include <semaphore.h>
int sem_unlink(const char *name);
参数说明
返回值
- 如果函数执行成功,返回 0。
- 如果发生错误,返回 -1。可以通过 errno 变量来获取具体的错误信息。
等待信号量
等待信号量的函数有两个 sem_wait() 函数和 sem_trywait() 函数。通过检查信号量的值,如果当前值小于或等于0,则调用进程阻塞,直到该值变成大于0。如果当前值大于0,解除阻塞后将信号量值减一,进程访问临界区资源。
sem_wait() 函数定义如下:
#include <semaphore.h>
int sem_wait(sem_t *sem);
参数说明
返回值
- 如果成功执行P操作,则函数返回0。
- 如果发生错误,返回 -1。可以通过 errno 变量来获取具体的错误信息。
sem_trywait() 函数定义如下:
int sem_trywait(sem_t *sem);
参数说明
返回值
- 如果成功执行P操作,则函数返回0。
- 如果信号量的值为0,表示无法立即执行 P 操作,返回 -1 并设置 errno 为 EAGAIN。
- 如果发生其他错误,返回 -1。
释放信号量
sem_post() 函数将信号量的值加1,如果有进程阻塞着等待该信号量,那么其中一个进程将被唤醒。该函数定义如下:
#include <semaphore.h>
int sem_post(sem_t *sem);
参数说明
返回值
- 如果函数执行成功,返回 0。
- 如果发生错误,返回 -1。可以通过 errno 变量来获取具体的错误信息。
无名信号量
无名信号量不会在文件系统中创建创建 IPC 名字,因此仅限于创建该信号量的进程中使用,用于多线程间的同步和互斥。不同进程之间的无名信号量是不能直接访问的。
无名信号量的使用流程:
- 使用 sem_init() 函数来创建无名信号量。
- 使用无名信号量,通过sem_wait() 函数来获取信号量,并执行相应的操作。当线程完成所需操作后,可以使用 sem_post() 函数释放信号量。
- 销毁无名信号量:在不再需要无名信号量时,可以使用 sem_destroy() 函数来删除信号量,并释放相关的系统资源。
创建无名信号量-sem_init() 函数
sem_init() 函数用于初始化无名信号量,该函数定义如下:
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
参数说明
- sem:指向需要初始化的信号量的指针。初始化成功后,该指针将指向一个有效的信号量对象。
- pshared:指定是否将信号量用于多个进程间的共享。取值如下:
- 0:该信号量仅在当前进程中有效。
- 非0:信号量可在不同进程间共享。在多个进程间共享信号量时,需要保证它们有同一个 sem 地址,通常使用共享内存来实现。
- value:指定信号量的初始值。
返回值
- 如果函数执行成功,返回 0。
- 如果发生错误,返回 -1。可以通过 errno 变量来获取具体的错误信息。
销毁信号量-sem_destroy() 函数
sem_destroy() 函数用于销毁无名信号量,释放信号量占用的系统资源。该函数定义如下:
#include <semaphore.h>
int sem_destroy(sem_t *sem);
参数说明
sem:指向需要销毁的信号量的指针。
返回值
- 如果函数执行成功,返回 0。
- 如果发生错误,返回 -1。可以通过 errno 变量来获取具体的错误信息。
在销毁信号量之前,应该确保该信号量不再被使用,所有线程都已经释放资源。一旦信号量被销毁,其行为将变得不可预测。