(PS:个人课下整理的操作系统笔记,OneNote直接拷贝过来的,图片看不了就将就一下,配合张伟老师的PPT看效果更好)
本章讨论一种机制,该机制能确保共享同一逻辑地址空间的协作进程能有序执行并维护共享数据的一致性
协作进程(cooperating processes):可以与在系统内执行的其他进程相互影响的进程,相互协作的进程可以共享逻辑地址空间,也可以通过文件访问访问数据。
一.生产者-消费者问题(bounded-buffer problem):
//定义全局变量
Item buffer[buffer_Size];
Int counter=0;
Int in=0;
Int out=0;
//生产者:
Item nextProduced;
while(1){
While(counter==bufferSize)
;
Buffer[in]=nextProduced;
In=(in+1)%buffer_Size;
Counter++;
}
//消费者:
Item nextConsumed;
While(1){
While(counter==0)
;
nextConsumed=buffer[out];
Out=(out+1)%buffer_Size;
Counter--;
}
二.临界区问题(the critical-section problem):
解决临界区问题必须满足以下三个条件:
互斥(mutual exclusion):
一个进程在临界区,其余进程不能进入该临界区
前进(progress):
p1想进,其余进程不想进,则p1可以进;
p1和p2想进,从临界区刚出来的p2要把机会让给p1;
p1刚出来又想进,其余进程不想进,则p1可以进;
有限等待(bounded waiting):
不能让一个进程无限等待
解决算法:
①皮特森算法(Peterson’s Solution):
记法:
外层一个do while
i想进, flag[i]= true,j的turn
While(flag[j] && turn==j );
否则i进临界区critical section
,出来后i不想进,flag[i] = false
具体算法步骤:
Do{
Flag[i]=true;
Turn=j;
While(flag[j]&&turn==j)
;
//critical section;
Flag[i]=false;
//reminder section;
}while(1)
②Test and Set:
boolean TestAndSet (boolean*target){
boolean rv = *target;
*target = TRUE;
return rv:
}
lock原来是啥就返回啥,然后lock置true
记法:
前提lock=false
While true,while(test and set(lock));
临界区出来后lock false
具体算法步骤:
Lock=false;
while(true){
While(test and set(&lock))
;
//critical section;
Lock=false;
//reminder section;
}
③Swap算法:
void Swap (boolean *a, boolean *b){
boolean temp = *a;
*a = *b;
*b = temp:
}
就是一个交换方法
记法:
前提lock=false
While true ,key =true
While key=true ,swap(lock 和 key);
临界区出来后lock false
具体算法步骤:
Lock=false;
While(true){
Key=true;
While(key==true)
Swap(&lock,&key);
//critical section;
Lock=false;
//reminder section;
}
三、信号量(semaphore)
1.信号量是一个整数变量,只允许有wait()与signal()两个原子操作,原来wait()称为P,signal()称为V,即PV测试
2.wait()与signal()算法:
3.利用信号量解决n个进程的同步问题
4.信号量的主要缺点是忙等待,浪费CPU时钟,试图进入临界区的进程必须在其代码中连续的循环。
5.死锁(deadlock)和饥饿(starvation)
6.信号量的类型:计数型(可作为互斥锁)和二进制型
四、经典的同步问题
1.有限缓冲区问题(Bounded-Buffer Problem)——生产者消费者问题
While(true){
//produce an item
Wait(Empty);
Wait(mutex);
//add the item to the buffer
Signal(mutex);
Signal(full);
}
While(true){
Wait(full);
Wait(mutex);
//remove an item from the buffer
Signal(mutex);
Signal(empty);
//consume the item removed
}
2.读写者问题(Readers and Writers Problem)
1、若没有写者已经获得允许使用共享数据库,则读者不需要等待;
2、若已有一个写者进程获得使用共享数据库,则读者需要保持等待;
3、若一个读者获得允许使用共享数据库,且有一个写者进程在等待,则其他读者进程不需要等待。
一旦写者就绪,写者会尽可能快获得共享数据库。
While(true){
Wait(wrt);
//开始更新数据
signal(wrt);
//reminder section
}
While(true){
Wait(mutex);//后续读进程在这里等
Readcount++;
If(readcount==1)
Wait(wrt);//第一个读进程在这里等
Signal(mutex);
Wait(mutex);
Readcount--;
If(readcount==0)
Signal(wrt);//最后一个读进程改这里的wrt,还给写者
Signal(mutex);
}
五、管程(Monitor)(了解)
为了更易于编写正确的程序,提出高级同步原语——管程
x.signal()重新启动一个悬挂的进程。
- x.wait()意味着调用操作的进程会暂停直到另一个进行调用,即x会暂停
- 管程机制确保一次只能有一个进程在管程内活动,因此程序员不需要显式编写同步机制,不过还需要一些额外的同步机制
- 对条件变量仅有的操作是wait和signal,即声明出来的condiction x;变量x只能有x.wait()和x.signal();
- 3.哲学家聚餐问题(Dining-Philosophers Problem)(了解)
- 记法:
- 初始化mutex=1;wrt=1;readcount=0
- 写者while true,等写,//更新,升写
- 读者while true
- 两对wait,signal(mutex)
- 第一对里,readcount++;if count==1,等写
- 第二对里,readcount--,if count==0,升写
- 读者(reader):
- 初始化:
- Semaphore mutex = 1
- Semaphore wrt = 1//读写者共享的信号量
- Int readcount = 0;//记录有多少个读者进程进入到数据库
- 写者(Writer):
- 第二读写者问题——写者优先
- 第一读写者问题——读者优先
- 记法:
- 初始化,mutex=1,full=0,empty=N
- While(true),两个wait与两个signal
- 生产者等待空,升满
- 消费者等待满,升空
- 中间的wait与signal是mutex
- Consumer:
- 初始化
- N buffers//共有N个格子
- Semaphore Mutex=1
- Semaphore Full=0//满项格子的个数
- Semaphore Empty=N//空项格子的个数
- Producer:
- 死锁:两个或多个进程都在等待一个事件,而该事件只能由其中一个等待进程来发生
- 一组进程处于死锁状态指:组内的每个进程都在等待一个事件,而该事件只能由同组内另一个进程触发
- 饥饿/无穷阻塞:同组内的某一个进程可能会无穷等待信号量,如果对信号量的链表采用LIFO顺序,则可能会发生无穷阻塞
- 为了克服忙等的缺点,修改wait和signal的定义
- 进入临界区:mutex==1,大于等于1.进入后,mutex==0
- 互斥,其他进程调用wait(mutex)则陷入while循环,直到前一个进程调用signal将mutex设为1.
- 并发的信号量也可以解决两个并发进程的执行顺序问题(第一个P1,signal,第二个wait,P2),semaphore初始化为0