第六章 进程同步
基础知识
竞争条件
多个进程并发访问和操作同一数据且执行结果与访问发生的特定顺序有关,称为竞争条件。
原子操作(Atomic Operation):
指一个操作中的所有动作要么全做,要么全不做;
该操作是一个不可分割的单位;
在执行过程中不允许被中断;
这些原子操作在管态(核心态)下运行;
常驻内存;
原语(Primitive):
是完成一定功能的一个过程
与一般程序段的不同是,它是原子操作。
原语是一段程序,与一般程序的不同点是,这段程序在执行期间不允许被中断;
这段程序要么全执行,要么全不执行
临界区问题
在程序中访问临界资源的那段代码称为临界区;
将对临界资源的互斥访问转化为对临界区的互斥访问。
临界区问题的解答必须满足如下三项要求:
互斥、前进、有限等待。
do {
进入区entry section
临界区critical section
退出区exit section
剩余区reminder section
} while(true)
临界资源
把在一段时间内只允许一个进程访问的资源称为临界资源。
临界资源要求互斥地共享,或互斥地访问。
硬件同步
do {
请求锁
临界区critical section
释放锁
剩余区reminder section
} while(true)
信号量
是一个受保护的量,对其只能进行初始化(即赋初值)以及wait及signal操作
<注>:定义变量之后,只能用wait、signal进行操作,不能用S++,if(s==5),s/3等写法。
wait (S) { //申请一个资源
while S <= 0; // no-op
S--;
}
signal (S) { //释放一个资源
S++;
}
当信号量S.value大于0的时候,代表可用资源数目,小于零的时候代表等待队列长度。
经典同步问题(超级重要)
生产者消费者问题
有界缓冲问题
读者写者问题
哲学家吃饭问题
理发师/看病问题
吸烟者问题
管程
课后作业
6.3
当一个进程位于其临界区内,任何试图进入其临界区的进程都必须在进入代码连续循环。忙等待可以被避免,但是承担这种开销与让一个进程处于沉睡状态,当相应程序的状态达到的时候进程又被唤醒有关。
6.4
不适合单处理器原因:自旋锁不适合在单处理器系统是因为从自旋锁中打破一个进程的条件只有在执行一个不同的进程时才能获得。
适合多处理器原因:一个线程在一个处理器上等待,而另一个线程可以在另一个处理器上访问临界区;当一个进程等待另一个进程产生的事件,另一个进程可以在其它处理机上执行从而产生该事件。
6.5
如果一个用户级程序具有停止中断的能力,那么它能够停止计时器中断,防止上下文切换的发生,从而允许它使用处理器而不让其他进程执行。
6.6
在多处理器系统中,进程在某一个处理器停止中断不能保证互斥进入程序状态。
6.8
int empty = N;
int full = 0;
int mutex = 1;
连接:
while(1) {
wait(empty);
wait(mutex);
connect();
signal(mutex);
signal(full);
}
释放:
While(1) {
wait(full);
wait(mutex);
release();
signal(mutex);
signal(empty);
}
6.9
当执行wait时,执行到s--的时候,发生中断,然后执行signal的s++,发生中断,接着执行wait,此时s不小于0,不能执行if语句,这样会发生混乱。反之亦然。所以wait和signal为原子操作,都不能被打断。
6.11
信号量:
int mutex=1;
int customer=0;
int barberReady=0;
int waiting=0
CHAIR = N;
理发师:
while(1) {
waiting(customer);
waiting(mutex);
waiting= waiting--;
signal(mutex);
signal(barberReady);
cuthair();
}
顾客:
while(1) {
wait(mutex);
if(waiting<CHAIR){
waiting++;
signal(mutex);
singal(customers);
wait(barberReady);
}else{
signal(mutex);
leaving();
}
6.13
Monitor bounded_buffer {
intitems[MAX_ITEMS];
intnumItems = 0;
conditionfull, empty;
voidproduce(int v) {
while(numItems==MAX_ITEMS)
full.wait();
iems[numItems++]=v;
empty.signal();
}
intconsume() {
intretVal;
while(numItems==0)
empty.wait();
retVal=items[--numItems];
full.signal();
returnretVal;
}
}
6.22
Monitor alarm {
conditionc;
voiddelay(int ticks) {
intbegin_time = read_clock();
while(read_clock()<begin_time+ ticks)
c.wait();
}
void tick() {
c.broadcast();
}
}
思考题:
Concepts:
race condition:多个进程并发访问和操作同一数据,且执行结果与访问发生的特定顺序有个条件关,称为竞争条件。
critical reource:把在一段时间内只允许一个进程访问的资源称为临界资源。
critical section:在程序中访问临界资源的那段代码称为临界区。
atomic operation:原子操作是指不能被中断的操作。
Semaphoer:信号量是一个受保护的量,对其只能进行初始化(即赋初值)和wait及signal操作。
wait() and signal() operation:测试,增加。
Monitor:管程是一种程序结构,结构内的多个子程序(对象或模块)形成的多个工作线程互斥访问共享资源。这些共享资源一般是硬件设备或一群变数。
如何利用硬件TestAndSet Instruction以及swap Instruction实现临界区的互斥?
booleanTestAndSet(boolean *target) {
boolean rv =*target;
*target =TRUE;
return rv;
}
void Swap(boolean*a, boolean *b) {
boolean temp =*a;
*a = *b;
*b = temp;
}
给出教材中讨论的三个经典问题、以及The Sleeping-Barber Problem及Cigarette Smoker’sProblem的问题描述,说明进程之间的制约关系,利用信号量及wait、signal操作给出能正确执行的程序
a) 生产者-消费者问题
semaphoremutex = 1, empty = n, full = 0;
i. 生产者
do {
wait(empty);
wait(mutex);
//放数
signal(mutex);
signal(full);
}while(TRUE);
ii. 消费者
do {
wait(full);
wait(mutex);
//取数
signal(mutex);
signal(empty);
}while(TRUE);
b) 读者-写者问题
i. 写者
semaphore mutex =1, wrt = 1;
int readcount =0;
do {
wait(wrt);
//写
signal(wrt);
}while(TRUE);
ii. 读者
do {
wait(mutex);
readcount++;
if(readcount== 1)
wait(wrt);
signal(mutex);
//读
wait(mutex);
readcount--;
if(readcount== 0)
signal(wrt);
signal(mutex);
}while(TRUE);
c) 哲学家进餐问题
i. 哲学家i
do {
wait(chopstick[i]);
wait(chopstick[(i+1)%5]);
//eat
signal(chopstick[i]);
signal(chopstick[(i+1)%5]);
//think
}while(TRUE);
d) 睡眠理发师问题
int chairs =n, waiting = 0;
semaphorecustomer = 0, barber = 0, mutex = 1;
i. 理发师
while(1) {
wait(customer);
signal(barber);
wait(mutex);
waiting--;
signal(mutex);
//理发
}
ii. 顾客
while(1) {
wait(mutex);
waiting++;
if(waiting<n)
signal(mutex);
signal(customer);
wait(barber);
//理发
}
e) 吸烟者问题
i. 供应者
int which =1+rand()%3;
wait(done_smoking);
//生产材料
switch(which){
case 1:
signal(paper_glue);
break;
case 2:
signal(tobacco_glue);
break;
case 3:
signal(tobacco_paper);
break;
}
ii. 吸烟者
switch(number){
case 1:
wait(paper_glue);
//吸烟
break;
case 1:
wait(tobacco_glue);
//吸烟
break;
case 1:
wait(tobacco_paper);
//吸烟
break;
}
signal(done_smoking);