目录
4.3.4 条件临界区CCR(Conditional Critical Region)
4.1并发进程(concurrent processes)
4.1.1前驱图的定义
前趋图(precedence graph) 是有向无环图,图中每个结点表示一个语句、一个计算步骤、或一个进
程。结点间的有向边表示偏序或前趋(precedence relation)关系,记为“→”,→={(Pi,Pj)|Pj启动之
前Pi必须已经完成}。(Pi,Pj)∈→可记作Pi→Pj,称Pi是Pj的前趋,Pj是Pi的后继。
在前趋图中,没有前趋的结点称为初始结点,没有后继的结点称为终止结点 每个结点可以有一个
权重(weight),它可以表示该结点所包含的程序量或计算时间。
4.1.2顺序程序及其特性
1.内部顺序性:对于一个进程来说,它的所有指令是按序执行的
2.外部顺序性:对于多个进程来说,所有进程的活动是依次执行的
3.顺序程序的特性:
顺序性:指令逐条执行
封闭性:不受其它程序及外界因素影响
可再现性:结果与推进速度无关
顺序性=>封闭性=>可再现性
4.1.3并发程序及其特性
1.内部并发性:指一个程序内部的并发性
2.外部并发性: 指多个程序活动的重叠执行(流水线)
3.并发程序的特性:
间断性:多个程序交叉执行,通过中断实现程序切换
非封闭性:一个进程的运行环境可能被其它进程所改变,从而相互影响
不可再现性:重复运行同一程序不一定得到相同的结果
间断性=>非封闭性=>不可再现性
4.1.4程序并发执行的条件
1.目标:在失去封闭性的条件下,保持可再现性
2.R(pi)={a1,a2,……,am}表示程序pi在执行期间所需读取的所有变量的集合,称为“读集”
3.W(pi)={b1,b2,……,bn}表示程序pi在执行期间所需改变的所有变量的集合,称为“写集”
4.若两个程序p1,p2满足如下条件,则能够保持可再现性,因而可以并发执行。该条件是1966年
首先由Bernstein提出,故称为Bernstein条件
(简单来讲就是:p1要读的变量p2不会去写, p2要读的变量p1不会去写,并且 p1和p2不会同时
去写相同的变量。但允许p1和p2同时去读相同的变量)
4.1.5与时间有关的错误
1.错误原因1:进程交叉执行(interleave,交错)
2.错误原因2:涉及公共变量(资源)
4.2进程互斥(mutual exclusion)
进程互斥是进程之间所发生的一种间接性相互作用,这种相互作用是进程本身不希望的,也是运行进程感觉不到的。进程互斥既可发生在相关进程之间,也可发生在不相关的进程之间。
4.2.1共享变量与临界区
1.共享变量(shared variable),多个进程都需要访问的变量(扩展到共享资源)
2.临界区域(critical region/critical section),访问共享变量的程序段
3.临界资源(critical resource),一次只允许一个进程使用的资源
4.2.2临界区与进程互斥
1.进程互斥:多个进程不能同时进入关于同一组共享变量的临界区域,否则可能发生与时间有关的错误,这种现象称为进程互斥
2.互斥的定义包含二层涵义
(1)不允许多个进程同时进入关于同一组共享变量的相同的临界区域
(2)不允许多个进程同时进入关于同一组共享变量的不同的临界区域
4.2.3进程互斥原则
1.临界区互斥的框架(framework)
do
{
entry section
critical section(临界区)
exit section
remainder section(其余代码)
}while(true);
2.互斥性原则。
mutual exclusion(互斥性):任意时刻只允许一个进程进入关于同一组共享变量的临界区—排他
3.进展性原则。
progress(进展性):临界区空闲时,必须允许一个请求进入临界区的进程进入—效率
4.有限等待性原则。
bounded waiting(有限等待性):一个请求进入临界区的进程在有限的等待时间内,必须获得进入该
临界区的机会—公平
4.2.4进程互斥的软件实现
1.Dekkel算法(课本91)
shared int flag[2]={false,false},turn=0/*or 1*/;
do{ /*P1:*/
flag[0]=1;
while(flag[1]==1){
if(turn==1){
flag[0]=0;
while(turn==1){skip();};
flag[0]=1;}};
临界区代码;
turn=1;
flag[0]=0;
其余代码;
}while(true);
do{ /*P2:*/
flag[1]=1;
while(flag[0]==1){
if(turn==0){
flag[1]=0;
while(turn==0){skip();};
flag[1]=1;}};
临界区代码;
turn=0;
flag[1]=0;
其余代码;
}while(true);
2.Peterson算法(课本93)
shared bool flag[2]; //进入临界区的两种钥匙之一
shared int turn; //进入临界区的两种钥匙之二
//P1:
do{
flag[0]=true;turn=1;
while(flag[1]&&turn==1)
{};
临界区代码;
flag[0]=false;
其余代码;
}while(true);
//P2:
do{
flag[1]=true;turn=0;
while(flag[0]&&turn==0)
{};
临界区代码;
flag[1]=false;
其余代码;
}while(true);
3.Lamport面包店算法(课本94)
do{/*课本94*/
choosing[i]=1;
number[i]=max{number[0],number[1],...,number[n-1]}+1;
choosing[i]=0;
for(j=0;j<n;j++){
while(choosing[j])
skip;
while((number[j]<>0)&&(number[j],j)<(number[i],i))
skip:
};
临界区
number[i]=0;
其余代码
}while(1);
4.Eisenberg/Mcguire算法(课本95)
/*所采用的数据结构*/
enum stat{idle, want_in, in_cs};
stat flag[n]={idle};
int turn; //取0⁓n-1,初始时任意
flag[i]=idle; //进程Pi不想进入临界区
flag[i]=want_in; //进程Pi想进入临界区
flag[i]=in_cs; //进程Pi想进入或已进入临界区
/*算法*/
//pi进入
do{
flag[i]=want_in;
j=turn;
while(j!=i){
if(flag[j] != idle)j=turn;
else j=(j+1)%n;};
flag[i]=in_cs;
j=0;
while((j<n)&&(j==i or flag[j]!=in_cs)){j++;};
}while(j!=n);
turn=i;
//临界区
//pi离开
j=(turn+1)%n;
while(flag[j]==idle)
{j=(j+1)% n;};
turn=j;
flag[i]=idle;
4.2.5进程互斥的硬件支持
1.硬件提供存储障碍语句
2.硬件提供原子变量
3.硬件提供“测试并设置”指令
4.硬件提供“交换”指令
5.硬件提供“开关中断”指令
4.3进程同步(synchronization)
进程同步是进程之间直接的相互作用的形式,是合作进程之间有意识地行为,只发生在相关进程之间。
4.3.1进程同步的概念
1.同步(synchronization),并发进程彼此协调推进速度,在某些点处互相等待或者唤醒的相互直接制约关系
2.一组进程靠并发得以正常执行,这种现象称为进程合作。参与合作的进程称为合作进程。
4.3.2进程同步机制
1.用于实现进程同步的工具称为同步机制,又称同步设施(synchronization mechanism)
2.同步机制要求
描述能力够用
可实现
高效
使用方便
4.3.3信号量与PV操作
1.信号量semaphore的定义
typedef semaphore struct{
int value;
PCB Pointer queue;};//进程控制块
semaphore s;
2.P/V操作原语定义
//P操作原语的定义
void P(semaphore *s){
s.value=s.value-1;
if(s.value<0)
asleep(s.queue);}
//V操作原语的定义
void V(semaphore s){
s.value=s.value+1;
if(s.value<=0)
wakeup(s.queue);}
/*asleep(s->queue):执行此操作的进程的进程控制块进入队列s->queue的尾部,其状态由运行态转为等待态,系统转到处理器调度程序*/
/*wakeup(s->queue):将队列s->queue头部进程的进程控制块由该队列中取出,并将其排入就序列队,其状态由等待态转为就绪态*/
3.对于信号量的规定
必须置一次且只能置一次初值,初值一般>=0
只能执行P/V操作改变信号量,其它操作非法
4.几个有用的结论
当s.value>=0时,s.queue队列为空
当s.value<0时,|s.value|为队列s.queue的长度
当s.value初值=1时,一般用于进程互斥
当s.value初值=0时,一般用于进程同步
5.使用信号量解题的步骤
(1)认真读完题目中的每一个字
(2)找出问题中存在的所有竞争和合作
竞争=>互斥
合作=>同步
(3)找出问题中涉及的所有资源,一类资源一个信号量
(4)根据题意,确定每类资源初始的可用数量
6.生产者/消费者问题
(1)问题分析
(2)只考虑同步
(3)同时考虑同步和互斥
(4)代码实现
item product,x,buffer[k]; //环形缓冲区
semaphore S1,S2,M1,M2;
int in,out; //取值范围0,1,2,……,k-1
void producer(void){
while(true){
produce a product;
P(S1);
P(M1);
buffer[in]=product;
in=(in+1)%k;
V(M1);
V(S2);};}
void consumer(void){
while(true){
P(S2);
P(M2);
x=buffer[out];
out=(out+1)%k;
V(M2);
V(S1);
consume x;};}
void main(){
S1.value=k;
S2.value:=0;
M1=M2=1;
in=out=0;
cobegin //并发执行多个进程
P1: producer(); …… , Pm: producer();
C1: consumer(); ……, Cn: consumer();
coend;}
7. 读者/写者问题(详细见课本108)
代码实现
void reader(void){
P(mutex);
read_count=read_count+1;
if(read_count==1)P(r_w_w);
V(mutex);
<read operations>
P(mutex);
read_count:=read_count-1;
if(read_count==0)V(r_w_w);
V(mutex);}
semaphore r_w_w,mutex;
int read_count;
void writer(void){
P(r_w_w);
<write operations>
V(r_w_w);}
void main(){
read_count=0;
r_w_w.value=1;
mutex.value=1;
cobegin
R1: reader(); …… , Rm: reader();
W1: writer(); ……, Wn: writer();
coend;}
8.障栅问题
(1)障栅(barrier)问题,一组进程并发执行,碰到障栅时将进入等待,直到全组进程都已到达障栅,之后这组进程一起越过障栅继续推进
(2)代码实现
semaphore mutex,barrier;
mutex.value=1;
barrier.value=0;
int count=0;
//进程Pi并发执行
P(mutex);
count=count+1;
if(count==n){
for(i=1;i<=n;i++)
V(barrier);};
V(mutex);
V(barrier);
//进程Pi继续并发执行
4.3.4 条件临界区CCR(Conditional Critical Region)
1.信号量与PV操作属于低级机制,条件临界区是一种高级的同步机制。
2.CCR与PV操作的等价性
用CCR可以实现PV操作
用PV操作可以实现CCR
3.条件临界区的实现效率是比较低的
4.3.5 管程(Monitor)
信号量+PV操作 | 管程 | |
同步机制 | 对共享变量的操作及对信号量的PV操作分散在整个系统和各个进程中 | 将共享变量及对其所能执行的全部操作集中在一个模块中 |
优点 | 较为高效 较为灵活 | 可读性好 正确性易于保证 易于修改和维护 |
缺点 | 可读性差 正确性不易保证 不易修改和维护 | 较不灵活 效率较低 |
4.3.5.1 管程的提出
1.70年代初为克服分散式同步机制的缺陷,introduced by E.W.Dijkstra,C.A.R.Hoare,P.B.Hansen
2.基于管程的语言:Concurrent Pascal(Hansen) Modula(N.With) Mesa MOD* Concurrent Euclid XCY
3.20世纪70年代初流行一种构造操作系统的PCM方法
进程P:Process
类程C:Class
管程M:Monitor
4.PCM构造的OS是结构化的,便于理解、修改、维护
4.3.5.2 管程的形式
unction 函数名(形参表): 返回值类型;
函数局部变量说明;
begin 语句序列 end;
……
begin 共享变量初始化语句序列 end;
4.3.5.3 管程的语义
1.管程中的共享变量在管程外部不可见,外部只能通过调用管程中声明的外部过程/函数间接访问这些共享变量
2.为保证管程共享变量的数据完整性,规定管程互斥进入
3.管程中设有进程等待队列、相应的等待/唤醒机制,进程等待时释放管程的互斥权,唤醒进程时(比如P唤醒Q)
(1)P等待Q继续,直到Q退出或等待(Hoare)
(2)Q等待P继续,直到P退出或等待(Java)
(3)唤醒操作是管程中可执行的最后一个操作(Hansen)
4.3.5.4 管程的使用
1.生产者/消费者问题
type producer_consumer = MONITOR
var B: array[0..n-1]of integer;
count, in, out: integer;
pq, cq: condition;
define put_in, get_out;
procedure put_in(item: integer);
begin
if(count=n)then wait(pq);
B[in]:=item;
in:=(in+1)%n;
count++;
signal(cq);
end;
procedure get_out(var item: integer);
begin
if(count=0)then
wait(cq);
item:=B[out];
out:=(out+1)%n;
count--;
signal(pq);
end;
begin
in:=0; out:=0; count:=0;
end;
2.读者/写者问题(写者优先)(部分代码)
//写者优先
type readers_writers = Monitor
var rq,wq: condition;
reading_count,write_count: integer;
define start_read,finish_read,
start_write,finish_write;
procedure start_read;
begin
if(write_count>0)then
wait(rq);
reading_count++;
signal(rq);
end;
procedure finish_read;
begin
reading_count--;
if(reading_count=0)then
signal(wq);
end;
procedure start_write;
begin
write_count++;
if(write_count>1)or
(reading_count>0)
then wait(wq);
end;
procedure finish_write;
begin
write_count--;
if(write_count>0)
then signal(wq);
else signal(rq);
end;
begin
reading_count:=0;
write_count:=0;
end;
var rw:readers_writers;
读者:
rw.start_read;
{reading};
rw.finish_read;
写者:
rw.start_write;
{writing};
rw.finish_write;
4.3.5.5 管程与PV操作的等价性
用PV操作可以构造管程
用管程可以构造PV操作
1.用管程构造PV操作
管程 | 信号量与PV操作 |
var s:semaphore; | var s:semaphore; |
init s(init_value); | s.value:=ini_value; |
s.P; | P(s); |
s.V; | V(s); |
2.用PV操作构造管程
管程 | 信号量与PV操作 |
一个管程实例 | 一个入口等待队列var mutex:semaphore;初值1 一个紧急等待队列var urgent:semaphore;初值0 一个紧急等待计数变量var urgent_count:integer;初值0 |
一个条件变量 var c:condition; | 一个信号变量var s:semaphore;初值0 一个计数变量var count:integer;初值0 |
等待操作 wait(c); | count:=count+1; if(urgent_count>0)then begin urgent_count:=urgent_count-1; V(urgent); end; else V(mutex); P(s); |
唤醒操作 signal(s); | if(count>0)then begin count:=count-1; urgent_count:=urgent_count+1; V(s); P(urgent); end; |
进入管程 | P(mutex); |
离开管程 | if(urgent_count>0)then begin urgent_count:=urgent_count-1; V(urgent); end; else V(mutex); |
4.3.5.6 管程的嵌套调用问题
问题:
1.是否允许嵌套
2.若允许,如何处理互斥权
方法:
1.禁止嵌套(Modula)
2.允许嵌套,等待时释放当前管程互斥权(并发Pascal)
3.允许嵌套,等待时释放所有管程互斥权
4.允许嵌套,调用时释放路经管程互斥权
4.3.6 会合(rendezvous)
1.会合指两个并发执行流汇集到一处
并发执行流: 调用和接受
均发生握手和同步
2.信号量和PV操作只适合具有统一内存的系统,不适合分布式环境
3.管程只适合具有统一内存的系统,不适合分布式环境
4.会合的图示
任务相当于进程
任务可以处于不同的主机中
被调用者代替调用者执行调用代码
Ada中会合的核心包括调用语句、接受语句accept和选择语句select
4.4进程高级通讯
4.4.1 进程高级通讯概念
1.进程通讯IPC(interprocess communication):进程之间的相互作用,包括互斥、同步及信息交换。
4.4.2 进程通讯模式
1.享内存模式(shared memory) :需要OS提供
公共内存
互斥同步机制
2.消息传递模式(message passing)
直接:进程=>进程
间接:进程=>信箱=>进程
4.4.3 直接方式
(1)对称形式(sender and receiver name each other)
(2)非对称形式(only sender names receiver)
1.有缓冲途径(消息传递模式、直接方式、非对称形式)
2.无缓冲途径(消息传递模式、直接方式、非对称形式)
4.4.4 间接方式(借助媒介)
1.相互通信的进程通过信箱来实现通信。
2.例子:
multi-sender<=>multi-receiver
multi-sender<=>one receiver