###概念###
场景:某个线程(进程)能从信号拿到锁,则运行,否则阻塞等待。
信号量:可以理解为信号集中某个信号当前锁的数值
正值:尚可接受的进程数 0:无可用,无等待 负值:阻塞等待在该信号上的进程数
###PV操作###
p:信号量-1 v:信号量+1
P: 进程从信号锁池拿锁,能拿到,则运行;否则,若锁池已空,则阻塞等待。
V: 进程释放锁到信号锁池,若锁池已满(一般不会),则阻塞等待;
否则,释放锁之后,若有等待的进程,系统会唤醒一个等待在该信号上的进程继续执行。
###信号量实现线程互斥###
可以认为信号量关联一组线程,保存一个指针,指向线程数组的首地址;
比如当前信号量为-1,一个线程对其进行P操作,信号量变为-2,说明没有拿到锁,线程等待;
此时,取值为-2,说明有两个线程等待在该信号上;
这个时候,其他线程进行V操作,信号量加1,为-1,信号量通知等待的线程中,第一个线程继续执行,第二个线程继续等待。
也就是说,P操作等待的情况是减1后,信号量小于0;
P操作继续执行的情况有两种:a、减1后,信号量大于等于0,不需等待,直接执行;
b、减1后,信号量小于0,等待中,其他人进行了V操作,通知这个线程,继续执行。
###函数接口###
1.int semget(key_t key, int num_sems, int sem_flags); //创建一个新信号量(集)或取得一个已有信号量(集)
//key: 关联信号量semid的键值 e.g. (key_t)201808
//num_sems: 信号集中信号的数量 一般为1
//sem_flgs: IPC_EXCL - 检查是否存在 IPC_CREAT - 创建
// 位或组合 IPC_CREAT | IPC_EXCL则可以创建一个新的,唯一 的信号量,如果信号量已存在,返回一个错误
//返回值:成功返回一个相应信号标识符(非零),失败返回-1
2.int semop(int sem_id, struct sembuf *sops, size_t nsops); //操作信号量
//sem_id: 信号量标识符
//struct sembuf *sops: 对应一个特定信号的操作
//nsops: 要进行操作的信号的个数 一般为1
- struct sembuf{
- unsigned short sem_num; //信号在信号集中的索引,0代表第一个信号,1代表第二个信号
- short sem_op; //操作类型
- short sem_flg; //操作标志
- };
sem_flg: IPC_NOWAIT 无阻塞等待,特殊临界情况直接返回EAGAIN
SEM_UNDO
该参数可设置为 IPC_NOWAIT 或 SEM_UNDO 两种状态。只有将 sem_flg 指定为 SEM_UNDO 标志后,semadj (所指定信号量针对调用进程的调整 值)才会更新。 此外,如果此操作指定SEM_UNDO,系统更新过程中会撤消此信号灯的计数(semadj)。此操作可以随时进行---它永远不会强制等待的过 程。调用进程必须有改变信号量集的权限。
sem_flg公认的标志是 IPC_NOWAIT 和 SEM_UNDO。如果操作指定SEM_UNDO,当该进程终止时它将会自动撤消。
sem_op > 0 : V操作,信号量加上对应的值,说明进程在释放锁
sem_op < 0: P操作,信号量减去对应的绝对值,说明进程在请求锁
sem_op = 0:
//返回值:成功返回 0,失败返回 -1
//该函数所做的对于信号量的操作都是原子操作,即整个行为是一个整体,是不可打断的;
//所有操作是否可以立即执行,取决于sem_flg的IPC_NOWAIT标志是否存在。
3.int semctl(int sem_id, int sem_num, int command, ...);
//sem_id: 信号量标识符
//sem_num: 信号集中信号的索引值 (第一个0,第二个1)
//command:命令类型
IPC_STAT:获取某个信号量集合的semid_ds结构,并将其储存在semun联合体的buf参数所指的地址之中
IPC_SET:设置某个集合的semid_ds结构的ipc_perm成员的值,该命令所取的值是从semun联合体的buf参数中取到的
IPC_RMID:从内核删除该信号量集合
GETALL:用于获取集合中所有信号量的值,整数值存放在无符号短整数的一个数组中,该数组有联合体的array成员所指定
GETNCNT:返回当前正在等待资源的进程的数目
GETPID:返回最后一次执行PV操作(semop函数调用)的进程的PID
GETVAL:返回集合中某个信号量的值
GETZCNT:返回正在等待资源利用率达到百分之百的进程的数目
SETALL:把集合中所有信号量的值,设置为联合体的array成员所包含的对应值
SETVAL:将集合中单个信号量的值设置为联合体的val成员的值
//第四个参数:某些特定操作用到
其中semun联合体的结构如下:
- union semun{
- int val;
- struct semid_ds *buf;
- unsigned short *array;
- struct seminfo *__buf;
- };
对于该函数,只有当command取某些特定的值的时候,才会使用到第4个参数,第4个参数它通常是一个union semun结构,定义如下:
- union semun{
- int val;
- struct semid_ds *buf;
- unsigned short *arry;
- };
当执行SETVAL命令时用到这个成员,他用于指定要把信号量设置成什么值,涉及成员:val
在命令IPC_STAT/IPC_SET中使用,它代表内核中所使用内部信号量数据结构的一个复制 ,涉及成员:buf
在命令GETALL/SETALL命令中使用时,他代表指向整数值一个数组的指针,在设置或获取集合中所有信号量的值的过程中,将会用到该数组,涉及成员:array
###测试例程###
/*ĐĹşĹÁż sem.c*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/sem.h>
#include <iostream>
using namespace std;
union semun{
int val;
struct semid_ds *buf;
unsigned short *arry;
};
static int sem_id = 0;
static int set_semvalue();
static void del_semvalue();
static int semaphore_p();
static int semaphore_v();
static int getsemval();
static void childforkProc();
static void childforkProc(){
int sn=getsemval();
cout << "[" << getpid() << ":] " << "semNum:" << sn << " " << endl;
semaphore_p();
std::cout << "[" << getpid() << ":] " << "critical region opperator..." << std::endl; //临界共享区操作
sleep(5);
semaphore_v();
exit(-1);
}
//带参数第一次运行 不带参数第二次运行
// ./sem 1 & ./sem
int main(int argc,char *argv[]){
char message='F';
int i=0;
int flg=0;
//创建信号量
sem_id = semget((key_t)1234,1,0666|IPC_CREAT);
if(!set_semvalue()){ //初始化信号量
fprintf(stderr, "Failed to initialize semaphore\n");
exit(EXIT_FAILURE);
}
for(int i=0;i<5;i++){
flg=fork();
if(flg > 0){
//no do
}else if(flg ==0){ //子进程
childforkProc();
}
}
if(flg > 0){
sleep(100);
del_semvalue();
}
return 0; //exit(EXIT_SUCCESS);
}
static int set_semvalue(){
//用于初始化信号量 在使用信号量前必须这样做
union semun sem_union;
sem_union.val=1;
if(semctl(sem_id,0,SETVAL,sem_union) == -1){
return 0;
}
return 1;
}
static void del_semvalue(){
//删除信号量
union semun sem_union;
if(semctl(sem_id,0,IPC_RMID,sem_union) == -1){
fprintf(stderr,"failed to delete semaphore \n");
}else{
fprintf(stdout,"has deled semaphore \n");
}
}
static int semaphore_p(){
//信号量减1操作 即等待P(s)
struct sembuf sem_b;
sem_b.sem_num=0; //第一个信号
sem_b.sem_op=-1; //P() 结果 >= 0 ,执行
sem_b.sem_flg=SEM_UNDO;
if(semop(sem_id,&sem_b,1) == -1){
fprintf(stderr, "semaphore_p failed\n");
return 0;
}
return 1;
}
static int semaphore_v(){
//释放操作 使信号量变为可用 即发送信号V(s)
struct sembuf sem_b;
sem_b.sem_num=0; //第一个信号
sem_b.sem_op=1; //V()
sem_b.sem_flg=SEM_UNDO;
if(semop(sem_id,&sem_b,1)==-1){
fprintf(stderr, "semaphore_v failed\n");
return 0;
}
return 1;
}
//获取当前信号量的的值
static int getsemval(){
int num=0; //无可用 无等待
num=semctl(sem_id,0,GETVAL);
return num;
}
###参考###
https://blog.csdn.net/qq_30168505/article/details/53041825
https://www.cnblogs.com/nzbbody/p/4219957.html