Linux POSIX 信号量 命名信号量与未命名信号量

Linux POSIX 信号量 命名信号量与未命名信号量


注意:
1、Linux信号量是一个递加递减的正整数,而不是ON、OFF的bool量。
2、Linux信号量数值为0时,尝试去递减该信号量,则会阻塞进程或者线程,直到某处让该信号量递加一次,才能触发被阻塞的递减操作。
POSIX信号量分为两种:
命名信号量:这种信号量有一个名字,不相关的进程可以通过名字访问同一个信号量,通过sem_open()函数调用;
未命名信号量:这种信号量没有名字,它位于内存中预定的位置(全局变量、静态变量等),未命名信号量主要在线程间共享。也可以在进程间共享,但是该信号量必须处于 共享内存区域

信号量的基本使用流程如下:

//创建命名(未命名)信号量 ---> 递增信号值 ---> 递减信号值  ---> 获取当前信号值  ---> 删除信号量与进程间的关联关系  ---> 删除该信号量
sem_open()/sem_init() ---> sem_post() ---> sem_wait() ---> sem_getvalue() ---> sem_close()                ---> sem_unlink()

命名信号量的创建

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

/*
 *  @Description: 创建一个新的命名信号量 或 打开一个既有信号量
 *  @Para       : const char * name         信号量的名称
 *                int oflag                 位掩码  等于0则为打开一个既有信号量  为O_CREAT则创建一个信号量
 *                mode_t mode               (创建信号量时)位掩码 可以设置信号量的文件权限,如S_IRUSR S_IWUSR S_IXUSR等
 *                unsigned int value        (创建信号量时)指定信号量的初始信号值。
 *  @return     : 成功返回创建或者打开的信号量指针,失败返回SEM_FAILED
**/
sem_t * sem_open(const char * name, int oflag /* , mode_t mode, unsigned int value */);

//例:
if(sem_open("/mysem", O_CREAT | O_EXCL, 0644, 0) == SEM_FAILED)
    printf("sem creat failed!\r\n");

注意:

  1. 信号量的名称参数name,必须以斜线开头,后面跟着一个或多个非斜线字符的名字,如 “/mysem”, 名称的最长字符数为 NAME_MAX(255) - 4;
  2. 当int oflag = O_CREAT | O_EXCL 时,如果信号量不存在则创建,如果存在,则函数调用失败返回;
  3. 如果sem_open()用于打开一个既有信号量,则仅适用前两个参数即可。如果是创建新信号量,则可以通过mode_t mode设置新创建的信号量的文件权限(Owner Group Other, Read Write Execute等),通过 unsigned int val可以设置信号量的初值。
  4. 在sem_open()返回的sem_t * 指针指向的变量的副本进行操作,结果是未知的,应禁止该行为。
    //错误代码,禁止该类操作
    sem_t * sp;
    sem_t sem2;
    sp = sem_open(...);
    sem2 = *sp;
    sem_wait(&sem2);
  1. 当打开或者创建命名信号量时,会自动建立进程与命名信号量间的关联关系;

初始化一个未命名信号量

#include <semaphore.h>

/*
 *  @Description: 初始化一个未命名信号量
 *  @Para       : sem_t *sem            待初始化的信号量指针
 *                int pshared           线程共享、进程共享区分,等于0则为线程间共享,不为0则为进程间共享
 *                unsigned int value    指定信号量的初始信号值。
 *  @return     : 成功返回0,失败返回-1
**/
int sem_init(sem_t *sem, int pshared, unsigned int value);

//例:
sem_t mysem;    //创建信号量
if(sem_init(&mysem, 0, 0) == -1)    //初始化信号量
    printf("sem_init failed!\r\n");

注意:

  1. int pshared 等于0则为线程间共享,不为0则为进程间共享,如果是进程间共享,则该信号量必须是共享内存区域;
  2. 对一个已经初始化的未命名信号量进行初始化操作将会导致未定义的行为,要确保所有的进程、线程中只进行过一次sem_init()信号量初始化操作;
  3. 对信号量的副本进行操作,结果是未定义的。也就是说,总是只能在最初的信号量上执行操作。

发布信号量和等待信号量

#include <semaphore.h>

/*
 *  @Description: 发布一个信号量,及信号量数值加一
 *  @Para       : sem_t *sem            要发布的信号量
 *  @return     : 成功返回0,失败返回-1
**/
int sem_post(sem_t *sem);

/*
 *  @Description: 等待一个信号量,及信号量数值减一,如果等待的信号量值为0,
 *                则阻塞进程,直到某处使用sem_post()
 *  @Para       : sem_t *sem            等待的信号量
 *  @return     : 成功返回0,失败返回-1
**/
int sem_wait(sem_t *sem);

/*
 *  @Description: 尝试等待一个信号量,如果等待的信号量值为0,不阻塞进程,立即失败返回。
 *  @Para       : sem_t *sem            等待的信号量
 *  @return     : 成功返回0,失败返回-1
**/
int sem_trywait(sem_t *sem);

/*
 *  @Description: 等待一个信号量,如果等待的信号量值为0,则阻塞进程。
 *                直到某处使用sem_post(),或者到达abs_timeout指定的绝对时间。
 *  @Para       : sem_t *sem            等待的信号量
 *                const struct timespec * abs_timeout   超时的绝对时间
 *  @return     : 成功返回0,失败返回-1
**/
int sem_timedwait(sem_t *sem, const struct timespec * abs_timeout);

注意:

  1. 如果同一时刻有多个进程、线程在使用sem_wait()阻塞的等待一个信号,当使用sem_post()发布一个信号时,默认的SCHED_OTHER调度机制下,哪个进程先被唤醒时不确定的。如果使用的是SCHED_RR或SCHED_FIFO调度机制,则最先被唤醒的进程是优先级最高、等待时间最长的进程;
  2. sem_timedwait()的abs_timeout时间参数是一个绝对时间参数,如果需要设置一个相对时间,如5s后结束等待,使用clock_gettime()获取CLOCK_REALTIME当前绝对时间值,并在此基础上加上5s,赋值到abs_timeout;

获取信号量的当前值

信号量的值是一个正整数,当没有sem_wait()等待信号时,使用sem_post()多次发布信号,则会使信号量当前值一直累加,等有sem_wait()时,则递减一。信号量值可以记录信号发送的次数,不遗漏已发送的信号。

#include <semaphore.h>

/*
 *  @Description: 获取信号量的当前值。
 *  @Para       : sem_t *sem   等待的信号量
 *                int * sval   信号量当前值
 *  @return     : 成功返回0,失败返回-1
**/
int sem_getvalue(sem_t * sem, int * sval);

注意:

  1. 当信号量的值为正数时,则正常返回信号量的值,但当信号量的值为零,且此时有多个sem_wait()在等待该信号量时,sval的具体返回值依赖于系统实现,Linux系统直接返回0,而其它一些系统可能返回绝对值等于sem_wait()中阻塞等待数目的负数。
  2. 使用sem_getvalue()获得信号量数目具有较短的时效性,因为可能刚获取信号量数目,就被其它线程打断,在其它线程中发送了信号量,当再次返回原线程时,已经取出的信号量值并不是真实值;

关闭一个命名信号量

该函数仅用于命名信号量,当进程打开一个命名信号量时,系统会记录命名信号量与进程间的关联关系,使用sem_close()可以断开其关联关系。

#include <semaphore.h>

/*
 *  @Description: 关闭命名信号量与进程的关联关系
 *  @Para       : sem_t *sem   等待的信号量
 *                int * sval   信号量当前值
 *  @return     : 成功返回0,失败返回-1
**/
int sem_close(sem_t * sem);

删除一个命名信号量

该函数仅用于命名信号量,把该信号量标记为当所有的进程与命名信号量的关联关系都被关闭时,可以销毁该信号量。如果信号量没有与进程的关联关系,则立刻删除。反之则等待所有关联关系关闭再删除。

#include <semaphore.h>

/*
 *  @Description: 删除一个命名信号量
 *  @Para       : const char * name   命名信号量命称
 *  @return     : 成功返回0,失败返回-1
**/
int sem_unlink(const char * name);

销毁一个未命名信号量

该函数仅用于未命名信号量,只有在不存在线程、进程使用sem_wait()等待该信号量时,才能安全的销毁该信号量。

#include <semaphore.h>

/*
 *  @Description: 摧毁一个未命名信号量
 *  @Para       : sem_t * sem   未命名信号量
 *  @return     : 成功返回0,失败返回-1
**/
int sem_destroy(sem_t * sem);

注意:

  1. 其参数sem_t * sem 必须是是一个未命名信号量,并且已经被sem_init()初始化过。
  2. 在一些实现上,省略sem_destroy()调用不会导致问题的发生,但在其它实现上,不调用sem_destroy()会导致资源泄露,为保持可移植性,应调用sem_destroy()。
  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值