【Linux】进程间通讯之信号量

**

信号量

**
一、信号量的概念
信号量是一个特殊的变量(计数器),其值大于0时,记录临界资源的个数,当值等于0时,对此信号量执行P操作(-1)会阻塞,直到信号量的值大于0,或者有其他进程在此信号量上执行了V操作(+1)
通过信号量我们主要完成的是对于进程执行的同步控制
二、与信号量有关的相关内容的概念
1、临界资源:临界资源是指每次仅允许一个进程访问的资源。
属于临界资源的硬件有打印机、磁带机等,软件有消息缓冲队列、变量、数组、缓冲区等。
2、临界区: 每个进程中访问临界资源的那段代码称为临界区
3、原子操作:是指不会被线程调度机制打断的操作。这种操作一旦开始,就一直运行到结束。
4、PV操作:PV操作与信号量的处理相关,P表示通过的意思,V表示释放的意思。所谓信号灯,实际上就是用来控制进程状态的一个代表某一资源的存储单元。
三、相关方法去操作信号量
因为Linux系统内核维护是一个信号量集,所有我们就会有一系列的方法去操作信号量
1、int semget():创建或者获取已存在的信号量
函数原型:

  • int semget((key_t)key, int nsems, int flag);

semget():创建或者获取已存在的信号量
semget():成功返回信号量的ID,失败返回-1
key:用户标识。如果多个进程想通过同一个消息队列完成数据通信,则每个进程使用相同的key值创建或者获取同一个消息队列的内核对象ID值
nsems:内核维护的是一个信号量集,在新建信号量时,其指定信号量集中信号量的个数
flag:指定一个操作权限
可选::IPC_CREAT IPC_EXCL

如果是第一次执行semget,内核中并没有信号量集,则需要创建信号量集,并且完成初始化
如果内核中已经有了此信号量集,则直接获取返回就可以

2、int semop():对信号量进行改变,做P操作或者V操作
函数原型:

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

semop()成功返回0,失败返回-1

struct sembuf

>  {
>  unsigned short sem_num;//指定信号量集中的信号量下标
>  short sem_op;//其值为-1,代表P操作,其值为1,代表V操作
>  short  sem_flg//SEM_UNDO
>  }

3、int semctl():设置方法

  • int semctl(int semid, int semnum, int cmd,…);

semnum:信号量的下标
cmd:IPC_RMID SETVAL

4、int CreateSem():创建信号量
int CreateSem(int key, int init_val[],int len);
(1)void SemP(int key, int index);
一次调用只操作一个信号量
(2)void SemV(int semdi,int index);
5、void DeleteSem():删除信号量
void DeleteSem(int semid);
四、系统上模拟实现信号量的操作

//sem.h
#pragma once
#include <sys/sem.h>
union semval
{
int val;
};
int CreateSem(int key, int init_val[], int len);
//一次只操作一个信号量
void SemP(int semid, int index);
void SemV(int semid, int index);
void DeleteSem(int semid);
//sem.c
#include "./sem.h"
#include <stdio.h>
int CreateSem(int key, int init_val[],int len)
{ 
	//获取
	int semid = semget((key_t)key, 0, 0664);
	if(semid != -1)
	{
		return semid;
	}
	//创建
	semid = semget((key_t)key,len, IPC_CREAT | 0664);
	if(semid == -1)
	{
		perror("Create Sem Error: ");
		return -1;
	}
	//初始化
	int i = 0;
	for(; i < len; ++i)
	{
		union semval data;
		data.val = init_val[i];
		if(-1 == semctl(semid, i, SETVAL, data))
		{
			perror("init sem value fail: ");
			return -1;
		}
	}
	return semid;
}
	//P操作
void SemP(int semid,int index)
{
	struct sembuf buf;
	buf.sem_num = index;
	buf.sem_op = -1;
	buf.sem_flg = SEM_UNDO;
	if(-1 == semop(semid,&buf,1))
	{
		perror("sem p operation fail: ");
	}
}
	//V操作
void SemV(int semid,int index)
{
	struct sembuf buf;
	buf.sem_num = index;
	buf.sem_op = 1;
	buf.sem_flg = SEM_UNDO;
	if(-1 == semop(semid, &buf, 1))
	{
		perror("sem v operation fail: ");
	}
}
//删除整个信号量集
void DeleteSem(int semid)
{
	if(-1 == semctl(semid, 0, IPC_RMID))
	{
		perror("delete sem fail: ");
}
//main.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include "sem.h"
#include <time.h>
int main(int argc, char *argv[])
{
	srand((unsigned int)(time(NULL)*time(NULL)));
	int val = 1;
	int semid = CreateSem(1234,&val,1);
	assert(semid != -1);
	int count = 0;	
	while(1)
	{
		SemP(semid, 0);
		printf("%s\n",argv[1]);
		int n = rand() % 4;
		sleep(n);
		SemV(semid,0);
		n = rand() % 4;
		sleep(n);
		count++;
		if(count == 5)
		{
			break;
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值