linux 信号_Linux:信号量

6dbc0f1f610b62d9e90d0e04953df9c3.png

一. System V 信号量说明

创建(获取)信号量

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

该函数创建或获取一个信号量集合,返回的是集合 id。如果创建失败,返回 -1

key:IPC 键值,通常由 ftok() 生成。该值用于创建消息队列、共享内存等;

nsems: 指定集合中包含的信号量个数;

semflg:该参数是多个标识的组合,其低 9 位指定用户对该信号量集合的权限。创建消息队列时使用 0666 | IPC_CREAT。当 key 指定的消息队列存在时,如果 IPC_CREATEIPC_EXCL 同时指定(IPC_CREAT | IPC_EXCL),那么该函数返回失败。在进程之间使用信号量进行同步时,一个进程负责创建(semflg = IPC_CREAT),另一个进程负责获取(semflg = IPC_EXCL)。

信号量操作

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

该函数指定信号量集合中的某个信号量的动作——获取还是释放。成功返回 0,失败返回 -1。

semid:由 semget() 返回的信号量集合 ID;

sops:该参数指定了对 semid 信号量集合中某些信号量的操作。该值为 struct sembuf 结构体数组的首地址。其结构体内容如下:(该结构体已在 <sys/sem.h> 文件中声明,无需在自己的代码中声明

struct sembuf
{
    unsigned short sem_num;  /* semaphore number */
    short          sem_op;   /* semaphore operation */
    short          sem_flg;  /* operation flags */
}

结构体成员说明

sem_num: 信号量集合中的信号量序号。序号从 0 开始。信号量的总数由 semget() 中的 nsems 指定;

sem_op :指定对序号为 sem_num 的信号量的操作。如果该值大于 0,表示释放信号量,值的大小表示释放的信号量的个数;如果等于 0,那么程序将阻塞,直到信号量的值变为 0;如果小于 0,其绝对值表示程序想要获取的信号量的数量。如果此时 sem_num 指定的信号量没有足够的数量,那么程序将阻塞。

sem_flg:该参数包含两个值——SEM_UNDOIPC_NOWAITIPC_NOWAIT 指定对 sem_num 信号量的操作非阻塞(如果不指定该值,默认为阻塞操作)。SEM_UNDO 表示撤销操作。即不论程序是正常还是异常结束,sem_num 指定的信号量的值都会恢复为调用 semop() 之前的值。主要用于防止程序异常结束时未将占用的资源释放,导致资源永远被锁定的情况出现。

nsops:表示 sops 参数指定的数组中有几个成员。即想要操作集合中的信号量的数量。

信号量控制

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

该函数用于控制信号量集合及其包含的信号量。成功返回 > 0,失败返回 -1。

semid:由 semget() 返回的信号量集合 ID;

sem_num: 信号量集合中的信号量序号。序号从 0 开始。信号量的总数由 semget() 中的 nsems 指定;

cmd:控制命令。常用值为:IPC_STAT IPC_SET IPC_RMID GETVAL SETVAL

IPC_STAT:获取 semid 信号量集合状态;

IPC_RMID:删除 semid 指定的信号量集合;

SETVAL:设置 semid 集合中 sem_num 指定的信号量的值。

cmd 来决定是否包含第 4 个参数。第 4 个参数必须为如下结构的变量:(该共用体需要在自己的代码中声明

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) */
};

具体的 cmd 与第 4 个参数的组合情况,请查看 man 手册。

二. 封装

osal_sem.h

#ifndef OSAL_SEM_H
#define OSAL_SEM_H
​
#include <stdint.h>
​
#ifdef __cplusplus
extern "C"
{
#endif
​
#define SEM_WAIT_NONE       (0)             //非阻塞
#define SEM_WAIT_FOREVER    (0xffffffff)    //阻塞
​
enum
{
    SEM_SUCCESS = 0,    /*成功*/
    SEM_FAILED,         /*失败*/
};
​
/*****************************************************************************
 * @brief:siot_sem_create
 * @param[in]:int32_t value:信号量初始值             
 * @param[out]:int32_t *ret  :函数执行结果
 * @return: 信号量操作句柄
 * @details:创建信号量
 * @author:sdc
*****************************************************************************/
extern void *osal_sem_create(int32_t value, int32_t *ret);
/*****************************************************************************
 * @brief:siot_sem_get
 * @param[in]:void *sem_handle:句柄 
             int32_t wait_flg :非阻塞:0;阻塞:0xffffffff
 * @param[out]:none
 * @return:0:成功;
           1:失败
 * @details:获取信号量
 * @author:sdc
*****************************************************************************/
extern int32_t osal_sem_get(void *sem_handle, int32_t wait_flg);
/*****************************************************************************
 * @brief:siot_sem_post
 * @param[in]:void *sem_handle:句柄
 * @param[out]:none
 * @return:0:成功;
           1:失败
 * @details:释放信号量
 * @author:sdc
*****************************************************************************/
extern int32_t osal_sem_post(void *sem_handle);
/*****************************************************************************
 * @brief:siot_sem_destroy
 * @param[in]:void *sem_handle:句柄
 * @param[out]:none
 * @return:0:成功;
           1:失败
 * @details:销毁信号量
 * @author:sdc
*****************************************************************************/
extern int32_t osal_sem_destroy(void *sem_handle);
​
#ifdef __cplusplus
}
#endif
​
#endif

osal_sem.c

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include "osal_sem.h"
​
typedef union 
{
    int val;          /*SETVAL用的值*/
    struct semid_ds *buf; /*IPC_STAT、IPC_SET用的semid_ds结构*/
    unsigned short *array; /*SETALL、GETALL用的数组值*/
    struct seminfo *__buf;   /*为控制IPC_INFO提供的缓存*/
}semctl_arg_union;
​
​
typedef struct 
{
    int32_t sem_id;
}osal_sem_struct;
​
static int32_t __sem_init(void)
{
    key_t sem_key = 0;
    static int32_t s_proj_id = 0;   //为了保证同一个进程中可以创建多个信号量,ftok(pathname, proj_id) 中的 proj_id 需要不同
    int32_t sem_id = -1;
    
    s_proj_id = (255 <= s_proj_id++) ? 1 : s_proj_id;
    sem_key = ftok(".", s_proj_id);
    if(-1 != sem_key)
    {
        sem_id = semget(sem_key, 1, 0666 | IPC_CREAT | IPC_EXCL);
        if(sem_id != -1)
        {
            printf("semget() exec succeedn");
        }
        else
        {
            printf("semget() exec failed, errno is %dn", errno);
        }
    }
    else
    {
        printf("ftok() exec failed, errno is %dn", errno);
    }
​
    return sem_id;
}
​
static int32_t __sem_set_value(int32_t sem_id, int32_t value)
{
    semctl_arg_union au = {value};
​
    return semctl(sem_id, 0, SETVAL, au);    
}
​
static int32_t __sem_destroy(int32_t sem_id)
{
    return semctl(sem_id, 0, IPC_RMID);
}
​
​
void *osal_sem_create(int32_t value, int32_t *ret)
{
    int32_t sem_id = 0;
    osal_sem_struct *sem_handle = NULL;
​
    if((0 == value) || (NULL == ret))
    {
        printf("wrong para, value is 0 or ret is NULLn");
        *ret = SEM_FAILED;
        return NULL;
    }
    
    sem_handle = (osal_sem_struct *)malloc(sizeof(osal_sem_struct));
    if(NULL == sem_handle)
    {
        printf("malloc failedn");
        *ret = SEM_FAILED;
        return NULL;
    }
​
    sem_id = __sem_init();
    if(0 > sem_id)
    {
        printf("inner sem init failedn");
        free(sem_handle);
        *ret = SEM_FAILED;
        return NULL;
    }
​
    sem_handle->sem_id = sem_id;
​
    if(0 > __sem_set_value(sem_id, value))
    {
        printf("inner set sem count failedn");
        __sem_destroy(sem_id);
        free(sem_handle);
        *ret = SEM_FAILED;
        return NULL;
    }
    
    return sem_handle;
}
​
​
int32_t osal_sem_get(void *handle, int32_t wait_flg)
{
    osal_sem_struct *sem_handle = (osal_sem_struct *)handle;
    struct sembuf sem_op = {0};
​
    if(NULL == handle)
    {
        printf("sempahore handle is NULLn");
        return SEM_FAILED;
    }
​
    sem_op.sem_num = 0;
    sem_op.sem_op = -1; //等待获取一个信号量
    sem_op.sem_flg = SEM_UNDO | ((SEM_WAIT_NONE == wait_flg) ? IPC_NOWAIT : 0);
    if(0 > semop(sem_handle->sem_id, &sem_op, 1))
    {
        printf("sempahore wait failedn");
        return SEM_FAILED;
    }
​
    return SEM_SUCCESS;
}
​
int32_t osal_sem_post(void *handle)
{
    osal_sem_struct *sem_handle = (osal_sem_struct *)handle;
    struct sembuf sem_op = {0};
​
    if(NULL == handle)
    {
        printf("sempahore handle is NULLn");
        return SEM_FAILED;
    }
​
    sem_op.sem_num = 0;
    sem_op.sem_op = 1; //等待获取一个信号量
    sem_op.sem_flg = SEM_UNDO;
​
    if(0 > semop(sem_handle->sem_id, &sem_op, 1))
    {
        printf("sempahore post failedn");
        return SEM_FAILED;
    }
​
    return SEM_SUCCESS;
​
}
​
int32_t osal_sem_destroy(void *handle)
{
    osal_sem_struct *sem_handle = (osal_sem_struct *)handle;
​
    if(NULL == handle)
    {
        printf("sempahore handle is NULLn");
        return SEM_FAILED;
    }
​
    if(0 > __sem_destroy(sem_handle->sem_id))
    {
        printf("sempahore delete failedn");
        return SEM_FAILED;
    }
​
    free(handle);
    return SEM_SUCCESS;
}

三. 例程

demo.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include "osal_sem.h"
​
void *thread_producer(void *para)
{
    void *sem_handle = para;
​
    printf("consumer threadn");
​
    while(1)
    {
        if(SEM_SUCCESS == osal_sem_post(sem_handle))
        {
            printf("semaphore post successn");
        }
        else
        {
            printf("semaphore post failedn");
        }
​
        sleep(3);
    }
}
​
void *thread_consumer(void *para)
{
    void *sem_handle = para;
​
    printf("producer threadn");
​
    while(1)
    {
        if(SEM_SUCCESS == osal_sem_get(sem_handle, SEM_WAIT_FOREVER))
        {
            printf("get sempahore successn");
        }
        else
        {
            printf("waitting for semaphore failedn");
            sleep(1);
        }
    }
}
​
int main(int argc, char *argv[])
{
    void *sem_handle = NULL;
    pthread_t pid_1 = 0;
    pthread_t pid_2 = 0;
    int32_t ret = 0;
​
    sem_handle = osal_sem_create(1, &ret);
    if(NULL == sem_handle)
    {
        printf("sempahore create failedn");
        return -1;
    }
    
    pthread_create(&pid_1, NULL, thread_consumer, sem_handle);
    pthread_create(&pid_2, NULL, thread_producer, sem_handle);
​
    pthread_join(pid_1, NULL);
    pthread_join(pid_2, NULL);
    
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值