进程间通信-信号量

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

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

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 0x6666

union 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

结果如下:









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值