**
信号量
**
一、信号量的概念
信号量是一个特殊的变量(计数器),其值大于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;
}
}
}