软件实现
单标志
int turn = 0; // 进程ID
// P0进程
while (turn != 0); // 进入区(死循环)
critical section; // 临界区
turn = 1; // 退出区
remainder section; // 剩余区
// P1进程
while (turn != 1);
critical section;
turn = 0;
remainder section;
两个进程交替执行。如果P0将turn修改为0后不再访问临界区,P1进程修改turn为0后,turn的值永远为0,P1无法再访问临界区,违背了空闲让进的原则。
双标志先检查
bool flag[2]; // flag[0]表示P0进入临界区, flag[1]表示P1进入临界区
// P0进程
while (flag[1]);
flag[0] = true;
critical section;
flag[0] = false;
remainder section;
// P1进程
while (flag[0]);
flag[1] = true;
critical section;
flag[1] = false;
remainder section;
但是按照如下执行顺序(两个while判断时标志均未修改,巧了~)
P0将要进入临界区,判断flag[1]为false,此时flag[0]还是false;- 此时
P1判断flag[0]是flase,可以进入临界区; - 两个进程同时访问了临界区
违背了忙则等待的原则。
双标志后检查
bool flag[2]; // flag[0]表示P0进入临界区, flag[1]表示P1进入临界区
// P0进程
flag[0] = true;
while (flag[1]);
critical section;
flag[0] = false;
remainder section;
// P1进程
flag[1] = true;
while (flag[0]);
critical section;
flag[1] = false;
remainder section;
违背了空闲让进、有限等待的原则(都将标志改为true后再判断,都进不去了~)
Peterson算法
bool flag[2];
int turn = 0;
// P0进程
flag[0] = true
turn = 1;
while (flag[1] && turn == 1);
critical section;
flag[0] = false;
remainder action;
// P1进程
flag[1] = true
turn = 0;
while (flag[0] && turn == 0);
critical section;
flag[1] = false;
remainder action;
违背了让权等待的原则,while判断始终执行,占用CPU。
硬件实现
中断屏蔽
禁止一切中断,CPU执行完临界区前不会切换。
缺点:
- 可能会被滥用
- 关中断时间长影响效率
- 不适用于多处理机
- 只适用于内核态进程
Test-And-Set(TS指令)
bool TestAndSet(bool *lock)
{
bool old;
old = *lock;
*lock = true; // 加锁
return old; // 返回原来的lock
}
while (TestAndSet(&lock));
// 临界区
*lock = false; // 解锁
// 剩余区
原子操作,不会被打断。但是违背了让权等待。
Swap指令
// lock表示临界区是否被加锁
bool old = true;
while (old)
swap(&lock, &old);
// 临界区
lock = false; // 解锁
// 剩余区
但是违背了让权等待。
信号量
PV操作:等待唤醒机制
信号量:停车场内的空余车位,共享资源剩余数量
- 整数型信号量(死循环→忙等待)
int S = 1; // 初始化,表示可用资源数
// wait原语,相当于进入区
void wait(int S)
{
while (S <= 0); // 资源不够,循环等待 违背“让权等待”,发生忙等待
S -= 1; // 如果资源数够,占用一个(或多个)
}
// signal原语,相当于退出区
void signal(int S)
{
S += 1; // 使用资源后释放
}
/*******************************/
// 进程Pn
...
wait(S); // 进入区,申请资源
访问共享资源; // 临界区
signal(S); // 退出区,释放资源
...
- 记录型
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)
{
wakeup(S.L); // wakeup原语唤醒队头进程(阻塞→就绪)
}
}