带*是重点
同步、互斥、临界区*
临界区: 一个时间段内只允许一个进程使用的资源被称为临界资源,而对临界资源进行访问的那段代码被称为临界区。
同步: 直接制约关系,协调进程的工作次序.
互斥: 间接制约关系,多个进程在同一时刻只有一个进程能进入临界区。
进程同步是为了解决进程特征中的异步性。
进程互斥的实现方式
需要遵循的规则
- 空闲让进:临界区空闲时,应允许一个进程访问
- 忙则等待:临界区正在被访问时,其他要访问的进程需要等待
- 有限等待:要在有限时间内进入临界区,保证不会饥饿
- 让权等待:进不了临界区的进程,要释放处理机,防止忙等
1. 软件实现
- 单标志法:两个进程在访问完临界区后会吧使用临界区的权限转交给另一个进程。
违背:空闲让进 - 双标志先检查法:设置一个布尔型数组 flag[],标记各进程想进入临界区的意愿,每个进程在进入前都需要检查有没有别的进程要进入。
违背:忙则等待 - 双标志后检查法:即将检查和上锁调换位置。
违背:空闲让进、有限等待 - Peterson 法:结合单标志法和双标志法,单标志用于表示优先让那个进程进入临界区(双方竞争则主动让出)。
违背:让权等待
2. 硬件实现
- 中断屏蔽法:使用开关中断原语,简单高效,不适用于多处理机。
- TestAndSet 指令:简称 TSL 指令,可用于多处理机,但不满足让权等待。
- Swap 指令:功能和逻辑类似于 TSL 指令。
信号量机制*
前面互斥实现的方式都无法实现让权等待原则,而信号量机制则符合所有原则。
概念
信号量是一个变量(整数/记录型),和单标志法一样用于表示系统中某种资源的数量,可以用操作系统提供的一对原语来对信号量进行操作,从而实现进程的互斥同步。
原语
- wait(S):简称为 P 操作,S 为信号量
- signal(S):简称为 V 操作,S 为信号量
互斥实现
1.整形
int S = 1;//一台打印机
void wait(int S){
while(S<=0);
S=S-1;
}
void signal(int S){
S=S+1;
}
//进程
...
wait(S);//P操作
//使用打印机
signal(S);//V操作
2.记录型
typedef struct {
int value;//剩余资源数
Struct process *L;//等待队列
} semaphore;
void wait(semaphore S){
S.value--;
if(S.value<0)
block(S.L);//block原语阻塞队列,由于进入阻塞态,所以遵循了 让权等待
}
void signal(semaphore S){
S.value++;
if(S.value<=0){
wakup(S,L);//唤醒等待队列中的第一个进程
}
}
3.总结
semaphore mutex = 1;//初始化信号量(简写形式)
semaphore mutex2 = 2;//不同的临界资源需要设置不同的互斥信号量
void P1(){
...
P(mutex);
//临界区...
V(mutex);
...
}
void P2(){
...
P(mutex);
//临界区...
V(mutex);
...
}