目录
信号量概述:
一:信号量的本质
信号量(semaphore)的数据结构为一个值和一个指针,指针指向等待该信号量的下一个进程。信号量的值与相应资源的使用情况有关。==当它的值大于0时,表示当前可用资源的数量;当它的值小于0时,其绝对值表示等待使用该资源的进程个数。注意,信号量的值仅能由PV操作来改变。
二:信号量的机制
信号量用于实现进程间的互斥与同步,而不是用于存储进程间的通信数据。举例子来理解这句话:
把一个临界资源(也可以认为是共享内存)比作一个上了锁的房间,某人比作一个进程,而开锁的钥匙就是信号量。某人有钥匙的话,就能开锁进入房间里,做它需要做的事情,如果这时候有第二个人想进入房间里的话,是进不去的,因为它没有钥匙(信号量(钥匙)<0)。直到进去的那个人出了房间把钥匙放在某个地方(使信号量(钥匙)>=0),然后第二人拿到了钥匙,才能渠道房间里去。这钥匙起到的作用就是信号量。若有100把钥匙,可以使101个人同时进入房间。
PV操作的定义:
P:
①将信号量S的值减1,即进行S = S-1;
②如果S < 0,则该进程进入阻塞队列;
③如果S >= 0, 则该进程继续执行;
④执行一次P操作其实就是意味请求分配一个资源。
V:
①将信号量S的值加1,即 S = S + 1;
②如果S > 0,则该进程继续执行;
③如果S < 0, 则释放阻塞队列中的第一个等待信号量的进程;
④执行一次V操作其实就是意味释放一个资源。
相关的API
头文件:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
①int semget(key_t key,int num_sems,int sem_flags);
功能:(semphore get)创建或获取信号量,返回信号量D。
参数:
key:信号量索引值,可使用ftok获取
num_sems:指定需要的信号量数目,它的值几乎总是1
sem_flags:一般用IPC_CREAT 加上权限创建信号量集
返回值:成功返回信号集的ID;失败返回 ==-1 ==;
②int semop(int semid,struct sembuf semoparray[ ],size_t semops);
功能:(semphore operation)它的作用是改变信号量的值(PV操作)
参数:
semid:目标信号量的ID号
semoparray[]:结构体数组,它有三个成员:
(1)sem_num:需要操作第几个信号量;
(2)sem_op:信号量的值,-1为p操作(拿钥匙);1为v操作(放钥匙)。
(3)sem_flg:一般赋值SEM_UNDO相当于wait;
semops :semoparray[] 数组的个数。
返回值: 成功返回0;失败返回 -1;
③int semctl (int semid,int sem_num,int cmd,…)
如果有第四个参数,它通常是一个union semun结构,定义如下:
union semun {
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO
(Linux-specific) */
};
控制信号量函数。用来控制信号,比如初始化,移除信号量等。
参数说明:
1. semid 被操作目标信号量的ID号;
2. sem_num 信号量的个数,即需要操作第几个信号量;从0开始计数。
3. cmd 操作指令,它有几个宏,常用一般用SETVAL (初始化信号量的值相当于钥匙的数量)IPC_RMID:用于删除一个信号量标识符。
4. 其他 这个参数,是配合SETVAL指令来使用,它是个联合体而且需要定义。
例子
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include<stdio.h>
#include<stdlib.h>
// int semget(key_t key, int nsems, int semflg);
// int semctl(int semid, int semnum, int cmd, ...);
union semun {
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO
(Linux-specific) */
};
void Pop(int semid){
int ret;
struct sembuf sembuf[1];
sembuf[0].sem_num=0;
sembuf[0].sem_op=-1;
sembuf[0].sem_flg=SEM_UNDO;
ret=semop(semid,sembuf,1);
if(ret == -1){
printf("get key faied\n");
}
else{
printf("get key sucess\n");
}
}
void Vop(int semid){
int ret;
struct sembuf sembuf[1];
sembuf[0].sem_num=0;
sembuf[0].sem_op=1;
sembuf[0].sem_flg=SEM_UNDO;
ret=semop(semid,sembuf,1);
if(ret == -1){
printf("return key faied\n");
}
else{
printf("return key sucess\n");
}
}
int main(){
key_t key;
int semid;
int pid;
union semun semclt;
semclt.val=0; //信号量的数量
key=ftok(".",6);
semid=semget(key,1,IPC_CREAT|0666);
semctl(semid,0,SETVAL);//删除信号量ID
pid=fork();
if(pid>0){
Pop(semid); // 减去一个信号量,由于<0程序阻塞
printf("this is parent pid\n");
semctl(semid,0,IPC_RMID,semctl);
}
else if(pid==0){
printf("this is child pid\n");
Vop(semid);//加上一个信号量。使信号量=0父进程运行
}
else{
printf("fork faile\n");
}
return 0;
}