一. System V 信号量说明
创建(获取)信号量
int semget(key_t key, int nsems, int semflg);
该函数创建或获取一个信号量集合,返回的是集合 id。如果创建失败,返回 -1
。
key
:IPC 键值,通常由 ftok()
生成。该值用于创建消息队列、共享内存等;
nsems
: 指定集合中包含的信号量个数;
semflg
:该参数是多个标识的组合,其低 9 位指定用户对该信号量集合的权限。创建消息队列时使用 0666 | IPC_CREAT
。当 key
指定的消息队列存在时,如果 IPC_CREATE
和 IPC_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_UNDO
和 IPC_NOWAIT
。IPC_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;
}