Linux进程间通信三 System V 信号量简介与示例

1. System V信号量简介

SystemV信号量主要用于解决生产者和消费者问题,一个信号量能够控制多个资源,说它是信号量集也不为过。

2. API接口介绍

2.1 创建或打开信号量集

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>


/**
* @brief 创建信号量或者返回已存在的信号量
*
* @params key 与信号量关联的key,三种生成方式,包括IPC_PRIVATE,注意key和信号量不是绑定关系,相同的key产生的标识符不一定相同,比如系统重启的情况
* @params nsems 集合中信号量的个数
* @params shmflg 标志位和权限控制标志位,可以多个用or运算。IPC_CREAT、 IPC_EXCL
*
* @returns 成功返回信号量集标识符,失败返回-1
*/

int semget(key_t key, int nsems, int semflg);

2.2 操作信号量集

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

/**
* @brief 操作信号量集
*
* @params semid 信号量集标识符
* @params sops 具体的操作行为,指向一个数组,每个元素都是sembuf结构,该结构指定了要操作哪个信号量以及相应的值等。
*
* @params nsops 表名sembuf的数量
*
* @returns 成功返回0,失败返回-1,注意这里的操作是原子性的,如果操作多个信号量,要么全部成功,要么什么都不做
*/

int semop(int semid, struct sembuf *sops, size_t nsops);

struct sembuf {
    unsigned short int sem_num;    // 指定操作信号量的下标,从0开始
    short sem_op;     // 信号量的值
    short sem_flg;    // 操作标志位,可选 IPC_NOWAIT and SEM_UNDO,前者表示非阻塞,后者标志当进程退出后,系统自动恢复对这个信号量的操作。
};


2.3 控制操作

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

/**
* @brief 操作信号量集
*
* @params semid 信号量集标识符
* @params semnum 要操作的信号量序号,下标从0开始
* @params cmd 控制行为,可选值IPC_STAT,IPC_RMID, IPC_SET, IPC_INFO
* @params 可选参数,这里的参数结构体需要用户自定义。
* @returns 不同的cmd返回不通值
*/


int semctl(int semid, int semnum, int cmd, ...);

// 可选参数,需要用户自定义
union semun {
    int              val;    /* Value for SETVAL */
    struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
    unsigned short  *array;  /* Array for GETALL, SETALL */
    struct seminfo  *__buf;  /* Buffer for IPC_INFO (Linux-specific) */
};

3. 示例

下面是信号量集的一个简单示例,分为生产者和消费者,其中生产者生产资源,消费者消费资源

生产者:

/*
**  Name: sem_wait.c
**  Desc: 生产者
**  Author: masonf
**  Date: 20200626
*/

#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

// 自定义结构体
union semun
{
    int val;
    struct semid_ds *buf;
    unsigned short *array;
    struct seminfo *__buf;
};

int main()
{
    int ret = 0;
    key_t key = 10240;
    int sem_id;

    // 创建或获取信号量集,其中包含一个信号量,这里key是固定的
    sem_id = semget(key, 1, IPC_CREAT);
    if (sem_id == -1)
    {
        perror("create sem error");
        return -1;
    }


    // 设置第0个信号量,资源数加1
    struct sembuf sem_operation[1]={0};
    sem_operation[0].sem_op=1;
    sem_operation[0].sem_num = 0;

    // 初始化信号量的值为1
    if (semop(sem_id, sem_operation, 1) == -1)
    {
        perror("init sem error");
        // 初始化失败则删除信号量集
        semctl(sem_id, 1, IPC_RMID);
        return -1;
    }

    int food_nums = 1;
    while (food_nums < 5)
    {
        printf("start producing foods\n");

        // 生产食物
        sem_operation[0].sem_num=0; // 操作第一个信号量,下标从0开始
        sem_operation[0].sem_op = 1;
        while (semop(sem_id, sem_operation, 1) == -1)
        {
            printf("producing error, continue\n");
            continue;
        }
        ++food_nums;
    }

    // 删除信号量集
    //semctl(sem_id, 1, IPC_RMID);
    printf("work done, Go home!\n");
    return 0;    
}

消费者

/*
**  Name: sen_wait.c
**  Desc: 消费者    
**  Author: mason
**  Date: 20200626
*/

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

// 自定义结构体
union semun
{
    int val;
    struct semid_ds *buf;
    unsigned short *array;
    struct seminfo *__buf;
};

int main()
{
    int ret = 0;
    key_t key = 10240;
    int sem_id;

    // 创建或获取信号量集
    sem_id = semget(key, 1, IPC_CREAT);
    if (sem_id == -1)
    {
        perror("create sem error");
        return -1;
    }

    //操作信号量,默认是阻塞
    struct sembuf sem_operation[1]={0};
    sem_operation[0].sem_num=0;
    sem_operation[0].sem_op=-1;         // 申请一个资源
    //sem_operation[0].sem_flg=SEM_UNDO;
    
    // 消费,每次消费一个
    while (semop(sem_id, sem_operation, 1) != -1)
    {
        printf("consuming foods\n");
    }
    return 0;    
}

 编译运行

4. 注意事项

4.1 信号量的创建和初始化操作是分开的,这点需要注意,好在有一种解决方法,信号量集相关的结构体中有一个sem_otime字段,初始化为0,可以通过检查sem_otime是否为0来判断是否已经经过初始化

 

2. 资源限制

SEMMSL 系统范围内信号量集的最大数量,可通过/proc/sys/kernel/sem查看
SEMMSL 单个信号量集中信号量的最大个数
SEMMNS 系统范围内信号量的最大数量

 

5.参考文档

1. https://man7.org/linux/man-pages/man2/semop.2.html

2. 《Linux环境编程 从应用到内核》

3. 《Unix网络编程 卷二 进程间通信》

================================================================================================

Linux应用程序、内核、驱动、后台开发交流讨论群(745510310),感兴趣的同学可以加群讨论、交流、资料查找等,前进的道路上,你不是一个人奥^_^。...
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章