【Linux】信号量

信号量是进程间通信的一种方式。本质上它不具有数据交换的功能,它相当于一个计数器,对于系统现有资源进行计数以达到使进程互斥的访问资源的目的。
当请求一个使用信号量来表示的资源时,进程需要先读取信号量的值来判断资源是否可用。大于0,资源可以请求,分配给进程后,信号量的值加一,这样请求资源的操作称为P操作。等于0,资源不可用,进程会进入睡眠状态直至资源可用。当资源使用完毕被释放时,信号量的值减一,释放资源的过程称为V操作。
我们通过代码来验证信号量的功能,在此之前,先认识几个函数,我们的代码中会用到。

1.semget


创建信号量函数,返回值是信号量的id(用来标识信号量)。
参数一:key,关键码,使用ftok函数随机得到。
参数二:nsems,信号量的个数,信号量申请的时候是以信号量集的形式(可以想象是一个数组)。如果传1说明这个信号量集中只有一个,它的下标就是0。
参数三:semflg,有两种状态:IPC_CREAT,IPC_EXCL
        IPC_CREAT:若内核中不存在键值与key相等的信号量集,则创建,否则,返回此信号量集的标识符
        IPC_EXCL:单独使用无意义
        IPC_CREAT | IPC_EXCL :创建一个新的信号量集并返回信号量集的标识符,否则,返回-1.
2.semctl

在指定的信号集或信号集内的某个信号上执行操作控制,可以用来销毁信号量和初始化信号量。
参数一:semid,函数semget返回的信号量的id,表示操作的是哪个信号量集。
参数二:semnum,表示操作的是信号量集中的哪个信号量。
参数三:cmd,取值有几种,我们要用到的是销毁信号量时使用IPC_RMID,此时后面的可变参数列表传入NULL。初始化时设置为SETVAL,可变参数列表要传入一个结构体。如下:
               union semun {
                   short val;          /*SETVAL用的值*/
                   struct semid_ds* buf; /*IPC_STATIPC_SET用的semid_ds结构*/
                   unsigned short* array; /*SETALLGETALL用的数组值*/
                   struct seminfo *buf;   /*为控制IPC_INFO提供的缓存*/
                  } arg;
3.setop

对信号量进行P,V操作。
参数一:semid,函数semget返回的信号量的id,表示操作的是哪个信号量集。
参数二:sembuf结构体,包含sem_num,sem_op,sem_flg。sem_num表示的是操作的是哪个信号量,sem_op取值为-1或者1,-1表示P操作,1表示V操作。
sem_flg成员可以为0、IPC_NOWAIT、SEM_UNDO 。
为SEM_UNDO时,它将使操作系统跟踪当前进程对这个信号量的修改情况,如果这个进程在没有释放该信号量的情况下终止,操作系统将自动释放该进程持有的信号量,从而使另外一个进程可以继续工作,防止其他进程因为得不到信号量而发生【死锁现象】。为此一般建议使用SEM_UNDO。
验证信号量代码:
/***************************comm.h************************/
#ifndef _COMM_
#define _COMM_

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#define PATHNAME "."
#define PROJID 0
union semun {
	int val; // 使⽤的值
	struct semid_ds *buf; // IPC_STAT、 IPC_SET 使⽤缓存区
	unsigned short *array; // GETALL,、 SETALL 使⽤的数组
	struct seminfo *__buf; // IPC_INFO(Linux特有) 使⽤缓存区
};
int creatShm();
static int commShm(int nsem,int IPC);
int initshm(int semId,int which);
int getShm();
int destoryShm(int semId);
int option(int semId,int which,int flag);
int P(int semId,int which);
int V(int semId,int which);
#endif
/***************************comm.c**********************/
#include"comm.h"

static int commShm(int nsem,int IPC)
{
	key_t key=ftok(PATHNAME,PROJID);
	if(key<0)
	{
		perror("ftok");
		return -1;
	}
	int id=semget(key,nsem,IPC);
	if(id<0)
	{
		perror("semget");
		return -1;
	}
	return id;
}
int creatShm()
{
	return commShm(1,IPC_CREAT|IPC_EXCL|0666);
}

int getShm()
{
	return commShm(0,0);
}

int destoryShm(int semId)
{
	int ret=semctl(semId,0,IPC_RMID,NULL);
	if(ret==-1)
	{
		perror("semctl");
		return -1;
	}
	return 0;
}
int initshm(int semId,int which)
{
	union semun _semun;
	_semun.val=1;
	int ret=semctl(semId,which,SETVAL,_semun);
	if(ret==-1)
	{
		perror("semctl");
		return -1;
	}
	return ret;
}
int option(int semId,int which,int flag)
{
	struct sembuf sem;
	sem.sem_num=which;
	sem.sem_op=flag;
	sem.sem_flg=0;
	return semop(semId,&sem,1);
}

int P(int semId,int which)
{
	return option(semId,which,-1);
}

int V(int semId,int which)
{
	return option(semId,which,1);
}
/******************************test_shm.c**************************/
#include"comm.h"

int main()
{
	int semid=creatShm();
	if(semid<0)
	{
		printf("creat shm error\n");
		return -1;
	}
	initshm(semid,0);
	pid_t id=fork();
	if(id==0)
	{
		//child
		while(1)
		{
			P(semid,0);
			usleep(10000);
			printf("A");
			fflush(stdout);
			usleep(100);
			printf("A");
			fflush(stdout);
			V(semid,0);
		}
	}
	else if(id>0)
	{
		//father
		while(1)
		{
			P(semid,0);
			usleep(100000);
			printf("B");
			fflush(stdout);
			usleep(1000);
			printf("B");
			fflush(stdout);
			V(semid,0);
		}
		wait(NULL);
	}
	destoryShm(semid);
	return 0;
}

在没有对父子进程进行PV操作之前,运行结果如下

父子进程打印的A和B都有单独出现的情况。而在使用了PV操作后,执行结果如下:

A,B成对出现,说明PV操作保证了一个进程访问资源时不被打扰,直到进程执行完自己的代码,释放资源后,该资源才会被其他进程所使用。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值