进程互斥:当一个进程访问某临界资源时,另一个想要访问该临界资源的进程必须等待。当前访问该临界资源的进程访问结束并释放该临界资源后,另一个进程才能进行访问。
do {
entry_section; // 进入区,检查并上锁
critical_section; // 临界区
exit_section; // 退出区,解锁
remainder_section; // 剩余区
} while (true)
为实现对临界资源的访问,同时保证系统的整体性能,需要遵循以下原则:
算法思想:两个进程在访问完临界区之后,会把访问权限赋给另一个进程。也就是说,访问临界区的权限只能由另一个进程赋予。
int turn = 0; // 表示当前运行进入临界区的进程号
// 进程 0
while(turn != 0);
critical_section;
turn = 1;
remainder_section;
// 进程 1
while(turn != 1);
critical_section;
turn = 0;
remainder_section;
问题:当进程1使用完临界区之后,假设进程0一直不适用临界区,那么进程1就始终无法再次进入临界区,违反了“空闲让进”原则。
算法思想:设置一个布尔型数组 flag[],用于表示各个进程进入临界区的意愿。如果flag[0] = true则表示0号进程想要进入临界区。
bool flag[2];
flag[0] = false;
flag[1] = false;
// 进程 0
while(flag[1]); // 如果对方要进入临界区,则等待
flag[0] = true;
critical_section;
flag[0] = false;
remainder_section;
// 进程 1
while(flag[0]); // 如果对方要进入临界区,则等待
flag[1] = true;
critical_section;
flag[1] = false;
remainder_section;
主要问题:检查和上锁的操作无法一气呵成,如果发生进程切换,会违反忙则等待原则。
算法思想:先上锁,后检查,避免出现1.2的问题。
bool flag[2];
flag[0] = false;
flag[1] = false;
// 进程 0
flag[0] = true;
while(flag[1]); // 如果对方要进入临界区,则等待
critical_section;
flag[0] = false;
remainder_section;
// 进程 1
flag[1] = true;
while(flag[0]); // 如果对方要进入临界区,则等待
critical_section;
flag[1] = false;
remainder_section;
主要问题:如果进程上锁后立马发生进程切换,会造成死锁,违背了空闲让进和有限等待原则。
算法思想:结合单标志法、双标志法的思想,既设置各自使用临界区的意愿,又增加谦让标志。
bool flag[2];
flag[0] = false;
flag[1] = false;
turn = 0; // 表示谦让给进程0使用
// 进程 0
flag[0] = true; // 我想要使用临界区
turn = 1; // 谦让给进程1优先使用
while(flag[1] && turn == 1); // 如果对方要进入临界区,则等待
critical_section;
flag[0] = false;
remainder_section;
// 进程 1
flag[1] = true; // 我想要使用临界区
turn = 0; // 谦让给进程1优先使用
while(flag[0] && turn == 0); // 如果对方要进入临界区,则等待
critical_section;
flag[1] = false;
remainder_section;
主要问题:仍旧未遵循让权等待原则。
...
关中断指令; // 关中断之后不允许当前进程被中断,自然不会发生进程切换
critical_section;
开中断指令;
...
优点:简单、高效
缺点:不适用于多处理机环境,关中断指令仅对当前处理机有效;关中断指令为特权指令,只适用于内核程序。
又称为TestAndSetLock,简称TS或TSL指令,由硬件实现,不可被中断。
bool lock = true; // 布尔型共享变量,表示临界资源是否被加锁
bool TestAndSet(bool *lock) {
bool old;
old = *lock;
*lock = true;
return old;
}
// 进程中:
while (TestAndSet(&lock));
critical_section;
lock = false;
remainder_section;
优点:实现简单;适用于多处理机环境。
缺点:不满足让权等待。
又称Exchange指令,或 XCHG.
// 交换两个变量的值
swap(bool *a, bool *b){
bool tmp = *a;
*a = *b;
*b = tmp;
}
// lock 表示当前临界资源是否被上锁
bool old = true;
while (old)
swap(&old, &lock);
critical_section;
lock = false;
remainder_section;
优缺点同 TSL。