信号量:以保护进程互斥与同步为目的,本质上为计数器,记录与统计临界资源的数目。
当请求一个使用信号量来表示的资源时,进程需要先读取信号量的值来判断资源是否可用:
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 {将此联合中val初始化值。
int val; // 使用的值
struct semid_ds *buf; // IPC_STAT、IPC_SET 使用缓存区
unsigned short *array; // GETALL,、SETALL 使用的数组
struct seminfo *__buf; // IPC_INFO(Linux特有) 使用缓存区
};
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
结果如下: