linux进程线程同步之 - POSIX无名信号量

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且信号量的值保持不变。

错误EINTRThe 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出来的子进程可以和父进程共享一个信号量,那么就可以进行同步了。

你可能感兴趣的:(linux)