第十章 Posix信号量

知乎讨论

通俗的描述信号量,是这么个东西:
每个信号量拥有一个计数器,sem_post()可以使该计数器+1V操作),sem_wait()在计数器不为0时,可以使该计数器-1,否则阻塞直到不为0(P操作)。
而该计数器的存在,就可以限定某个容器中元素的个数。

如果单纯的看效果,互斥量+条件变量可以实现计数器为1的信号量。

优缺点

UNP卷二

作者对于信号量的评价为:

  1. 信号量默认就是进程间共享
    对同一个信号量,加锁和解锁可以在不同的进程或是进程进行。
    而互斥量只能有是由加锁线程解锁,且默认不再进程间共享。
  2. 信号量有与之关联的一个值,计数器
    该值在信号量结构体内部维护。即使消费者还没有阻塞在sem_wait(),该值也会递增。
    相比与条件变量,如果消费者还没阻塞在pthread_cond_wait(),那么由pthread_cond_signal()发送的信号就会丢失,没有函数去处理。
  3. 相比于其他锁,只有sem_port()是异步信号安全函数
    异步信号安全函数指的是:可以在信号处理函数中调用的函数。

muduo陈硕

  1. 信号量不是必备的锁
    使用条件标量和互斥量可以完全代替信号量的功能
  2. 信号量的计数器是个累赘
    程序的容器有自己的长度,而信号量又自带一个,这样造成了同样的信息维护两份,需要时刻保持一致。增大了程序员的负担和出错的可能。
  3. 同时建议使用单独一个线程去分配任务
    其他线程使用条件变量阻塞

个人见解

没有项目经验,不保证全对

  1. 不同线程间可解锁
    这还真可能是个累赘。
  2. 确实可以使用条件变量+互斥量去代替
  3. 异步信号安全函数
    没用过,不知道啊。书上也将,可以在函数内向管道内写东西的方式来激活一个别的线程,解决这些问题。

Posix 信号量

Posix信号量不必在内核中维护。

有名信号量

这个信号量有名字啦,根据这个名字就可以在不同的进程间使用。

这个名字可以时文件系统中对应的名字来表示。

Api

打开/创建有名信号量

#include 
sem_t sem;
sem_t *sem_open(const char *pathfile, int flag/, mode_t mode ,unsigned int value/);

打开一个信号量
flag:可以时0,主要使用来,当指定的文件不存在时,使用O_CREATE | O_EXEL新建,如果指定为0,后两个参数可省略,否则后面两个参数需要带上。
mode:文件权限。
value:信号量初始的值,这这个参数只有在新创建的时候才需要设置,如果是打开已有的,不需要在去指定,否则会报错。该值不超过是SEM_VALUE_MAX(至少是32767)。
取消对信号量的使用

#include 
int sem_close(sem_t *sem);
//成功返回0,否则返回-1

这个函数只是声明,在这个进程中不再使用这个信号量,并不是去析构该信号量。
同时进程结束的时候,无论是正常还是信号中断退出进程,内核都会主动调用该函数去关闭进程使用的信号量。
即使都没有后进程在使用这个信号量了,内核也会维持这个信号量。
也就是说,Posix的信号量是随着内核持续的。
主动析构信号量

#include 
int sem_unlink(const char *name);
//成功返回0,否则-1

对打开的信号量的路径执行,直接删除指定的文件,但是并不析构该信号量。当最后一个使用该信号量的进程调用sem_close()对该信号量的时候,内核析构该信号量。

P操作

#include 
int sme_wait(sem_t *);
int sem_trywait(sem_t *);
//成功返回0 ,否则-1

sem_wait()测试指定的信号量,如果计数器值大于0,那么递减计数器,并立即返回,否则,阻塞在计数器为0。直到不是0时,递减并返回。如果因为信号中断,则过返回EAGAIN错误。
sem_trywait()函数,如果指定信号量的计数器为0,那么直接返回EAGAIN错误。

为什么阻塞递减叫做P操作?源自荷兰单词proberen意思是尝试。
V操作

#include 
int sme_post(sem_t *);
//成功返回0 ,否则-1

递增指定的信号量计数器+1
同样因为荷兰单词verhogen增加的意思。

当前计数器的值

#include 
int sem_getvalue(sem_t *,int *);
//成功返回0 ,否则-1

传入两个参数,第二个参数是需要填充的。
如果当前信号量已经上锁,也就是有线程在等待,那么该值为0或者是负数,而如果是负数,那么表示等待该信号量解锁的线程数。

信号量死锁

主要出现在,如果同时使用两个信号量,那么顺序一定不能出错。

基于内存信号量

不使用文件系统标识,直接存在程序运行的内存中,(非共享内存)。
这样就造成了,不同进程之间不能访问,不能用于不同进程之间相互访问。
一个父进程初始化一个信号量,然后fork其副本得到的是该信号量的副本,这两个信号量之间并不存在关系。
初始化

#include 
int sem_init(sem_t *sem,int shared,unsignel int value);
//出错返回-1,成功的不一定。
int sem_destory(sem_t *);
//成功0,失败-1;

第二个参数,表示是否在进程件共享,在这种实现中不可能进程间共享,需要基于共享内存的信号量,才可以。
sem_init()需要用户自己创建一个结构体(不是指针)。然后使用该函数去初始化结构体。
sem_open()则可以直接返回一个指针,并不需要去创建这个结构体。

基于共享内存的信号量

和共享内存有关了

你可能感兴趣的:(第十章 Posix信号量)