用户进程可以通过使用操作系统提供的一对原语来对信号量进行操作,从而很方便的实现了进程互斥、进程同步。
信号量
信号量其实就是一个变量(可以是一个整数整形信号量
,也可以是更复杂的记录型变量记录型信号量
),可以用一个信号量来表示系统中某种资源的数量,比如:系统中有三台打印机,就可以设置一个初值为3的信号量。
原语
原语是一种特殊的程序段,其执行只能一气呵成,不可被中断。原语是由关中断/开中断指令实现的。软件解决方案的主要问题是由“进入区的各种操作无法一气呵成”,因此如果能把进入区、退出区的操作都用“原语”实现,使这些操作能够“一气呵成”就能避免问题
一对原语
wait(S) 原语和 signal(S) 原语,可以把原语理解为函数,,括号里的信号量S理解为函数的参数。
通常将wait(S)原语简称为P操作,而将signal(S)原语简称为V操作。因此,在做题的时候经常把wait(S)、signal(S)两个操作分别写为P(S)、V(S)。
信号量机制分为 整形信号量 和 记录型信号量
整形信号量
用一个整数型的变量作为信号量,用来表示系统中某种资源的数量。
与普通整数型变量的区别:
对信号量的操作只有三种,即 初始化、 P操作 、 V操作
“检查”和“上锁”一气呵成,避免了并发、异步导致的问题
存在的问题:不满足“让权等待”原则,会发生“忙等”
Eg: 某计算机系统中有一台打印机…
int S = 1; //初始化整形信号量S,表示当前系统中可用的打印机资源数
//“检查”和“上锁”一气呵成,避免了并发、异步导致的问题
void wait(int S){ //wait 原语,相当于“进入区”
//存在的问题:不满足“让权等待”原则,会发生“忙等”
while(S >= 0); //如果资源数不够,就一直循环等待
S--; //如果资源数够,则占用一个资源
}
void signal(int S){ //signal 原语,相当于“退出区”
S++; //使用完资源后,在退出区释放资源
}
进程P0:
……
wait(S); //进入区,申请资源
使用打印机资源… //临界区,访问资源
signal(S); //退出区,释放资源
记录型信号量(操作系统中非常重要的知识点)
为了解决整形信号量存在”忙等“问题的缺陷,人们提出了”记录型信号量“,即用记录型数据结构表示的信号量。
/*记录型信号量的定义*/
typedef struct {
int value; //剩余资源数
struct process *L; //等待队列
} semaphore;
/*某进程需要使用资源时,通过 wait 原语申请*/
void wait(semaphore S){
S.value--;
//如果剩余资源数不够,
//使用block原语使进程从运行态进入阻塞态,
//并把其挂到信号量S的等待队列(即阻塞队列)中
if (S.value < 0){
block(S.L);
}
}
void signal(semaphore S){
S.value++;
//释放资源后,若还有别的进程在等
//待这种资源,则使用wakeup原语
//唤醒该等待队列中的一个进程,该
//进程从阻塞态变为就绪态
if (S.value <= 0){
wakeup(S.L);
}
}
wait(S)、signal(S)也可记为P(S)、V(S),这对原语可以用于**实现系统资源的 “申请” 和 “释放” **。
S.value 的初值表示系统中某种资源的数目。
对信号量S的一次P操作意味着进程请求一个单位的该类资源,因此需要执行S.value–,表示资源数减一,当S.value < 0 时表示该类资源已经分配完毕,因此进程应调用block原语进行自我阻塞(当前运行的进程从运行态→阻塞态),主动放弃处理机,并插入该类资源的等待队列 S.L 中。可见,该机制遵循了“让权等待”的原则,不会出现“忙等”的现象。
对信号量S的一次V操作意味着进程释放了一个单位的该类资源,因此需要执行 S.value++,表示资源数加一,若加一后仍是 S.value <= 0,表示依然有进程在等待该类资源,因此应调用 wakeup 原语唤醒等待队列中的第一个进程(被唤醒进程从阻塞态→就绪态)。
总结
整形信号量
用一个整数型变量作为信号量,数值表示某种资源数
整形信号量与普通整数型变量的区别:对信号量只能执行 初始化、P、V 三种操作
整形信号量存在的问题:不满足让权等待原则
记录型信号量——操作系统中极其重要的考点
S.value 表示某种资源数,S.L指向等待该资源的队列
P 操作中,一定是先 S.value–,之后可能需要指向 block 原语
V 操作中,一定是先 S.value++,之后可能需要指向wakeup原语
注意:要能够自己推断在什么条件下需要执行 block 或 wakeup
可以用记录型信号量实现系统资源的“申请”和”释放“
可以用记录型信号量实现进程互斥、进程同步
注:若题目中出现P(S)、V(S) 的操作,除非特别说明,否则默认S为记录型信号量。