一、进程同步
进程同步机制的主要任务,是对多个相关进程在执行次序上进行协调,使并发执行的诸进程之间能按照一定的规则(或时序)共享系统资源,并能很好地相互合作。
1、临界区
人们把在每个进程中访问临界资源的那段代码称为临界区。
包括四部分:
进入区:检查是否可进入临界区,若可进入,需要“上锁”。
临界区:访问临界资源的那段代码。
退出区:负责“解锁”。
剩余区:其余代码部分。
2、同步机制应遵循的规则
(1)空闲让进
临界区空闲时,可以允许一个请求进入临界区的进程立即进入临界区。
(2)忙则等待
当已有进程进入临界区时,其他试图进入临界区的进程必须等待。
(3)有限等待
对请求访问的进程,应保证能在有限时间内进入临界区。
(4)让权等待
当进程不能进入临界区时,应立即释放处理机,防止进程忙等待。
二、进程同步工具(信号量机制)
用户进程可以通过使用操作系统提供的一对原语来对信号量进行操作,从而很方便的实现了进程同步。
信号量其实是一个变量,可以用一个信号量来表示系统中某种资源的数量。
原语:是一种特殊的程序段,其执行只能一气呵成,不可被中断,原语是由关中断/开中断指令实现的。
一对原语:wait(s)和signal(s)原语。wait、signal原语通常简称为P、V操作。
1、整型信号量
用一个整数型的变量作为信号量,用来表示系统中某种资源的数量。
存在的问题:不满足让权等待。
2、记录型信号量
为解决“忙等”问题,提出记录型信号量。
//记录型信号量的定义
typedef struct{
int value; //剩余资源数
struct process *L; //等待队列
}semaphore;
//某进程需要使用资源时,通过wait原语申请
void wait(semaphore S){
S.value--;
if(S.value < 0){
block(S.L);//如果剩余资源数不够,使用block原语使进程从运行态进入阻塞态。
}
}
//进程使用完资源后,通过signal原语释放
void signal(semaphore S){
S.value++;
if(S.value <= 0){
wakeup(S.L)//释放资源后,若还有等待的,则使用wakeup原语唤醒等待的一个。
}
}
执行P操作:S-1
若S>=0 表示该类资源足够,继续进行。
若S<0 表示该类资源已分配完毕,进行自我阻塞。
执行V操作:S+1
若S>0 表示没有进程在等待。
若S<=0 表示仍有进程在阻塞等待,唤醒等待队列中的第一个进程。
例:假设有一个临界资源S=1。
进程1进行P操作:S-1 = 0。判断S>=0,可以继续进行。
进程2进行P操作:S-1 = -1。判断S<0,表示无可用资源,进行自我阻塞。
此时临界资源S = -1。
进程1进行V操作:S+1 = 0.判断S<=0,表示仍有进程在等待,唤醒进程2。