Linux信号量详解

一、什么是信号量

信号量的本质是一种数据操作锁、用来负责数据操作过程中的互斥、同步等功能。

信号量就和互斥锁类似,本质上就是一个计数器(全局变量)。不同之处在于,互斥锁的值只有 2 个(加锁 "lock" 和解锁 "unlock"),而信号量的值可以根据实际场景的需要自行设置(取值范围为 ≥0)。当有进程对它所管理的资源进行请求时,进程先要读取信号量的值,大于0,资源可以请求,等于0,资源不可以用,这时进程会进入睡眠状态直至资源可用。当一个进程不再使用资源时,信号量+1(对应的操作称为V操作),反之当有进程使用资源时,信号量-1(对应的操作为P操作)。对信号量的值操作均为原子操作

二、信号量的分类

根据初始值的不同,信号量可以细分为 2 类,分别为二进制信号量和计数信号量:

  • 二进制信号量:指初始值为 1 的信号量,此类信号量只有 1 和 0 两个值,通常用来替代互斥锁实现线程同步;

  • 计数信号量:指初始值大于 1 的信号量,当进程中存在多个线程,但某公共资源允许同时访问的线程数量是有限的(出现了“狼多肉少”的情况),这时就可以用计数信号量来限制同时访问资源的线程数量。

根据使用场景的不通,可以分为两类:无名信号量命名信号量

  • 无名信号量只可以在共享内存的情况下,比如实现进程中各个线程之间的互斥和同步,因此无名信号量也被称作基于内存的信号量;

  • 命名信号量通常用于不共享内存的情况下,比如进程间通信

三、信号量的api

3.1 无名信号量

信号量的函数都以sem_开头,线程中使用的基本信号函数有4个,他们都声明在头文件semaphore.h中,该头文件定义了用于信号量操作的sem_t类型:

【sem_init】:

该函数用于创建信号量,原型如下:

intsem_init(sem_t *sem,int pshared,unsignedint value);

该函数初始化由sem指向的信号对象,设置它的共享选项,并给它一个初始的整数值。pshared控制信号量的类型,如果其值为0,就表示信号量是当前进程的局部信号量,否则信号量就可以在多个进程间共享,value为sem的初始值。

该函数调用成功返回0,失败返回-1。

【sem_destroy】:

该函数用于对用完的信号量进行清理,其原型如下:

intsem_destroy(sem_t *sem);

成功返回0,失败返回-1。

【sem_wait函数】:

该函数用于以原子操作的方式将信号量的值减1。原子操作就是,如果两个线程企图同时给一个信号量加1或减1,它们之间不会互相干扰。其原型如下:

intsem_wait(sem_t *sem);
intsem_trywait(sem_t *sem);
intsem_timedwait(sem_t *sem, conststructtimespec*abs_timeout);

sem指向的对象是sem_init调用初始化的信号量。调用成功返回0,失败返回-1。

sem_trywait()则是sem_wait()的非阻塞版本,当条件不满足时(信号量为0时),该函数直接返回EAGAIN错误而不会阻塞等待。

sem_timedwait()功能与sem_wait()类似,只是在指定的abs_timeout时间内等待,超过时间则直接返回ETIMEDOUT错误。abs_timeout是UTC时间戳

https://www.cnblogs.com/coding-my-life/p/4085336.html

【sem_post函数】:

该函数用于以原子操作的方式将信号量的值加1,其原型如下:

intsem_post(sem_t *sem);

与sem_wait一样,sem指向的对象是由sem_init调用初始化的信号量。调用成功时返回0,失败返回-1。

【sem_getvalue函数】:

该函数返回当前信号量的值,通过restrict输出参数返回。如果当前信号量已经上锁(即同步对象不可用),那么返回值为0,或为负数,其绝对值就是等待该信号量解锁的线程数。

intsem_getvalue(sem_t *restrict,int*restrict);

3.2 有名信号量

之所以称为命名信号量,是因为它有一个名字、一个用户ID、一个组ID和权限。这些是提供给不共享内存的那些进程使用命名信号量的接口。命名信号量的名字是一个遵守路径名构造规则的字符串。

【sem_open函数】:

该函数用于创建或打开一个命名信号量,其原型如下:

sem_t *sem_open(constchar*name,int oflag);
sem_t *sem_open(constchar*name,int oflag,
                mode_t mode,unsignedint value);

参数name是一个标识信号量的字符串。参数oflag用来确定是创建信号量还是连接已有的信号量。

oflag的参数可以为0,O_CREAT或O_EXCL:如果为0,表示打开一个已存在的信号量;如果为O_CREAT,表示如果信号量不存在就创建一个信号量,如果存在则打开被返回,此时mode和value都需要指定;如果为O_CREAT|O_EXCL,表示如果信号量存在则返回错误。

mode参数用于创建信号量时指定信号量的权限位,和open函数一样,包括:S_IRUSR、S_IWUSR、S_IRGRP、S_IWGRP、S_IROTH、S_IWOTH。

value表示创建信号量时,信号量的初始值。

【sem_close函数】:

该函数用于关闭命名信号量:

intsem_close(sem_t *);

单个程序可以用sem_close函数关闭命名信号量,但是这样做并不能将信号量从系统中删除,因为命名信号量在单个程序执行之外是具有持久性的。当进程调用_exit、exit、exec或从main返回时,进程打开的命名信号量同样会被关闭。

【sem_unlink函数】:

sem_unlink函数用于在所有进程关闭了命名信号量之后,将信号量从系统中删除:

intsem_unlink(constchar*name);

【信号量操作函数】:

与无名信号量一样,操作信号量的函数如下:

intsem_wait(sem_t *sem);
intsem_trywait(sem_t *sem);
intsem_timedwait(sem_t *sem,conststructtimespec*abs_timeout);
intsem_post(sem_t *sem);
intsem_getvalue(sem_t *restrict,int*restrict);

ref:

http://c.biancheng.net/view/8632.html

https://www.jianshu.com/p/6e72ff770244

猜你喜欢

转载自blog.csdn.net/wwwlyj123321/article/details/129375900