美文网首页
第十章 Posix信号量

第十章 Posix信号量

作者: Myth52125 | 来源:发表于2017-10-02 19:57 被阅读0次

    知乎讨论

    通俗的描述信号量,是这么个东西:
    每个信号量拥有一个计数器,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 <semaphore.h>
    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 <semaphore.h>
    int sem_close(sem_t *sem);
    //成功返回0,否则返回-1
    

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

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

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

    P操作

    #include <semaphore.h>
    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 <semaphore.h>
    int sme_post(sem_t *);
    //成功返回0 ,否则-1
    

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

    当前计数器的值

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

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

    信号量死锁

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

    基于内存信号量

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

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

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

    基于共享内存的信号量

    和共享内存有关了

    相关文章

      网友评论

          本文标题:第十章 Posix信号量

          本文链接:https://www.haomeiwen.com/subject/nqiaextx.html