- 进程间的交互关系
- 互斥,指多个进程不能同时使用同一个资源
- 同步,进程之间的协作
- 死锁,指多个进程互不相让,都得不到足够的资源
- 饥饿,指一个进程一直得不到资源(其他进程可能轮流占用资源)
- 临界区的访问过程
- 对于临界资源,多个进程必须互斥地对它进行访问
- 临界区
-
- 进程中访问临界资源的一段代码
- 临界区的执行在时间上是互斥的,进程必须请求允许进入临界区
-
- 进入区
-
- 在进入临界区之前,检查可否进入临界区的一段代码。如果可以进入临界区,通常设置相应“正在访问临界区”标志
- 退出区
-
- 用于将“正在访问临界区”标志清除
- 剩余区
-
- 代码中的其余部分
- Solution to critical-section problem
- Mutual exclusion
- Progress
- Bounded waiting
- 同步机制应遵循的准则
- 空闲则入
- 忙则等待
- 有限等待
- 让权等待
- 进程互斥的方法
- 软件
-
- 算法1
-
- 缺点
-
- 强制轮流进入临界区,没有考虑进程的实际需要。容易造成资源利用不充分:在进程1让出临界区之后,进程2使用临界区之前,进程1不可能再次使用临界区
- 算法2
-
- 优点
-
- 不用交替进入,可连续使用;
- 缺点
-
- 两进程可能都进入不了临界区
- Pi和Pj在切换自己flag之后和检查对方flag之前有一段时间,如果都切换flag,都检查不通过。
- 算法3
-
- 硬件
-
- 特殊硬件指令 --- 原子地执行,因而保证读写操作不被中断
-
- Test and Set(TS)指令
-
- SWAP指令
-
- 优点
-
- 适用于任意数目的进程,在单处理器或多处理器上适用
- 简单,容易验证其正确性
- 可以支持进程内存在多个临界区,只需为每个临界区设立一个布尔变量
- 缺点
-
- 等待要耗费CPU时间,不能实现“让权等待”
- 可能饥饿:从等待进程中随机选择一个进入临界区,有的进程可能一直选不上
- 可能死锁
- Semaphore:代表可用资源实体的数量
- Can only be accessed via two indivisible(atomic) operations
-
- P(S):
-
- while S<= 0 do no-op;
- --S;
- V(S):
-
- ++S;
- 在信号量经典定义下,信号量S的值不可能为负
-
- S >= 0
-
- 可供并发进程使用的资源数
- S < 0
-
- 其绝对值就是正在等待进入临界区的进程数
- 信号量初始化指定一个非负数值,表示空闲资源总数
- Two types of semaphore
-
- Counting semaphore
-
- Integer value can range over an unrestricted domain
- Binary semaphore
-
- Integer value can range only between 0 and 1
- 利用信号量来描述前趋关系
-
- 并发执行的进程P1和P2中,分别有代码C1和C2,要求C1在C2开始前完成
- 为每个前趋关系设置一个同步信号量,其初值为0
- 方法:前趋图
-
- 若图中存在结点S1指向结点S2的有向边,表示进程P1中的程序段S1应该先执行,而进程P2中的程序段S2后执行。设置一个信号量s,初值为0,将V(s)放在S1后面,而在S2前面先执行P(s)。
- 进程P1的语句序列为:S1;V(s)
- 进程P2的语句序列为:P(s);S2
- Classical problems of synchronization
- Dining-philosophers problem
-
- Philosopher i:
- Philosopher i:
while (true) {
// get left chopstick
chopStick[i].P();
// get right chopstick
chopStick[(i + 1) % 5].P();
// eat for awhile
//return left chopstick
chopStick[i].V();
// return right chopstick
chopStick[(i + 1) % 5].V();
// think for awhile
}
-
- Bounded-buffer problem
-
- Readers and writers problem
-
- 问题描述
-
- 对共享资源的读写操作,任一时刻“写者”最多只允许一个,而“读者”则允许多个
- 读写互斥
- 写写互斥
- 读读互斥
- 如果读者来:
-
- 无读者、写者,新读者可以读
- 有写者等,但有其它读者正在读,则新读者也可以读
- 有写者写,新读者等
- 如果写者来:
-
- 无读者,新写者可以写
- 有读者,新写者等待
- 有其它写者,新写者等待
- PV操作讨论
- 互斥操作
-
- PV处于同一进程
- 同步操作
-
- PV不在同一进程
- 对于前后相连的两个P(S1)和P(S2),顺序是至关重要的
-
- 同步P操作应该放在互斥P操作前面(否则可能会产生死锁[WHY]),而两个V操作顺序则无关紧要
- 信号量集
-
信号量机制的缺点
-
同步操作分散:信号量机制中,同步操作分散在各个进程中,使用不当就可能导致各进程死锁(如P、V操作的次序错误、重复或遗漏)
-
易读性差:要了解对于一组共享变量及信号量的操作是否正确,必须通读整个系统或者并发程序;
-
不利于修改和维护:各模块的独立性差,任一组变量或一段代码的修改都可能影响全局;
-
正确性难以保证:操作系统或并发程序通常很大,很难保证这样一个复杂的系统没有逻辑错误;
-
- Monitors:关于共享资源的数据结构及一组针对该资源的操作过程所构成的软件模块
- 管程是把所有进程对某一临界资源的使用进行击中控制,以提高可靠性。相比之下,管程比信号量好控制,
- 管程是管理进程间同步的机制,它保证进程互斥地访问共享变量,变更方便地阻塞和唤醒进程
- 基本思想就是把信号量及其操作原语封装在一个对象内部
- 任一时刻在管程中只有一个线程使能运行的,所以程序员不必去实现互斥算法(而由管程本身实现)
- 管程的主要特性
-
- 模块化:一个管程是一个基本程序单位,可以单独编译
- 抽象数据类型:管程是一种特殊的数据类型,其中不仅有数据封装,而且有对数据进行操作的代码
- 信息封装:管程是半透明的,管程中的过程(函数)实现了某些功能,至于这些功能是怎样实现的,在其外部则是不可见的
- 实现管程的三个关键问题
-
- 互斥:仅允许一个进程调用管程中的有关入口过程进入管程,而其他调用者必须等待
-
- 入口等待队列
-
- 管程入口处应当有一个进程等待队列
- 条件变量:管程机制中引起等待的原因很多,为了区别它们,引入了条件变量
-
- 每个条件变量表示一种等待原因,对应一个等待队列(每个条件变量都需要在管程中说明)
- 针对条件变量的两个同步操作原语
-
- C.wait(c)
-
- 执行此操作的进程排入c队列的尾部
- C.signal(c)
-
- 如果c队列为空,则相当于空操作,执行此操作的进程继续;否则唤醒第一个等待者
- 同步