王嘉贝

导航

操作系统学习笔记 2.1 - 进程同步

进程的同步 学习笔记

参考资料:


1. 相关概念

1.1 什么是进程的同步?

首先复习以下进程的5个特征:
1. 动态性
2. 并发性 
3. 独立性 
4. 异步性 
5. 结构性  

由于本来进程的特性,当出发并发的时候每个进程之间的进度。没有直接相关性。
会导致结果的不可复现,等问题。 为了协调进程之间的相互制约关系,引入了进程同步的概念

1.2 临界资源、临界区
一次只允许一个进程所用的资源叫做临界资源。 例如打印机一次只能正在执行一个打印任务;
其他的打印作业都处于 阻塞态 等待上个任务释放打印机临界资源

临界区: 访问临界资源的代码段叫做临界区

可以将临界资源的访问过程分成4个部分 
1. 进入区:检查资源,设置正在访问的标志 
2. 临界区:访问和使用临界资源 
3. 退出区:将正在访问的标识消除掉 
4. 剩余区:除此以外的剩下部分

同步: 让本来异步的两个或者多个进程,按照特定次序的直接制约关系:

例如 进程A通过缓冲区对进程B提供数据,当缓冲区为空的时候,B会发生阻塞;
一旦A对缓冲区写入了数据,则唤醒进程B,当缓冲区满了以后,A阻塞,一旦B完成读取
则唤醒A进程;

互斥: 两个没有先后的直接制约关系的进程,因为竞争临界资源发生的等待

例如 进程A B 都要使用打印机,A b直接并没有直接制约关系。
某一个作业先获得打印机资源,另外一个资源就需要等待指导前一个进程释放临界资源

为了禁止两个进程同事进入临界区,同步机制应该遵循如下的原理:

  1. 空闲让进 可以允许一个进程进程立刻进入临界区
  2. 忙则等待 已经进入临界区的进程 应该让其他试图进入的临界区的进行等待
  3. 有限等待:确定再有限时间内可以进入临界区,不会出现饥饿状况
  4. 让权等待: 如果阻塞,释放处理器

软件实现方法:

1. 单标志法
2. 双标志法先检查
3. 双标志法后检查 
4. peterson算法 结合了上述方法

硬件实现方法:

1. 中断屏蔽方法
2. 原语 TestAndSet 以及 Swap 

信号量与进程同步

整型信号量 S

wait(S)/P(S){
while(S<=0); // handling for S changed by other 
S=S-1
}

signal(S)/V(S){
S=S+1; // release the resouce signal;
}
以上的方式虽然实现了,不断的测试等待同步,但是没有实现让权等待的功能
因为当P过程失败的时候,一直处于while循环,忙等,并没有释放CPU

记录型信号量

typedef struct{
int value;
struct process *L;
}semaphore;

wait(semaphore){
S.value--;
if(S.value<0){
add this process to S.L;
block(S.L); //使用原语block阻塞进程,释放cpu
}
}
 
signal/V(semophore S){
  S.value++;
  if(S.value<=0){ // 仍然有进程被阻塞
  remove a process P from S.L
  wakeup(P);
  }
}
利用信号量来实行同步 (前驱关系)

例题:

同时有4个进程再进行 P1, P2 ,P3,P4
P2,P3都必须要P1完成后才可以进行
P4需要P2 P3完成后才开始运行  
如何再并发中保证他们的前驱关系?

解决方案:

设置好初始的信号量 
A1,A2; P1给P2 P3的信号量
B1; P2给P4的信号量, C1,P3给P2的信号量  
Process P Do V
P1() - do something ; V(A1);V(A2);
P2() P(A1) do something V(B1);}
P3() P(A2); do something ; V(C1);
P4() P(B1); P(C1); do something ; -
利用信号量来实行互斥

例题:

同时有2个进程再进行 P1, P2 
使用临界资源 A
如何再并发中保证互斥?

resurce A=1;
P1(){
	P(A);
	// use A
	V(A);
};

P2(){
	P(A);
	// use A
	V(A);
}

管程Monitor与进程同步

相比于信号量实现的进程同步和互斥:

1. 程序员需要自己实现进程互斥
2. 大量定义的分散的同步变量造成系统管理混乱
3. 操作不当产生死锁(下一篇学习)
便产生了一种新的进程同步工具 - 管程;  
优点:
1. 保证了进程互斥,无需程序员自己实现
2. 提供了条件变量Condition_Variable 可以让程序员灵活的实现进程同步

利用共享数据结构抽象的表示系统中的共享资源;并且由对此数据结构实时操作
的一组对应过程组成的资源管理程序,称之为管程;

组成:

  1. 管程名字
  2. 共享结构数据说明
  3. 对其操作的过程函数
  4. 对于内部设置初始值语句
    其表现结构非常像 面向对象中的一个类

monitor demo{
shared_resource S;
condition x;
init_code(){S=5; //初始资源数}
take_away(){S--;
if(S<=0)x.wait;
operations;}
give_back(){S++;operations;if(进程在等待)x.signal;}
}

名称 相同点 不同点
信号量 PV操作 有值,显示剩余资源
条件变量 wait/signal 进程阻塞唤醒,配合管程管理剩余资源

案例:哲学家进餐问题

题目:

一个圆桌有5个哲学家,每两个哲学家之间有一根筷子 
哲学家只有两种状态:吃饭 思考
只有当哲学家饥饿时候才试图拿起左右的两根筷子
如果筷子已经再别人手上,等待别人。
只有拿到两根筷子才进行进食, 放下筷子继续思考 

目标:

  1. 防止饥饿
  2. 防止死锁

最开始,按照题意构建的进程如下:

Piloshopher(int i)
	do{
		P(chopstick[i]);
		P(chopstick[(i+1)%5]);
		eat;
		Vchopstick[i]);
		Vchopstick[(i+1)%5]);
		think;
	}while(1);
}

但是上面的过程有一个问题,当哲学家同时饿了,同时开始拿起左边的筷子,会导致所有的哲学家
都只有一根筷子;因此对于他们取筷子应该也做出限制 , 最多一个一个来去筷子;

mutex=1
Piloshopher(int i)
	do{
		P(mutex);
		P(chopstick[i]);
		P(chopstick[(i+1)%5]);
		V(mutex);
		eat;
		Vchopstick[i]);
		Vchopstick[(i+1)%5]);
		think;
	}while(1);
}

posted on 2021-06-17 13:27  toBeGeek  阅读(90)  评论(0编辑  收藏  举报