一种资源

临界资源:进程不能共享的计算机资源,只分别单独访问的关系。比如打印机,一时刻只能有一个进程使用

两大核心点

互斥:竞争临界资源

同步:进程间的先后顺序,相互合作处理上的前后关系

四项基本原则

空闲让进:当需要的资源处于空闲的时候,任何进程都有资格使用它。

忙则等待:当有进程使用临界资源时,其他进程要等待

让权等待:不可进入临界区使用时,释放CPU,避免忙等

有限等待:在有限时间可以访问临界资源。避免“永远等待”

类成功的机制

硬件机制

禁止中断

使用禁止中断,原理时发生中断时系统不会发生CPU从一个进程切换到另外一个进程中的情况,其他进程不可能进入临界区执行。中断—>完成后—>中断结束。

简单,但是容易出现系统不知名故障,以及需要给普通用户禁止中断的权限。而且,只能用于单处理机系统,因为只能中断一个处理机的指令

TSL指令

TSL指令全称为Test and Set Lock 指令,意为检测并上锁。这种指令一般用于计算机多处理机系统,为专用的机器指令。

直接说缺点:1.存在空循环浪费计算机资源 2.可能存在现象

软件方式

面包店算法,容易在多进程时出现问题

锁机制

类似硬件方式的TSL。一样存在空循环浪费CPU资源

整型信号量

初始化操作

P/V两个原语操作,P表示测试,V表示增加。后面用wait()表示P,用singal()表示V

1
2
3
4
5
6
7
wait(S){
while(S<=0);
S--;
}
singal(S){
S++
}

还是违背了让权等待的原则 ,计算机一直以空循环消耗计算机资源

成功的机制

记录型信号量

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//信号量的数据结构
typedef struct{
int value;
struct PCB *list;
} semaphore;



void wait(S){ //请求资源
S.value--;
if(S.value < 0) block(S.list); //无可用资源,阻塞进程
}

void signal(S){ //释放资源
S.value++;
if(S.value <= 0) wakeup(S.list);//唤醒进程
}

wait()通过判断减少一个资源是否可以继续进程,如果可以,进程继续进行,如果不行,那就将此进程放入睡眠队列中。

singal通过释放一个资源,然后判断在此之前计算机的资源有多少个,是否小于等于0,小于的情况就是唤醒睡眠队列下的队首进程利用刚刚释放资源。大于则说明之前计算机的资源充足

实现互斥的例子

A,B 两个进程需要计算机的打印机资源打印数据,两者运行时需要的时间以及次数不确定,此时

1
2
3
4
5
6
7
8
9
10
11
semaphore mutex = 1; // 这是一个互斥信号量,一般设置为1,可以表示一个资源
P(A){
wait(mutex);
进程A使用打印机
singal(mutex);
}
P(B){
wait(mutex);
进程B使用打印机
singal(mutex);
}

两者比较完善地的实现了这两个进程之间的资源的互斥:

空闲让进?实现了

忙则等待?实现了

有限等待?实现了,只要每个进程不是长时间使用打印

让权等待?实现了,不存在CPU为一直运行的状态。

在互斥之中,信号量的初始值一般都赋值为1或者以上,表示一个资源。

实现同步的例子

进程A需要把中间数值放入单缓冲中,进程B需要取出进程A放入的中间数值。单缓冲一次只能一个进程操作,实现同步。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
semaphore empty = 1; //记录单缓冲中空位
semaphore full = 0; //记录单缓冲的数据 表示同步

P(A){
wait(empty);
放入数据;
singal(full);
}

P(B){
wait(full);
放入数据;
singal(empty);
}

在同步问题中 最为关键的便是确定前驱后继关系。信号初始值设为0表示此进程还没有完成,便于实现同步

经典类型问题

生产者消费者问题

哲学家思考问题

读写进程问题

理发店问题

完善机制-管程

管程定义:编译器设置

条件变量 直接调用阻塞