UNIX网络编程(十)Posix信号量

10.1概述
信号量是一种用于不同进程间或者一个给定进程中的不同线程间的同步手段的原语;
二值信号量:其值或为0或为1的信号量。
Posix信号量不必在内核中维护
可以在某个信号量上执行的三种操作:
1.创建一个信号量,要求调用者指定初始值,对于二值信号量来说,可以是1也可以是0;
2.等待(wait)一个信号量,该操作会测试这个信号量的值,如果其值小于或等于0,那么就等待(阻塞),一旦其值大于0就将它减去1。本操作还有其他名字:最初称为P操作,它也称为递减或者上锁,不过我们用Posix术语等待;
3.挂出一个信号量。该操作将信号量的值加1。如果一些进程阻塞着等待值变为大于0,其中一个进程可能现在就被唤醒。本操作最初被称作V操作,它也被称作递增,解锁,或发信号,我们使用Posix术语挂出
信号量的值可以初始化为任意非负数,这样的信号量称作计数信号量。通常初始化为某个值N,指示可用的资源数。

下面列出信号量,互斥锁和条件变量的差异:
1.互斥锁必须是由给它上锁的线程解锁,信号量的挂出不必由执行过它的等待操作的同一线程执行
2.互斥锁要么被锁住要么被解开(类似于二值信号量)
3.既然信号量有一个与之关联的状态(它的计数值),那么信号量挂出总是被记住,然而当一个条件变量发送信号时,如果没有线程等待在该条件变量上时,那么信号将丢失
Posix提供两类信号量:有名信号量和基于内存的信号量,后者也称为无名信号量

10.2 sem_open,sem_close和sem_unlink函数
函数sem_open创建一个有名信号量或者打开一个有名信号量。有名信号量总是既用于线程间的同步,又可以用于进程间的同步
#include<semaphore.h>
sem_t *semopen(const char *name,int oflag,mode_t mode,unsigned int value)
//若成功返回信号量的指针,若出错则为SEM_FAILED

oflag参数可以是0,O_CREATE或者O_CREATE|O_EXCL,如果指定了O_CREATE那么第三个和第四个参数是需要的的:其中mode指定权限位,value制动信号量的初始值。
如果指定了O_CREATE,那么只有当所需的信号量尚未存在时才初始化它。但是所需信号量已存在条件下指定O_CREATE|O_EXCL却是一个错误

使用sem_open打开的有名信号量,使用sem_close将其关闭

int sem_close(sem_t *sem)
//若成返回0.失败返回-1

一个进程终止时,内核还对其上仍然打开着的所有有名信号量自动执行这样的信号量关闭操作
关闭一个信号量并没有从系统中将它删除。这就是说,Posix有名信号量至少是随内核持续的:即使当前没有进程打开着某个信号量,它的值仍然保持
有名信号量使用sem_unlink从系统中删除

#include<semaphore.h>
int sem_unlink(const char *name)
//若成功返回0,否则返回-1
10.3sem_wait 和sem_trywait函数
sem_wait函数测试信号量的值,如果大于0,就将它减一并立即返回。如果该值等于0,调用线程就被投入到睡眠中,直到该值变为大于0。这时再将它减一,函数随后返回
“测试并减一”必须是原子的

#inlcude<semaphore.h>
int sem_wait(sem_t *sem)
int sem_trywait(sem_t *sem)
//若成功则为0.若出错则为-1

sem_trywait函数指定的信号量是0时,后者并不将调用线程投入睡眠,相反,它返回一个EAGAIN错误
当被某个信号中断,sem_wait就可能过早的返回,所返回错误为EINTR

10.4sem_post和sem_getvalue函数
sem_post函数将信号量的值加1.然后唤醒等待该信号量变为正数的任意线程。
#include<semaphore.h>
int sem_post(sem_t *sem)
int sem_getvalue(sem_t *sem,int  *valp)
//若成功则为0,若出错则为-1

10.6生产者-消费者问题

produce和consume函数
struct
{
int buff[NBUFF];
sem_t *mutex,*nempty,*nstored;
}shared;


void *produce(void *arg)
{
    int i;
    for(i=0;i<nitems;i++)
    {
        sem_wait(&shared.nempty);
        sem_wait(&shared.mutex);
        shared.buff[i%NBUFF]=i;
        sem_post(&shared.mutex);
        sem_post(&shared.nstored);
    }
    return (NULL);
}

void *consume(void *arg)
{
    int i;
    for(i=0;i<nitems;i++)
    {
        sem_wait(&shared.nstored);
        sem_wait(&shared.mutex);
        ........accumulate.....
        sem_post(&shared.mutex);
        sem_posr(&shared.nempty);
        
    }
    return (NULL);
}

10.8sem_init和sem_destroy函数

Posix也提供基于内存的信号量,他们由应用程序分配信号量的内存空间,然后由系统初始化它们的值
#include<semaphore.h>
int sem_init(sem_t *sem,int shared,usigned int value)
//若出错为-1
int sem_destory(sem_t * sem)
//若成功则为0,若出错则为-1

基于内存的信号量第由sem_init初始化的。sem参数指向应用程序必须分配的sem_t变量。如果shared为0.那么待初始化的信号量是在同一进程中的各个线程间共享的。如果shared为非0时,该信号量必须放在某种类型的共享内存区中,而即将使用它的所有进程都要能访问该共享内存区。value是该信号量的初始值
sem_open不需要类似于shared的参数或者PTHREAD_PROCESS_SHARED的属性,因为有名信号量总是可以再不同的进程中共享的
sem_open函数本身分配一个信号量并且初始化,sem_init必须传递由调用者分配的信号量的指针,然后由sem_init进行初始化、
基于内存的信号量至少具有随进程的持续性,然而,它们的持续性取决于存放信号量的内存区的类型,只要含有基于内存信号量的内存区保持有效,该信号量一直存在

10.9多个生产者,单个消费者
void *produce(void *arg)
{
    for(;;)
{
    sem_wait(&shared.nempty);//能同时获取nempty信号量的生产者线程可能有很多个,但是每个时刻只有一个生产者线程能获得mutex信号量
    sem_wait(&shared.mutex);
    if(shared.nput>=nitems)
    {
        sem_post(&shared.nempty);
        sem_post(&shared.mutex);
        return (NULL);
        }

    shared.nuff[shared.nput%NBUFF]=shared.nputval;
shared.nput++;
shared.nputval++;
sem__post(&shared.mutex);
sem_post(&shared.nstored);
*((int *)arg)+=1;
}
}

10.12进程间共享信号量
进程间共享基于内存信号量的规则很简单:信号量本身必须驻留在希望共享它的进程所共享的内存区中,而且sem_init第二个参数必须为1.
在父进程中打开的信号量仍然在子进程中打开’


10.13信号量限制

SEM_NSEMS_MAX 一个进程中同时打开的最大信号量数(Posix要求至少为256)
SEM_VALUE_MAX一个信号量的最大值(Posix要求至少32767)




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值