信号量

  信号量的概念

         信号量广泛用于进程或线程间的同步和互斥, 信号量本质上是一个非负的整数计数器, 它被用来控制对公共资源的访问

        编程时可根据操作信号量值的结果判断是否对公共资源具有访问的权限, 当信号量值大于0 时, 则可以访问,否则将阻塞

       PV原语是对信号量的操作,一次P操作使信号量 sem 减1,一次V 操作使信号量 sem 加1

  信号量主要用于进程或线程间的同步和互斥这两种典型情况

      若用于互斥, 几个进程( 或线程) 往往只设置一个信号量

      若用于同步操作, 往往会设置多个信号量, 并且安排不同的初始值, 来实现它们之间的执行顺序

  信号量用于互斥

信号量用于同步

信号量的操作

  信号量的初始化

#include <semaphore.h>

/*
 *功能:
 *  创建一个信号量并初始化它的值
 *参数:
 *  sem: 信号量的地址
 *  pshared:
 *    = 0,信号量在线程间共享
 *    != 0,信号量在进程间共享
 *  value:信号量的初始值
 *return:
 *  成功:0
 *  失败:-1
 */
int sem_init(sem_t *sem, int pshared, unsigned int value);

信号量 P 操作

#include <semaphore.h>

/*
 *功能:
 *  将信号量的值减 1,若信号量的值小于 0,此函数会引起调用者阻塞
 *参数:
 *  sem:信号量地址
 *return:
 *  成功: 0
 *  失败: -1
 */
int sem_wait(sem_t *sem);
#include <semaphore.h>

/*
 *功能:
 *  将信号量的值减 1
 *  若信号量的值小于 0,则对信号量的操作失败,函数立即返回
 *参数:
 *  sem:信号量地址
 *return:
 *  成功:0
 *  失败:-1
 */
int sem_trywait(sem_t *sem);

信号量的 V 操作

#include <semaphore.h>

/*
 *功能:
 *  将信号量的值加 1,发出信号唤醒等待线程
 *参数:
 *  sem:信号量地址
 *return:
 *  成功:
 *  失败:
 */
int sem_post(sem_t *sem);

获取信号量的计数值

#include <semaphore.h>

/*
 *功能:
 *  获取sem标识的信号量的值,保存在 sval 中
 *参数:
 *  sem:信号量地址
 *  sval:保存信号量值的地址
 *return:
 *  成功: 0
 *  失败:-1
 */
int sem_getvalue(sem_t *sem, int *sval);

信号量的销毁

#include <semaphore.h>

/*
 *功能:
 *  删除 sem 标识的信号量
 *参数:
 *  sem:信号量地址
 *return:
 *  成功:0
 *  失败:-1
 */
int sem_destroy(sem_t *sem);

信号量实现互斥功能, 模拟打印机

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <semaphore.h>

sem_t sem;

int main(void)
{
    pthread_t tid1, tid2;
    sem_init(&sem, 0, 1);
    pthread_create(&tid1, NULL, thread_fun1, NULL);
    pthread_create(&tid2, NULL, thread_fun2, NULL);

    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);

    return 0;
}

void printer(char *str)
{
    sem_wait(&sem);
    while(*str)
    {
        putchar(*str);
        fflush(stdout);
        str++;
        sleep(1);
    }
    sem_post(&sem);
}

void *thread_fun1()
{
    char *str1 = "hello";
    printer(str1);
}

void *thread_fun2()
{
    char *str2 = "world";
    printer(str2);
}

验证信号量实现同步功能

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>


//定义两个信号量
sem_t sem_g, sem_p;
char ch = 'a';
void *pthread_p(void *arg);
void *pthread_g(void *arg);

int main(int argc, char *argv[])
{
    pthread_t tid1, tid2;
    //初始化信号量
    sem_init(&sem_g, 0, 0);
    sem_init(&sem_p, 0, 1);

    pthread_create(&tid1, NULL, pthread_g, NULL);
    pthread_create(&tid2, NULL, pthread_p, NULL);

    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);

    return 0;
}

//此线程改变字符 ch 的值
void *pthread_g(void *arg)
{
    while(1)
    {
        sem_wait(&sem_g);
        ch++;
        sleep(1);
        sem_post(&sem_p);
    }
}

void *pthread_p(void *arg)
{
    while(1)
    {
        sem_wait(&sem_p);
        printf("%c",ch);
        fflush(stdout);
        sem_post(&sem_g);
    }
}

有名信号量

  有名信号量概念

     POSIX 的信号量:
         无名信号量
         有名信号量

     无名信号量一般用于线程间同步或互斥

      有名信号量一般用于进程间同步或互斥

有名信号量的打开或创建

#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>

/*
 *功能:
 *  创建一个信号量
 *参数:
 *  name:信号量文件名
 *  flags:sem_open 函数的行为标志
 *  mode:文件权限(可读、可写、可执行)的设置
 *  value:信号量初始值
 *return:
 *  成功:信号量的地址
 *  失败:SEM_FAILED
 */
//当信号量存在时使用
sem_t *sem_open(const char *name, int oflag);

//当信号量不存在时使用
sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value);

信号量的关闭

#include <semaphore.h>

/*
 *功能:
 *  关闭有名信号量
 *参数:
 *  sem:指向信号量的指针
 *return:
 *  成功: 0
 *  失败: -1
 */
int sem_close(sem_t *sem);

信号量的删除

#include <semaphore.h>

/*
 *功能:
 *  删除信号量的文件
 *参数:
 *  name:信号量文件名
 *return:
 *  成功: 0
 *  失败: -1
 */
int sem_unlink(const char *name);