IPC

Posix 信号量基础

2019-12-18  本文已影响0人  madao756

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

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

0X00 信号量的感性认识

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

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

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

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

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

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

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

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

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

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

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

#include <semaphore.h>

int sem_close(sem_t *sem);

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

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

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

#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

#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

上一篇下一篇

猜你喜欢

热点阅读