POSIX - 无名信号量(匿名信号量)
使用范围:线程间同步,相关进程间同步
此文转自:http://blog.csdn.net/jiebaoabcabc/article/details/37885659
一、函数介绍
1.初始化无名信号量
#include
int sem_init(sem_t *sem, int pshared, unsigned int value);
函数功能:sem_init()初始化一个定位在 sem的匿名信号量。
返回值:sem_init()成功时返回 0;错误时,返回 -1,并把 errno设置为合适的值。
错误:EINVAL
value 超过 SEM_VALUE_MAX。
ENOSYS
pshared 非零,但系统还没有支持进程共享的信号量。
输入参数:
1.pshared
pshared==0用于同一多线程的同步;
若pshared>0 用于多个相关进程间的同步(即由fork产生的)
如果 pshared是非零值,那么信号量将在进程之间共享,并且应该定位共享内存区域(见 shm_open(3)、mmap(2) 和 shmget(2))。(因为通过 fork(2) 创建的孩子继承其父亲的内存映射,因此它也可以见到这个信号量。所有可以访问共享内存区域的进程都可以用 sem_post(3)、sem_wait(3) 等等操作信号量。
2.value
信号量初值,如果大于0则为最大值,如果为0,最大值则是1。
输出参数:
sem
输出描述sem的结构,代表刚初始化的信号量。
2.注销信号量
#include
int sem_destroy(sem_t *sem)
函数功能:销毁由sem指向的匿名信号量
返回值:sem_destroy() 成功时返回 0;错误时,返回 -1,并把errno设置为合适的值。
错误:EINVALsem不是一个有效的信号量。
输入参数:sem
表明哪一个信号量需要被注销。
注意:
只有通过sem_init(3)初始化的信号量才应该使用sem_destroy()销毁。
销毁一个有其它线程或进程当前阻塞(在sem_wait(3))的信号量将导致未定义行为。
使用一个已经销毁的信号量将导致未定义结果,除非这个信号量已经使用sem_init(3)重新初始化了。
3.申请信号量 P操作
#include
int sem_wait(sem_t *sem);
函数功能:sem_wait函数也是一个原子操作,它的作用是从信号量的值减去一个“1”,但它永远会先等待该信号量为一个非零值才开始做减法。
返回值:成功返回0,错误的话信号量的值不改动,返回-1.errno设定来标识错误.
错误:EINTR The call was interrupted by a signal handler; seesignal(7).
//调用被信号处理中断
EINVAL sem is not a valid semaphore.
//sem不是有效的信号量
The following additional error can occurfor sem_trywait():
//下面的错误是sem_trywait()可能发生的:
EAGAIN The operation could not beperformed without blocking (i.e., the
semaphore currently has the value zero).
//除了锁定无法进行别的操作(如信号量当前是0值).
The following additional errors canoccur for sem_timedwait():
//下面的错误是sem_timedwait()可能发生的:
EINVAL The value of abs_timeout.tv_nsecsis less than 0, or greater than or
equal to 1000 million.
//abs_timeout.tv_nsecs 的值比0小或者大于等于1000毫秒(译者注:纳秒的值不能比0小,不能比1秒大)
ETIMEDOUT
The call timed out before the semaphore could be locked.
//在信号量锁定之前就超时了
输入参数:sem
表明哪一个信号量需要申请信号量。
4.申请信号量 无阻塞版本
#include
int sem_trywait(sem_t *sem);
函数功能:无阻塞获取信号量,如果信号量为0也不会阻塞。
返回值:执行成功返回0,执行失败返回 -1且信号量的值保持不变。
错误:EINTR:The call was interrupted by a signalhandler.(信号处理函数中断调用)
EINVAL: sem is not a valid semaphore.(信号量sem值无效)
EAGAIN: The operation could not beperformed without blocking (i.e., the semaphore currently has the value zero).
输入参数:sem
表明哪一个信号量需要申请信号量。
5.申请信号量 带超时版本
#include
int sem_timedwait (sem_t *sem, const structtimespec *abstime);
函数功能:带超时的阻塞获取信号量,如果
返回值:成功时返回 0;错误时,信号量的值没有更改,-1被返回,并设置 errno 来指明错
错误:ETIMEDOUT 阻塞超时
EINTR
这个调用被信号处理器中断,参看 signal(7)。
EINVAL
sem 不是一个有效的信号量。
对 sem_trywait() 有如下额外的错误:
EAGAIN
操作不能执行而不阻塞(也就是说,信号量当前值是零)。
对 sem_timedwait() 有如下额外的错误:
EINVAL
abs_timeout.tv_nsecs 的值小于0,或者大于等于 100 百万。
输入参数:1.sem 指向描述当前信号量的结构体指针
2. abstime 绝对时间,设定超时时间值
6.释放信号量 V操作
#include
int sem_post(sem_t *sem);
函数功能:信号量加1的原子操作
返回值:sem_post()成功时返回 0;错误时,信号量的值没有更改,-1被返回,并设置 errno 来指明错误。
错误: EINVAL
sem 不是一个有效的信号量。
EOVERFLOW
信号量允许的最大值将要被超过。
输入参数:sem
表明哪一个信号量需要释放信号量。
7.查询信号量
#include
int sem_getvalue(sem_t *sem,int *sval);
函数功能:原子查询当前信号量的值
返回值:sem_getvalue()成功时返回 0;错误时,返回 -1,同时把 errno设置为合适的值。
错误:EINVAL
sem 不是一个有效的信号量。
输入参数:sem 指向描述当前信号量的结构体指针
输出参数:sval 返回当前信号量的值
二、典型操作 线程同步例子
#include
#include
#include
#include
#include
#include
#include // clock_gettime
#include
#define NO_WAIT 0
#define WAIT_FOREVER (-1)
// ms -> timespec(sec and nsec)
static inline void getTimespec(int wait_ms, struct timespec *tp)
{
time_t sec, t;
long long nsec;
sec = 0;
if (wait_ms == NO_WAIT)
{
nsec = 0;
}
else
{
nsec = wait_ms * 1000000LL;
}
if (clock_gettime(CLOCK_REALTIME, tp) == -1) //get now time
{
printf("getTimespec: clock_gettime call fail, error %d(%s)\n", errno, strerror(errno));
tp->tv_sec = time(NULL) + 1;
tp->tv_nsec = 0;
}
else
{
t = time(NULL) + 1;
if ((int)(tp->tv_sec - t) > 30)
{
tp->tv_sec = t;
tp->tv_nsec = 0;
}
}
nsec += tp->tv_nsec;
if (nsec >= 1000000000) // if nsec > 1sec
{
sec = nsec / 1000000000;
nsec = nsec % 1000000000;
}
tp->tv_sec += sec;
tp->tv_nsec = nsec;
return;
}
// int sem_init(sem_t *sem, int pshared, unsigned int value);
// pshared = 0 for thread else pshared = 1 for process
int semInit(sem_t *sem, unsigned value)
{
int rval;
sem_init(sem, 0, value);
if (rval != -1)
{
return 0;
}
return rval;
}
int semPost(sem_t *sem)
{
int rval;
rval = sem_post(sem);
return rval;
}
int semWait(sem_t *sem, int wait_ms)
{
int rval;
struct timespec timeout;
if (wait_ms == NO_WAIT)
{
rval = sem_trywait(sem);
}
else if (wait_ms == WAIT_FOREVER)
{
rval = sem_wait(sem);
}
else
{
getTimespec(wait_ms, &timeout);
rval = sem_timedwait(sem, &timeout);
}
return rval;
}
int semGetvalue(sem_t *sem, int *sval)
{
int rval;
rval = sem_getvalue(sem, sval);
return rval;
}
int semDestroy(sem_t *sem)
{
int rval;
rval = sem_destroy(sem);
return rval;
}
//---------------------------------------< glb >
sem_t sem;
//---------------------------------------< /glb >
/*生产者*/
void* produce(void* arg)
{
int sval = 0;
sleep(5);
semGetvalue(&sem, &sval); // get sem value
printf("sval is %d\n", sval);
semPost(&sem);
return NULL;
}
/*消费者*/
void* consumer(void* arg)
{
int ret = 0;
ret = semWait(&sem, 2000);
printf("timeout ret = %d\n", ret); // timeout return -1
ret = semWait(&sem, 10000);
printf("notimeout ret = %d\n", ret); // no timeout return 0
return NULL;
}
int main()
{
pthread_t p1_tid;
pthread_t p2_tid;
semInit(&sem, 0);
int i=1,j=2;
pthread_create(&p1_tid,NULL,produce,(void*)&i);
pthread_create(&p2_tid,NULL,consumer,(void*)&j);
pthread_join(p1_tid,NULL);
pthread_join(p2_tid,NULL);
semDestroy(&sem);
return 0;
}
注意:编译时加上-lrt
运行结果截图:
相关进程同步同理,即fork出来的子进程可以和父进程共享一个信号量,那么就可以进行同步了。