临界资源:表示该资源一次只允许一个进程使用,也叫互斥资源或共享变量
临界区(互斥区):各个进程对某个临界资源实施操作的程序片段
解决进程互斥的方法
软件方案:
1、加锁(对free加锁)
...
//封装成原语lock()
while(free);
free = true;
//lock end
//临界区
//解锁
free = false;
...
2、DEKKER算法
进程p1:
pturn = true;
while(qturn) {
if(turn == 2){
pturn = false;
while(turn == 2);
pturn = true;
}
}
//临界区
turn = 2;
pturn = false;
进程p2:
qturn = true;
while(pturn) {
if(turn == 1){//turn==1表示让进程p1进入临界区
qturn = false;
while(turn == 1);
qturn = true;
}
}
//临界区
turn = 1;
qturn = false;
缺点:浪费cpu资源,忙等待
3、PETERSON算法
...
enter_region[i];
//临界区
leave_region[i];
...
#define FALSE 0
#define TRUE 1
#define N 2 //只有两个进程
int turn; //轮到哪个进程号
int interested[N]; //表明进程是否有意愿进入临界区,初始值为{0, 0}
void enter_region(int process){//process进程号表示0或1
int other;//另外一个进程的进程号
other = 1 - process;
interested[process] = TRUE;
turn = process;
while(turn == process && interested[other] == TRUE);
}
void leave_region(int process) {
interested[process] = FALSE;
}
硬件解决方案
1、中断屏蔽
互斥这种问题实质是由于多个进程不断的中断、上时间片造成的,因此最直接的办法是:
在进入临界区之前关闭中断指令,出临界区后打开中断,目的在于进入临界区时不允许中断,从而避免多个进程同时进入一个临界区
优点:简单高效
缺点:限制并发能力;不适用于多处理器;由于这种操作属于特权指令,仅适用于操作系统本身,不适用于用户进程
2、“测试并加锁”指令
enter_region:
TSL REGISTER,LOCK ;复制锁到寄存器、并将锁置1
CMP REGISTER,#0 ;判断寄存器的值是否为0
JNE enter_region ;若不是0,跳转到enter_region
RET ;是0则返回,进入临界区
leave_region:
MOVE LOCK,#0 ;将锁置0
RET
可以看出,这也是一种忙等待
3、交换指令
原理与2相似
在单处理情况下,忙等待必然不是一个好方法,但是在多核处理器的环境下,可以让进程一直在某个cpu上运行而不中断切换,这时候忙等待的开销足以抵过进程上下文切换带来的开销,因此忙等待的思路又被重新提出且人们给他起了一个高大上的名字叫自旋锁(spin lock)