信号量

一、信号量

      以一个停车场的运作为例。假设停车场只有三个车位,一开始三个车位都是空的。这时如果同时来了五辆车,看门人允许其中三辆直接进入,然后放下车拦,剩下的车则必须在入口等待,此后来的车也都不得不在入口处等待。这时,有一辆车离开停车场,看门人得知后,打开车拦,放入外面的一辆进去,如果又离开两辆,则又可以放入两辆,如此往复。

在这个停车场系统中,车位是公共资源,每辆车好比一个线程,看门人起的就是信号量的作用。

同样的,为了防止因多个程序同时访问一个共享资源而引发的一系列问题,我们需要一种方法,它可以通过生成并使用令牌来授权,在任意一时刻只能有一个执行线程访问代码的临界区域。

临界区域是指数据更新的代码需要独占式地执行,而信号量就可以提供这样的一种访问机制,让一个临界区同一时间只有一个线程在访问它,也就是说信号量是用来调协进程对共享资源的访问的。其中共享内存的使用就要用到信号量。

二、P,V原语

信号量本质上是⼀一个计数器,只能进行两种操作等待和发送信号,即P(sv)和V(sv),他们的行为:

P(s) 
{    s.value = s.value--;    
     if (s.value < 0)    
     {        
            该进程状态置为等待状状态       
             将该进程的PCB插⼊入相应的等待队列s.queue末尾    
     }
 }


V(s) 
{    s.value = s.value++;    
     if (s.value < =0)    
     {       
           唤醒相应等待队列s.queue中等待的⼀一个进程       
           改变其状态为就绪态         
           并将其插⼊入就绪队列    
     } 
}

 

三、信号量集函数

1、semget 函数--用来创建和访问⼀一个信号量集

原型  : int semget(key_t key, int nsems, int semflg);

参数:

 key: 信号集的名字    

nsems:信号集中信号量的个数   

semflg: 由九个权限标志构成,它们的用法和创建文件时使⽤的mode模式标志是一样的

返回值:成功返回⼀一个⾮非负整数,即该信号集的标识码;失败返回-1

2、shmctl 函数--用于控制信号量集

原型 :  int semctl(int semid, int semnum, int cmd, ...);

参数 :

semid:由semget返回的信号集标识码   

semnum:信号集中信号量的序号   

cmd:将要采取的动作(有三个可取值)    最后一个参数根据命令不同而不同 ,如下:

返回值:成功返回0;失败返回-1

3、semop函数--用来创建和访问⼀个信号量集

原型 :  int semop(int semid, struct sembuf *sops, unsigned nsops);

参数  :

semid:是该信号量的标识码,也就是semget函数的返回值   

sops:是个指向⼀一个结构数值的指针   

nsops:信号量的个数

返回值:成功返回0;失败返回-1

四、代码

1、Makefile

 1 test_sem:comm.c test_sem.c
  2     gcc -o $@ $^
  3 
  4 .PHONY:clean
  5 clean:
  6     rm -f test_sem


2、comm.h

 1 #ifndef _COMM_H_
  2 #define _COMM_H_
  3 
  4 #include<stdio.h>
  5 #include<sys/types.h>
  6 #include<sys/ipc.h>
  7 #include<sys/sem.h>
  8 
  9 #define PATHNAME "."
 10 #define PROJ_ID 0x6666
 11 
 12 union semun{
 13     int val;
 14     struct semid_ds *buf;
 15     unsigned short *array;
 16     struct seminfo *_buf;
 17 };
 18 
 19 int createSemSet(int nums);
 20 int initSem(int semid,int nums,int initVal);
 21 int getSemSet(int nums);
 22 int P(int semid,int who);
 23 int V(int semid,int who);
 24 int destroySemSet(int semid);
 25 
 26 #endif               

3、comm.c

 1 #include"comm.h"
  2 
  3 static int commSemSet(int nums,int flags){
  4     key_t key=ftok(PATHNAME,PROJ_ID);
  5     if(key<0){
  6         perror("ftok");
  7         return -1;
  8     }
  9     int semid=semget(key,nums,flags);
 10     if(semid<0){
 11         perror("semget");
 12         return -2;
 13     }
 14     return semid;
 15 }
 16 
 17 int createSemSet(int nums){
 18     return commSemSet(nums,IPC_CREAT|IPC_EXCL|0666);
 19 }
 20 
 21 int getSemSet(int nums){
 22     return commSemSet(nums,IPC_CREAT);
 23 }
25 int initSem(int semid,int nums,int initVal){
 26     union semun _un;
 27     _un.val=initVal;
 28     if(semctl(semid,nums,SETVAL,_un)<0){
 29         perror("semctl");
 30         return -1;
 31     }
 32     return 0;
 33 }
 34 
 35 static int commPV(int semid,int who,int op){
 36     struct sembuf _sf;
 37     _sf.sem_num=who;
 38     _sf.sem_op=op;
 39     _sf.sem_flg=0;
 40     if(semop(semid,&_sf,1)<0){
 41         perror("semop");
 42         return -1;
 43     }
 44     return 0;
 45 }
 46 
 47 int P(int semid,int who){
 48     return commPV(semid,who,-1);
 49 }
 50 
 51 int V(int semid,int who){
 52     return commPV(semid,who,1);
 53 }
 54 
 55 int destorySemSet(int semid){
 56     if(semctl(semid,0,IPC_RMID)<0){
 57         perror("senctl");
 58         return -1;
 59     }
 60 }

4、test_sem.c

 1 #include"comm.h"
  2 
  3 int main(){
  4     int semid=createSemSet(1);
  5     initSem(semid,0,1);
  6     pid_t id=fork();
  7     if(id==0){
  8         int _semid=getSemSet(0);
  9         while(1){
 10             P(_semid,0);
 11             //P(_semid,0);
 12             printf("A");
 13             fflush(stdout);
 14             usleep(123456);
 15             printf("A  ");
 16             fflush(stdout);
 17             usleep(321456);
 18             V(_semid,0);
 19             //V(_semid,0);
 20         }
 21     }
 22     else{
 23         while(1){
 24             P(semid,0);
 25             //P(semid,0);
 26             printf("B");
 27             fflush(stdout);
 28             usleep(123456);
 29             printf("B  ");
 30             fflush(stdout);
 31             usleep(321456);
 32             V(semid,0);
 33             //V(semid,0);
 34         }
 35         wait(NULL);
 36     }
 37     destorySemSet(semid);
 38     return 0;
 39 }
                 

结果:



所有的AB成对出现,不会出现交叉的情况,如果去掉PV现象是什么呢?如上代码中主函数将PV注释:



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值