1. 互斥:某些临界资源(如公共变量或文件)同时只能被一个进程访问,则需要采用互斥机制来避免两个或多个进程同时访问该资源。可通过信号量、互斥锁等方法实现。
2. 同步:进程之间具有所谓的“直接制约关系”,即一个进程在完成某项操作前,必须等待另一个进程执行相应的操作后才能开始自己的下一步操作,这时候需要使用同步对象进行同步。
3. 死锁避免:如果每个进程都获得了一个资源并且希望获得另一个进程拥有的资源,则可能出现死锁情况。所以需要规定顺序分配资源。
4. 进展:当所有请求进程中至少有一个可以运行时,不能进一步延迟该请求的满足,避免饥饿现象的发生。
1. 预防死锁:采用预防死锁的措施,尽量避免系统运行陷入死锁状态。例如,破坏进程请求资源的必要条件之一:消除互斥、占有且等待、不可剥夺、环路等条件中的任意一个。
2. 避免死锁:在运行时动态地检测系统状态,确保不会出现死锁状态。使用避免死锁算法进行检查,如银行家算法、安全顺序算法等。
3. 检测死锁:当避免死锁不可行时,可以通过定期检测系统状态来发现是否存在死锁,并采取相应的措施解除死锁。
4. 解除死锁:当检测到系统处于死锁状态时,需要采取恰当的措施对死锁进行解除。常用的方法包括终止某些进程、抢占资源、撤销进程申请的部分或全部资源,可以结合操作系统提供的算法对资源进行选择释放等。
Work |
Need |
Allocation |
Work+Allocation |
Finish |
|||||||||
A |
B |
C |
A |
B |
C |
A |
B |
C |
A |
B |
C |
||
P1 |
3 |
3 |
2 |
1 |
2 |
2 |
2 |
0 |
0 |
5 |
3 |
2 |
True |
P3 |
5 |
3 |
2 |
0 |
1 |
1 |
2 |
1 |
1 |
7 |
4 |
3 |
True |
P4 |
7 |
4 |
3 |
4 |
3 |
1 |
0 |
0 |
2 |
7 |
4 |
5 |
True |
P2 |
7 |
4 |
5 |
6 |
0 |
0 |
3 |
0 |
2 |
10 |
4 |
7 |
True |
P0 |
10 |
4 |
7 |
7 |
4 |
3 |
0 |
1 |
0 |
10 |
5 |
7 |
True |
可知,在T0时刻存在着一个安全序列{P1,P3,P4,P2,P0},故系统是安全的。
Request(1,0,2)<=Need(1,2,2)
Request(1,0,2)<=Available(3,3,2)
于是系统试探分配,修改相应的向量,形成的资源变化清况如下表所示
Max |
Allocation |
Need |
Available |
|||||||||
A |
B |
C |
A |
B |
C |
A |
B |
C |
A |
B |
C |
|
P0 |
7 |
5 |
3 |
0 |
1 |
0 |
7 |
4 |
3 |
2 |
3 |
0 |
P1 |
3 |
2 |
2 |
3 |
0 |
2 |
0 |
2 |
0 |
— |
— |
— |
P2 |
9 |
0 |
2 |
3 |
0 |
2 |
6 |
0 |
0 |
— |
— |
— |
P3 |
2 |
2 |
2 |
2 |
1 |
1 |
0 |
1 |
1 |
— |
— |
— |
P4 |
4 |
3 |
3 |
0 |
0 |
2 |
4 |
3 |
1 |
— |
— |
— |
再利用安全性算法检查此时系统是否安全,如下表所示
Work |
Need |
Allocation |
Work+Allocation |
Finish |
|||||||||
A |
B |
C |
A |
B |
C |
A |
B |
C |
A |
B |
C |
||
P1 |
2 |
3 |
0 |
0 |
2 |
0 |
3 |
0 |
2 |
5 |
3 |
2 |
True |
P3 |
5 |
3 |
2 |
0 |
1 |
1 |
2 |
1 |
1 |
7 |
4 |
3 |
True |
P4 |
7 |
4 |
3 |
4 |
3 |
1 |
0 |
0 |
2 |
7 |
4 |
5 |
True |
P0 |
7 |
4 |
5 |
7 |
4 |
3 |
0 |
1 |
0 |
7 |
5 |
5 |
True |
P2 |
7 |
5 |
5 |
6 |
0 |
0 |
3 |
0 |
2 |
10 |
5 |
7 |
True |
由于安全性算法检查可知,可以找到一个安全序列{P1,P3,P4,P0,P2}。因此,系统是安全的,可以立即把P1所申请的资源分配给它。
Request(3,3,0)<=Need(4,3,1)
Request(3,3,0)>Available(2,3,0),所以让P4等待。
Request(0,2,0) Request(0,2,0)<=Available(2,3,0) 于是系统试探分配,修改相应的向量,形成的资源变化清况如下表所示 Allocation Need Available A B C A B C A B C P0 0 3 0 7 2 3 2 1 0 P1 3 0 2 0 2 0 — — — P2 3 0 2 6 0 0 — — — P3 2 1 1 0 1 1 — — — P4 0 0 2 4 3 1 — — — 对其进行安全性检查,可用资源Available(2,1,0)已不能满足任何进程的需要,故系统进入不安全状态,此时系统不分配资源。 分析题意可知,小桥可能有3种状态:桥上可能没有人,也可能有一人,也可能有两人。 这里用了`wait`和`signal`两个信号量操作。当读者进入阅览室时,首先会等待`emptySeat`信号量,表示等待有空闲座位。一旦获取到了空闲座位,就会等待`registry`信号量,表示获取了登记表。在获取到登记表后,会在登记表上登记该读者的信息,并在此后释放`registry`信号量。读者离开阅览室时,则需要重新获取`registry`信号量,并在登记表上注销该座位的记录。注销后,先释放`registry`信号量,再通过`signal`操作将空闲座位数量加一。 可以使用两个信号量来实现这个同步过程,分别是“售票员”和“司机”,初始值都为0。 这里用了`wait`和`signal`两个信号量操作。当售票员完成了售票和关门操作后,会通过`signal`操作唤醒等待在`driver`信号量上的司机,表示已经准备好让车行驶了。同时,售票员也会在`conductor`信号量上等待司机完成了停车操作后再开门。 当司机完成了停车操作后,会通过`signal`操作唤醒等待在`conductor`信号量上的售票员开门。而售票员也会在完成了开门操作后再次等待司机关门完成。
四.完成以下各题伪代码。
semaphore empty= 30; //表示缸中目前还能装多少桶水,初始时能装30桶水
semaphore full=0; //表示缸中有多少桶水,初始时缸中没有水
semaphore buckets =5; //表示有多少只空桶可用,初始时有5只桶可用
semaphore mutex_well=1; //用于实现对井的互斥操作
semaphore mutex bigjar =1; //用于实现对缸的互斥操作
young_monk(){
while(1){
P(empty);
P(buckets);
go to the well;P(mutex_well);get water;
V(mutex_well);go to the temple;P(mutex _bigjar);}
pure the water into the big jar; }
V(mutex_bigjar);
V(buckets);
V(fu11);
old_monk(){
while(){
P(ful1);
P(buckets);
P(mutex_bigjar);
get water;
V(mutex_bigjar);
drink wateri
V(buckets);
V(empty);
}
}
一座小桥(最多只能承重两个人)横跨南北两岸,任意时刻同一方向只允许一人过桥,南侧桥段和北侧桥段较窄只能通过一人,桥中央一处宽敞,允许两个人通过或歇息。试用信号量和P、V操作写出南、北两岸过桥的同步算法。
当相向两人过桥时也可能有3种状态:
(a)两人同时上桥;(b)两人都到中间;(c)南(北)来者到北(南)段。
共需要3个信号量,load用来控制桥上人数,初值为2,表示桥上最多有两人;north用来控制北段桥的使用,初值为1,用于对北段桥互斥;south用来控制南段桥的使用,初值为1,用于对南段桥互斥。
算法如下:semaphore load =2;
semaphore north=1;
semaphore south=1;
tosouth(){ tonorth(){
P(load); P(load);
P(south); P(north);
过北段桥; 过南段桥;
到桥中间; 到桥中间
V(north); V(south);
P(south); P(north);
过南段桥 过北段桥;
到达南岸 到达北岸
V(south); V(north);
V(load); V(load);
假设图书馆阅览室共有100个座位。读者进入时必须先在登记表上登记,该表为每一座位登记一个记录,包括座号和读者姓名。读者离开时要注销掉登记内容。请用信号量及操作的伪代码实现读者进入阅览室的同步过程。
可以使用两个信号量来实现这个同步过程,分别是“空闲座位”和“登记表”,初始值分别为100和1。
semaphore emptySeat = 100; // 空闲座位数量
semaphore registry = 1; // 登记表可用
void reader_enter(string name, int seat) {
wait(emptySeat); // 等待空闲座位
wait(registry); // 获取登记表
// 在登记表上登记
registerSeat(name, seat);
signal(registry); // 释放登记表
}
void reader_leave(int seat) {
wait(registry); // 获取登记表
// 在登记表上注销
unregisterSeat(seat);
signal(registry); // 释放登记表
signal(emptySeat); // 空闲座位数量加一
}
在公共汽车上,司机和售票员各行其职,司机负责开车和到站停车;售票员负责售票和开、关车门。当司机停车后售票员才能开门;当售票员关好车门后司机才能开车行驶。请用信号量及操作的伪代码实现司机和售票员的同步过程。
semaphore driver = 0; // 初始值为0
semaphore conductor = 0; // 初始值为0
// 司机的行为
void driver_behavior() {
while (true) {
// 开车行驶
drive();
// 等待售票员关好车门
wait(conductor);
}
}
// 售票员的行为
void conductor_behavior() {
while (true) {
// 售票
sellTicket();
// 关闭车门
closeDoor();
signal(driver); // 唤醒司机开车
wait(conductor); // 等待司机停车
openDoor(); // 开门放客
}
}
// 司机停车的操作
void driver_stop() {
stop();
signal(conductor); // 唤醒售票员开门
wait(driver); // 等待售票员关门
}
在4×100米接力赛中,4个运动员之间存在如下关系,运动员1跑到终点把接力棒交给运动员2;运动员2一开始处于等待状态,在接到运动员1传来的接力棒后才能往前跑,他跑完100米后交给运动员3,运动员3也只有在接到运动员2传来的棒后才能跑,他跑完100米后交给运动员4,运动员4接到棒后跑完全程。请用信号量及操作的伪代码实现运动员接力的同步过程。
可以使用三个信号量来实现这个同步过程,分别是“棒1”,“棒2”和“棒3”,初始值都为0。
semaphore baton1 = 0; // 初始值为0
semaphore baton2 = 0; // 初始值为0
semaphore baton3 = 0; // 初始值为0
void runner1_behavior() {
// 跑到终点把接力棒交给运动员2
run();
signal(baton1); // 传递接力棒,唤醒运动员2
}
void runner2_behavior() {
wait(baton1); // 等待接力棒,运动员1传来棒后才能开始跑
// 跑完100米后交给运动员3
run();
signal(baton2); // 传递接力棒,唤醒运动员3
}
void runner3_behavior() {
wait(baton2); // 等待接力棒,运动员2传来棒后才能开始跑
// 跑完100米后交给运动员4
run();
signal(baton3); // 传递接力棒,唤醒运动员4
}
void runner4_behavior() {
wait(baton3); // 等待接力棒,运动员3传来棒后才能开始跑
// 跑完100米,比赛结束
run();
}