进程间通信-信号量

来源:互联网 发布:unity3d入门文章 编辑:程序博客网 时间:2024/06/05 20:25

信号量:以保护进程互斥与同步为目的,本质上为计数器,记录与统计临界资源的数目。

当请求一个使用信号量来表示的资源时,进程需要先读取信号量的值来判断资源是否可用:

1.大于0,资源可以请求,等于0,无资源可用,进程会进入睡眠状态直至资源可用;

2.当进程不再使用一个信号量控制的共享资源时,信号量的值+1(对信号量的值进行的增减操作均为原子操作,这是由于信号量主要的作用是维护资源的互斥或多进程的同步访问。而在信号量的创建及初始化上,不能保证操作均为原子性)。


二原信号量:信号量的值为1或0,任一时刻只允许一个进程访问临界资源,其+-操作必须为原子操作。

信号量保护进程访问临界资源的互斥与同步,在进程申请临界资源时,先要申请信号量,所以信号量也成为临界资源。


P、V操作:

P(sv):如果sv的值大于零,就给它减1;如果它的值为零,就挂起该进程的执行;
V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而挂
起,就给它加1.


Linux下信号量命令:

ipcs -s ;          //查看信号量ipcrm -s <semid>  //删除信号量


信号量机制函数:

1.创建信号量:

函数:

int semget(key_t key,int nsems,int semflg);
参数:

key:标识信号量的键值,由ftok()函数初始;

nsems:申请的信号量数目;

semflg:IPC_CREAT(单独使用,存在打开,不存在创建新的)IPC_EXCL(结合使用,不存在创建,存在返回错误码);

返回值:

返回信号量集标识符。


2.删除信号量

函数:

int semctl(int semid,int semnum,int cmd,...);
参数:

semid:信号量集标识符;

semnum:信号量集中要操作的信号量下标;

cmd:删除设为IPC_IMRD;

... :可变参数列表,此处设为NULL;

返回值:

<0,失败,==0,成功删除。


3.信号量初始化:

函数:

int semctl(int semid,int semnum,int cmd,...)'
参数:

semid:信号量集标识符;

semnum:信号量集中要操作的信号量下标;

cmd:此处设为SETVAL,对semnum信号量进行此控制操作;

...  :可变参数设为一个联合体,如下:

union semun {      int val; // 使用的值      struct semid_ds *buf;    // IPC_STAT、IPC_SET 使用缓存区      unsigned short *array;   // GETALL,、SETALL 使用的数组      struct seminfo *__buf;     // IPC_INFO(Linux特有) 使用缓存区};
将此联合中val初始化值。


4.信号量操作P()、V():

函数:

int semop(int semid,struct sembuf* sops,unsigned nsops);
参数

semid:信号量集标识符;

sops:结构体指针,结构体如下:

struct sembuf{      unsignea short sem_num; //表示在信号量集哪一个信号量上操作      short sem_op;     //P操作设为-1,V操作设为1;      short sem_flg;    //此处代码实现设为缺省0};

初始化时初始化此结构体,并传入其地址为函数参数;

nsops:前一个参数可视为一个数组,其表示数组中元素的个数;

以上结构体成员sem_flg取值:

(1).0               (2).IPC_NOWAIT

(3).SEM_UNDO:

有一种情况:如当有两个进程同时申请一个信号量去使用临界资源时,其中第一个进程在P()操作申请以后,还没有进行V()释放操作就异常退出了,所以此时另一个进程再对此信号量进行P()操作时,因为第一个进程没有释放信号量但已经退出,此进程则会一直申请不到信号量,从而造成死锁。

解决办法:将sem_flg取值为SEM_UNDO时,它将使操作系统跟踪当前进程对信号量的修改情况,若此进程在没有释放信号量的情况下异常退出终止,它将取消先前的P()操作,使信号量恢复原值,即操作系统会自动释放该进程持有的信号量,使其它进程可以正常工作。


代码实现:模拟实现将显示器作为临界资源,若实现信号量机制父子进程则分别成对向显示器打印‘BB’‘AA’,若没有信号量,则父子进程会以无规律形式向显示器打印BA.

test_sem.c

#include "comm.h"int main(){//创建二原信号量 int semid=createSem(1);//printf("create sucess:%d\n",semid);    if(initSem(semid,0,1)<0)  //初始化 {perror("initSem");return -1;}pid_t id=fork();if(id==0)   //child{int _semid=getSem(0);   //获取信号量集 while(1){P(_semid,0);  //申请信号量 printf("A");fflush(stdout);usleep(123456);printf("A");fflush(stdout);usleep(321456);V(_semid,0);    //释放 }}else   //father{while(1){P(semid,0);   printf("B");fflush(stdout);usleep(102456);printf("B");fflush(stdout);usleep(311456);V(semid,0);}}destroySem(semid);   //删除信号量集 return 0;}


comm.c

#include "comm.h"static int commSemMessage(int nums,int flags){key_t _key=ftok(PATH,PROJ_ID);if(_key<0){perror("ftok");return -1;}else{int semid=semget(_key,nums,flags);   //以flag模式得到nums个信号量返回键值为_key的信号量集标识符semid if(semid<0){perror("semget");return -2;}return semid;}} int createSem(int nums)     //创建 {return commSemMessage(nums,IPC_CREAT|IPC_EXCL|0666);}int getSem(int nums)   //获取 {return commSemMessage(nums,IPC_CREAT);}int destroySem(int semid)    //删除 {if(semctl(semid,0,IPC_RMID,NULL)<0)    //IPC_RMID立即删除semid信号量集中数组下标为第0个信号量 {perror("destroy:semctl");return -1;}return 0;}int initSem(int semid,int nums,int initVal){union semun _un;_un.val=initVal;if(semctl(semid,nums,SETVAL,_un)<0)    //对semid信号量集中第nums个信号量进行SETVAL操作将联合中_un成员val设为初始值initVal {perror("semctl");return -1;}return 0;}static int commPV(int semid,int nums,int initop){struct sembuf _buf;_buf.sem_num=nums;     //信号量集中哪一个信号量 _buf.sem_op=initop;     //P:-1,  V:1_buf.sem_flg=0;          //此处默认为0 if(semop(semid,&_buf,1)<0)   {perror("semop");return -1;}return 0;}int P(int semid,int nums)   {return commPV(semid,nums,-1);}int V(int semid,int nums){return commPV(semid,nums,1);}

comm.h

#ifndef COMM_H__#define COMM_H__#include <stdio.h>#include <sys/types.h>#include <sys/ipc.h>#include <sys/sem.h>#include <unistd.h>#include <errno.h>#define PATH "."#define PROJ_ID 0x6666union semun{int val;struct semid_ds *buf;unsigned short *array;struct seminfo *_buf;};int createSem(int nums);int getSem(int nums);int initSem(int semid,int nums,int initVal);int destroySem(int semid);int P(int semid,int nums);int V(int semid,int nums);#endif

结果如下:









原创粉丝点击