Posix 信号量基础

前言:书上这一章的内容真多,一点点总结,废话不多说,让我们开始

今天不开车,拿停车场举个例子

0X00 信号量的感性认识

之前我们学了「互斥锁」和「条件变量」,互斥锁只让某一块代码被一个多线程的一个线程执行,而条件变量可以让线程阻塞,然后一旦某个条件满足了,还可以被唤醒

今天学习的信号量是两者的结合,可用在单纯上锁,还可用在等待唤醒

在知乎上看到了一个信号量的解释,描述的很有画面感:

信号量就是停车场还能停车子多少车子,它的最大值:该停车场能停多少车子,它的当前值:如果当前值大于 0,那么是停车场还能停多少车子,如果当前值等于 0,那么代表停车场已经满了,就该去排队了

信号量有两个基本操作:等待(wait)和挂出(post)

  1. 等待(wait)的意思就是,有一个车子要进来了,停车场还能停车子,值减 1。如果停车场满了,值等于 0 了,那么车子就要等待,直到大于 0,可以停车子了,然后值再减去 1
  1. 挂出(post)的意思就是,有一个车子要走了,停车场多了一个位子,该值加 1,让一个等待的车子进来

如果没有读懂我说的这个例子,建议阻塞一下,好好理解一下信号量是个啥

说完了感性的理解,我们来看一下,「信号量」在 POSIX 标准下的分类

  • 有名信号量:使用 POSIX IPC 名字标识,可用于进程或线程间的同步
  • POSIX 基于内存的信号量:存放在共享内存中,可用于进程或线程间的同步

0X01 信号量部分相关函数解释

接下来我们介绍与「有名信号量」有关的函数

  • sem_open 用于创建一个新的或打开一个已经存在的有名信号量
sem_t *sem_open(const char *name, int oflag);
sem_t *sem_open(const char *name, int oflag,
                mode_t mode, unsigned int value);

更详细的资料见:http://man7.org/linux/man-pages/man3/sem_open.3.html

  • sem_close 用于关闭 sem_open 打开的信号量
#include <semaphore.h>

int sem_close(sem_t *sem);

一个进程终止时,内核对其上仍打开的信号量进行关闭。但是关闭不意味着删去

所以有名信号量是随内核持续的,关闭不影响它的值

更详细的资料见:http://man7.org/linux/man-pages/man3/sem_close.3.html

  • sem_waitsem_trywait 用于测试信号量的值
#include <semaphore.h>

int sem_wait(sem_t *sem);

int sem_trywait(sem_t *sem);

sem_t 是一个结构体,里面有一个成员记录信号量的值,我们把这个成员叫做 data 吧

如果 semaphore 的 data 大于 0,那就不阻塞减 1 后立即返回;如果 semaphore 的 data 等于 0,那么就会阻塞,等待唤起以后(也就是 semaphore 的 data 大于 0 )以后,再减 1 返回。

sem_trywait 与 sem_wait 最大的不同就是,它不会阻塞,而是返回一个错误 EINTR

更多详细的资料见:http://man7.org/linux/man-pages/man3/sem_timedwait.3.html

  • sem_post 和 sem_getvalue
#include <semaphore.h>
int sem_post(sem_t *sem);

当一个线程使用完某个信号量的时候,它就应该调用 sem_post 把 semaphore 的 data 加 1,然后唤起那些由于之前 data 为 0 阻塞的进程、线程

更多详细的资料见:http://man7.org/linux/man-pages/man3/sem_post.3.html

#include <semaphore.h>
int sem_getvalue(sem_t *sem, int *sval);

得到信号量的值,小于 0 的绝对值就是等待该信号量解锁的线程数

更多详细的资料见:http://man7.org/linux/man-pages/man3/sem_getvalue.3.html

0X02 程序举例

用「信号量」实现环形缓冲区的生产者消费者问题

书上的程序稍微改了改

/* include main */
#include "unpipc.h"

#define NBUFF 100
#define SEM_MUTEX "/tmp/mutex"
#define SEM_NEMPTY "/tmp/nempty"
#define SEM_NSTORED "/tmp/nstored"

int nitems; /* read-only by producer and consumer */
struct
{ /* data shared by producer and consumer */
    int buff[NBUFF];
    sem_t *mutex, *nempty, *nstored;
} shared;

void *produce(void *), *consume(void *);

int main(int argc, char **argv)
{
    pthread_t tid_produce, tid_consume;

    if (argc != 2)
        printf("usage: prodcons1 <#items>");
        exit(0);
    nitems = atoi(argv[1]);

    /* 4create three semaphores */
    shared.mutex = sem_open(SEM_MUTEX, O_CREAT | O_EXCL,
                            FILE_MODE, 1);
    shared.nempty = sem_open(SEM_NEMPTY, O_CREAT | O_EXCL,
                             FILE_MODE, NBUFF);
    shared.nstored = sem_open(SEM_NSTORED, O_CREAT | O_EXCL,
                              FILE_MODE, 0);

    /* 4create one producer thread and one consumer thread */
    pthread_setconcurrency(2);
    pthread_create(&tid_produce, NULL, produce, NULL);
    pthread_create(&tid_consume, NULL, consume, NULL);

    /* 4wait for the two threads */
    pthread_join(tid_produce, NULL);
    pthread_join(tid_consume, NULL);

    /* 4remove the semaphores */
    sem_unlink(SEM_MUTEX);
    sem_unlink(SEM_NEMPTY);
    sem_unlink(SEM_NSTORED);
    exit(0);
}
/* end main */

/* include prodcons */
void *
produce(void *arg) {
    int i;
    for (i = 0; i < nitems; i++) {
        sem_wait(shared.nempty); /* wait for at least 1 empty slot */
        sem_wait(shared.mutex);
        shared.buff[i % NBUFF] = i; /* store i into circular buffer */
        sem_post(shared.mutex);
        sem_post(shared.nstored); /* 1 more stored item */
    }
    return (NULL);
}

void *
consume(void *arg) {
    int i;

    for (i = 0; i < nitems; i++) {
        sem_wait(shared.nstored); /* wait for at least 1 stored item */
        sem_wait(shared.mutex);
        if (shared.buff[i % NBUFF] != i)
            printf("buff[%d] = %d\n", i, shared.buff[i % NBUFF]);
        sem_post(shared.mutex);
        sem_post(shared.nempty); /* 1 more empty slot */
    }
    return (NULL);
}

原理就是有三个信号量,一个用来互斥,一个用来判断是不是为空,一个判断是不是为满

用「信号量」给文件上锁

也是书上的程序改了改:

#include "unpipc.h"
#define LOCK_PATH "/tmp/pxsemlock"


sem_t   *locksem;
int     initflag;

void my_lock(int fd)
{
    if (initflag == 0) {
        locksem = sem_open(LOCK_PATH, O_CREAT, FILE_MODE, 1);
        initflag = 1;
    }
    sem_wait(locksem);
}

void my_unlock(int fd)
{
    sem_post(locksem);
}

上的锁是文件劝告锁

头文件的地址:https://github.com/TensShinet/learn_IPC/blob/master/my_code/semaphore/unpipc.h

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,902评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,037评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,978评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,867评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,763评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,104评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,565评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,236评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,379评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,313评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,363评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,034评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,637评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,719评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,952评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,371评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,948评论 2 341

推荐阅读更多精彩内容