操作系统清华向勇陈渝版笔记(九) 同步协同多道程序设计和并发问题,同步互斥,死锁,临界区
2017-12-21 14:59
876 查看
前篇在此:
操作系统清华向勇陈渝版笔记(七) 进程与线程 PCB TCB 进程挂起 用户线程 内核线程 轻量级进程 僵尸队列
操作系统(八)CPU调度 短剩余时间 吞吐量 轮循 实时调度 多处理器调度 (清华 向勇 陈渝版)
index
9-1 同步互斥、临界区、死锁、互斥概念等等
9-2 临界区和三种满足性能的方法
独立的线程:不和其他线程共享资源或状态,不交互,所以具有确定性(输入状态决定结果),可重现(能重现起始条件),I/O,调度顺序不重要
合作的线程:在多个线程中共享状态,不确定性,不可重现
不确定性和不可重现意味着BUG可能是间歇性发生的
为什么要合作?
共享资源(嵌入式系统);
加速,效率高(I/O操作和计算可以重叠,拆分小任务,流水,并行,多处理器);
模块化:大程序分解成小程序,使系统易于扩展
存在的问题——>举例:
希望是
无论多个线程的指令序列怎么交替,程序都必须正常工作,调试难度很高;
不确定性要求并行程序的正确性,一定更要预先思考。
Race condition 竞态条件
结果依赖于并发执行的时间点 顺序/时间
怎么避免?如何不让指令被打断?
atomic operation原子操作
指一次不存在任何中断或者失败的执行,要么成功done要么没执行
实际操作往往不是原子的,甚至单个机器指令都不一定是原子的。
对内存的load store是原子的,但++ 、–都不是
临界区critical section:
进程中访问共享资源的代码区域,且当另一个进程处于相应代码区域时便不会执行。
互斥mutual exclusion:
任一时刻只能有一个进程进入临界区访问。当一个进程处于临界区并访问共享资源时,没有其他进程会处于临界区,并访问任何相同的共享资源。
死锁Dead lock
多个进程相互等待完成特定任务,而最终没法继续自身任务
饥饿starvation:
一个可执行的进程长期等待执行,被调度器持续忽略。
可以留便签,但还是做不到,因为上下文切换不知道何时会发生。
便签上打不同的标签→不同标签的Note+不同代码的线程/进程
一个while 一个if
busy-waiting 当A等待DO nothing时浪费了CPU的时间,忙等待。
而且有不对称性
以买面包bread为例:
互斥
progress前进,如果一个线程想要进入临界区,不会一直死等,总能成功
优先等待,如果一个线程i处于入口区,那么i的请求被接受之前,其他线程进入临界区的时间是有限制的。否则,饥饿。是对progress的补充
无忙等待:尽量不要忙等,如果进程一直等待进入临界区,那么在它可以进入之前会被挂起。(可以不满足)
三种方法:禁用硬件中断,基于软件,更高级的抽象
(1)禁用硬件中断:
没有中断,就没有上下文切换,没有并发。减少不确定性。进入临界区禁用中断,退出时再开启。
问题:
一旦禁用中断,线程无法停止
整个系统都会停下来,I/O啥的都没用了,其他线程可能会饥饿 影响效率
临界区要是太长咋整?无法限制响应中断所需的时间,可能有硬件影响。一般都用于短的临界区时间。
如果两个CPU并行的话,一个CPU只能屏蔽自身,另一个仍可能产生中断。
(2)基于软件的方式
共享变量初始化
Ti在退出临界区后做其他的事情去了,Tj想继续运行,但必须等待Ti处理临界区
必须是一种交替循环。
改进,用数组flag
满足进程Pi and Pj之间互斥的正解:
对N个进程,Eisenberg and Mcguire’s algorirhm
进程i之前的先进入了i再进入,i之后的等i先进入
Bakery算法
N个进程的临界区 排号,只有一个窗口
进入前,进程接受一个数字,数字最小的进入临界区。如果Pi和Pj
得到相同的数字,比较i,j的大小,小的进入。编号方案总是按照枚举的增加顺序生成数字。
问题:
复杂;需要共享数据项
耗资源;需要忙等待,浪费CPU时间
没有硬件保证的情况下无真正的软件解决方法;load store必须要是原子操作
(3) 基于硬件原子操作的高层抽象实现
硬件提供了一些原语,用原子操作直接实现进退临界区
锁是一个抽象的数据结构,获得锁就是进入临界区的实现过程
lock_next_pid->acquire();
new_pid=next_pid++ ;
lock_next_pid->release();
大多数现代体系结构都提供特殊的原子操作指令
(1) test-and-set 测试和置位
从内存中读值,判断是否为1并返回,同时设置内存值为1
(2) exchange 交换 输入两个内存单元,交换其值并返回
这两条如果有一条可以是原子指令,就可以完成
lock::release(){value=0;}
可以支持N个进程的操作且是一样的,都很简洁
改进:让它不忙等
当它等待其他事件时,可以睡眠/阻塞
如果临界区短,开销小于上下文切换的开销,直接忙等,否则要引入WAITING和wakeup
实现简单,易扩展到多临界区,开销小,适用于单处理器或共享主存的多处理器中任意数量的进程 广泛使用
缺点: 还是有忙等,浪费时间;
抢LOCK随机可能某个一直抢不到,当进程离开临界区,且多个进程在等待时可能导致饥饿;
也许死锁,一个低优先级的进程拥有临界区,一个高优先级的 进程也需求,那么高优先级进程忙等,占用cpu,低优先级的不能释放Lock,要通过优先级反转来解决。
总结:
用锁来解决互斥问题,锁是高层编程抽象,需要一定硬件支持
常用三种:禁用中断(仅可单处理器),软件方法(复杂),原子操作指令(单处理器或多处理器都可以)
可选的实现内容:有忙等待,无忙等待(进程睡眠)
下一篇在此:操作系统清华大学版笔记(十) 信号量、管程、条件互斥、经典同步问题(读者写者、哲学家问题)
操作系统清华向勇陈渝版笔记(七) 进程与线程 PCB TCB 进程挂起 用户线程 内核线程 轻量级进程 僵尸队列
操作系统(八)CPU调度 短剩余时间 吞吐量 轮循 实时调度 多处理器调度 (清华 向勇 陈渝版)
index
9-1 同步互斥、临界区、死锁、互斥概念等等
9-2 临界区和三种满足性能的方法
正文
9-1 同步互斥、临界区、死锁、互斥概念等等
多个进程会交互,对共享资源的访问。处理不当就会饥饿,死锁。独立的线程:不和其他线程共享资源或状态,不交互,所以具有确定性(输入状态决定结果),可重现(能重现起始条件),I/O,调度顺序不重要
合作的线程:在多个线程中共享状态,不确定性,不可重现
不确定性和不可重现意味着BUG可能是间歇性发生的
为什么要合作?
共享资源(嵌入式系统);
加速,效率高(I/O操作和计算可以重叠,拆分小任务,流水,并行,多处理器);
模块化:大程序分解成小程序,使系统易于扩展
存在的问题——>举例:
希望是
无论多个线程的指令序列怎么交替,程序都必须正常工作,调试难度很高;
不确定性要求并行程序的正确性,一定更要预先思考。
Race condition 竞态条件
结果依赖于并发执行的时间点 顺序/时间
怎么避免?如何不让指令被打断?
atomic operation原子操作
指一次不存在任何中断或者失败的执行,要么成功done要么没执行
实际操作往往不是原子的,甚至单个机器指令都不一定是原子的。
对内存的load store是原子的,但++ 、–都不是
临界区critical section:
进程中访问共享资源的代码区域,且当另一个进程处于相应代码区域时便不会执行。
互斥mutual exclusion:
任一时刻只能有一个进程进入临界区访问。当一个进程处于临界区并访问共享资源时,没有其他进程会处于临界区,并访问任何相同的共享资源。
死锁Dead lock
多个进程相互等待完成特定任务,而最终没法继续自身任务
饥饿starvation:
一个可执行的进程长期等待执行,被调度器持续忽略。
可以留便签,但还是做不到,因为上下文切换不知道何时会发生。
便签上打不同的标签→不同标签的Note+不同代码的线程/进程
一个while 一个if
busy-waiting 当A等待DO nothing时浪费了CPU的时间,忙等待。
而且有不对称性
以买面包bread为例:
假设有一些锁 breadlock.acquire(): -----enter the critical section 在锁被释放前一直等待,然后获得锁 if(no bread) {buy bread;} lock.release():-----exit the critical section 解锁并唤醒任何等待中的锁 release and acquire都要是原子操作,如果两个进程同时发现了一个被释放了的锁,那么只有一个能获得
9-2 临界区和三种满足性能的方法
临界区的特点:互斥
progress前进,如果一个线程想要进入临界区,不会一直死等,总能成功
优先等待,如果一个线程i处于入口区,那么i的请求被接受之前,其他线程进入临界区的时间是有限制的。否则,饥饿。是对progress的补充
无忙等待:尽量不要忙等,如果进程一直等待进入临界区,那么在它可以进入之前会被挂起。(可以不满足)
三种方法:禁用硬件中断,基于软件,更高级的抽象
(1)禁用硬件中断:
没有中断,就没有上下文切换,没有并发。减少不确定性。进入临界区禁用中断,退出时再开启。
问题:
一旦禁用中断,线程无法停止
整个系统都会停下来,I/O啥的都没用了,其他线程可能会饥饿 影响效率
临界区要是太长咋整?无法限制响应中断所需的时间,可能有硬件影响。一般都用于短的临界区时间。
如果两个CPU并行的话,一个CPU只能屏蔽自身,另一个仍可能产生中断。
(2)基于软件的方式
共享变量初始化
int turn=0; //初始是Ti进入临界区 对Thread Ti,有: do{ while(turn!=i); // busy waiting critical section turn=j; //after exit turn=j reminder section }while (1);
Ti在退出临界区后做其他的事情去了,Tj想继续运行,但必须等待Ti处理临界区
必须是一种交替循环。
改进,用数组flag
flag[i]==1 进程i想进入临界区执行 int flag[2]; flag[0]=flag[1]=0; do{ while(flag[j]==1) ; //初始flag都是0,没有满足互斥 flag[i]==1; //先请求 critical section flag[i]=0; remainder section }while (1) 如果先flag[i]=1再while循环,可能死锁
满足进程Pi and Pj之间互斥的正解:
use two shared data items int turn; // who ‘s turn to enter the critical section bollean flag[]; //whether the process is ready to enter PETERSON算法 do{ flag[i] = TURE; turn=j; while(flag[j]&&turn==j ) ; CRITICAL SECTION flag[i]=FLASE; REMAINDER SECTION } while(TURE);
对N个进程,Eisenberg and Mcguire’s algorirhm
进程i之前的先进入了i再进入,i之后的等i先进入
Bakery算法
N个进程的临界区 排号,只有一个窗口
进入前,进程接受一个数字,数字最小的进入临界区。如果Pi和Pj
得到相同的数字,比较i,j的大小,小的进入。编号方案总是按照枚举的增加顺序生成数字。
问题:
复杂;需要共享数据项
耗资源;需要忙等待,浪费CPU时间
没有硬件保证的情况下无真正的软件解决方法;load store必须要是原子操作
(3) 基于硬件原子操作的高层抽象实现
硬件提供了一些原语,用原子操作直接实现进退临界区
锁是一个抽象的数据结构,获得锁就是进入临界区的实现过程
lock_next_pid->acquire();
new_pid=next_pid++ ;
lock_next_pid->release();
大多数现代体系结构都提供特殊的原子操作指令
(1) test-and-set 测试和置位
从内存中读值,判断是否为1并返回,同时设置内存值为1
(2) exchange 交换 输入两个内存单元,交换其值并返回
这两条如果有一条可以是原子指令,就可以完成
lock::release(){value=0;}
可以支持N个进程的操作且是一样的,都很简洁
改进:让它不忙等
当它等待其他事件时,可以睡眠/阻塞
class lock{ int value = 0; waitqueue q;} lock::acquire(){ while (test-and-set(value)){ add this TCB to wait queen q; schedule();} } lock::release(){ value=0; remove one thread t from q; wakeup(t); }
如果临界区短,开销小于上下文切换的开销,直接忙等,否则要引入WAITING和wakeup
int lock=0; to process Ti that want to enter the critical section int key; do{ key=1; while(key==1)exchange(lock,key); //when key!=1 swap out of critical section //at that time key=0 lock=1 lock=0; remainder section }
实现简单,易扩展到多临界区,开销小,适用于单处理器或共享主存的多处理器中任意数量的进程 广泛使用
缺点: 还是有忙等,浪费时间;
抢LOCK随机可能某个一直抢不到,当进程离开临界区,且多个进程在等待时可能导致饥饿;
也许死锁,一个低优先级的进程拥有临界区,一个高优先级的 进程也需求,那么高优先级进程忙等,占用cpu,低优先级的不能释放Lock,要通过优先级反转来解决。
总结:
用锁来解决互斥问题,锁是高层编程抽象,需要一定硬件支持
常用三种:禁用中断(仅可单处理器),软件方法(复杂),原子操作指令(单处理器或多处理器都可以)
可选的实现内容:有忙等待,无忙等待(进程睡眠)
下一篇在此:操作系统清华大学版笔记(十) 信号量、管程、条件互斥、经典同步问题(读者写者、哲学家问题)
相关文章推荐
- 操作系统清华大学版笔记(十) 信号量、管程、条件互斥、经典同步问题(读者写者、哲学家问题)
- 操作系统学习笔记(13) 互斥与同步的经典问题 -哲学家进餐问题
- 进程、线程知识点总结和同步(消费者生产者,读者写者三类问题)、互斥、异步、并发、并行、死锁、活锁的总结
- Linux下生产者消费者问题详细分析(操作系统期中考试论文---并发程序的同步和互斥)
- 进程、线程知识点总结和同步(消费者生产者,读者写者三类问题)、互斥、异步、并发、并行、死锁、活锁的总结
- 操作系统学习笔记(10) 互斥和同步的经典问题
- 计算机操作系统笔记(5)--进程管理之经典进程的同步问题
- 操作系统同步互斥经典问题——读者写者问题
- 操作系统同步互斥经典问题——读者写者问题
- 操作系统from清华大学向勇,陈渝 笔记(二)操作系统的启动、中断、异常、系统调用
- 操作系统from清华大学向勇,陈渝 笔记(一)绪论
- Linux下线程的同步与互斥以及死锁问题整理
- 同步和互斥的一些问题(死锁,优先级逆转)
- 操作系统经典同步互斥问题——哲学家就餐
- 【操作系统笔记】同步与互斥的区别和联系
- Java并发编程之同步互斥问题
- iOS并发编程笔记,包含GCD,Operation Queues,Run Loops,如何在后台绘制UI,后台I/O处理,最佳安全实践避免互斥锁死锁优先级反转等,以及如何使用GCD监视进程文件文件夹,并发测试的方案等
- 操作系统-并发:互斥与同步
- 操作系统进程同步互斥经典问题之读者写者问题
- 操作系统经典同步互斥问题——生产者消费者问题