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

信号量:实现两个进程的同步,对于进程执行的同步控制

特殊的一个变量(计数器),其值大于0时,纪录临界资源的个数;当值小于0时,对此信号量执行P操作(-1),会阻塞,直到信号量的值大于0,或者有其他进程在此信号量上执行了V操作。

信号量的相关操作:

1、临界资源:临界资源是指每次仅允许一个进程访问的共享资源,即各进程采取进程互斥的方式,实现共享的资源称作临界资源。属于临界资源的硬件有打印机、磁带机等,软件有消息缓冲队列、变量、数组、缓冲区等。

2、临界区: 每个进程中访问临界资源的那段代码称为临界区

  • 每次只允许一个进程进入临界区,进入后不允许其他进程进入。
  • 任何临界资源,多个进程必须互斥的对它进行访问

3、原子操作:不可被中断的一个或一系列操作,即临界区的代码不会被这个临界数据的其它临界区的代码打断。这种操作一旦开始,就一直运行到结束。

4、PV操作:PV操作与信号量的处理相关,P表示通过的意思,V表示释放的意思。所谓信号灯,实际上就是用来控制进程状态的一个代表某一资源的存储单元。

  • P 操作(等待操作):如果信号量sv的值大于0,就给他 -1,如果sv的值等于0,就挂起该进程。
  • V 操作(发送信号):如果有其他进程因等待sv而被挂起,就让他恢复运行;如果没有进程因等待sv而挂起,就给他+1。

信号量的相关函数:Linux系统内核维护是一个信号量集

1、semget函数:创建或获取已存在的信号量

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

  • key:用户标识,如果多个进程想通过同一个信号量完成数据通信,则每个进程使用相同的key值创建或者获取同一个消息队列的内核对象ID值

  • nsems:在创建信号量时,其指定信号量集中信号量的个数。

  • flag:指定一个权限操作,IPC_CREAT:新建一个信号量集对象,IPC_EXCL:与IPC_CREAT一同使用,表示如果要创建的信号量集对象已经存在,则返回错误。

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

2、semctl函数: 一次只能操作一个信号量

int semctl(int semid, int semnum, int cmd, ...)

  •  semid:信号量集标识符,senget的返回值

  • semnum:一个数组,表示信号量的下标

  • cmd:要执行的操作
    IPC_STAT读取一个信号量集的数据结构semid_ds,并将其存储在semun中的buf参数中。
    IPC_SET设置信号量集的数据结构semid_ds中的元素ipc_perm,其值取自semun中的buf参数。
    IPC_RMID删除一个信号量集。

3、semop函数:对信号量的操作,p操作或者v操作

int semop(int semid,struct *buf,int len)

  • 成功返回0,失败返回-1;
  • struct sembuf
    {
       int sem_num;//信号量的下标
       int sem_op;//-1:P操作  1:V操作
       int sem_flg;//设置方式,SEM_UNDO
    }

     

通过把上面三种方法进行封装,封装成我们使用的下面几种方法:

int CreateSme( int  key, int  init_val, int  len);//创建一个信号量

  • 对于获取而言,只需要key,因为key可以找到内核对象,返回id值
  • 对于创建这三个变量都必须有

void SemP( int  semid,int  index);//P操作,一次调用只操作一个信号量

  • semid是CreateSme的返回值
  • index设置的下标

void SemV( int  semid,int  index);//V操作,一次调用只操作一个信号量

  • semid是CreateSme的返回值
  • index设置的下标

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>
//1、如果内核已经有key值对应的信号量,则直接返回
//2、如果没有,2.1先去创建此信号量集  2.2所有的信号量根据init_val的值进行初始化
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;
		}
	}
}

用命令管理:

ipcs -s :chakan

ipcrm -s semid:删除信号量

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值