操作系统简记
中断、异常和系统调用
区别
-
处理时间
- 中断:异步
- 异常:同步
- 系统调用:异步或同步
异步:不知道什么时候会产生
-
响应
- 中断:持续,对用户应用程序是透明的
- 异常:杀死或者重新执行意想不到的应用程序指令
- 系统调用:等待和持续
系统调用
如C语言调用printf时调用的是内核态的write()
函数调用
只存在一个栈空间,系统调用存在两个栈(因此系统调用时候需要有堆栈切换)
系统调用开销大于函数调用
物理内存管理
不同进程看到的是一段连续可以使用的地址空间,实际上在磁盘中这些空间不一定连续(逻辑地址空间:运行程序看到的空间)
进程多的时候,会出现内存不够,操作系统会将最需要放在内存的数据放在内存,其他一些数据临时地放在硬盘上(虚拟内存)。
物理地址空间:硬件支持的地址空间
逻辑地址空间:一个运行的程序所拥有的内存范围(一个程序中,一个变量的名字就是一种逻辑地址)
ALU:计算逻辑单元
MMU:内存管理单元
连续内存分配和内存碎片
内部碎片,比如程序需要510字节,但是最小只能分配512字节,2字节就是内部碎片。
动态分区分配
最先:找的第一个大于程序指定的大小,就ok
最佳:全部扫一遍,看哪个最合适,大的最小的一个
最差:大的最多的
释放慢:因为不是按地址排序的,但是合并的时候需要查询相邻地址是否空闲
主要是:空闲列表排序方式,查询和释放地址的开销。
碎片整理
通过调整进程占用的分区位置,来减少或避免分区碎片
分区对换
通过抢占并回收处于等待状态进程的分区,以增大可用内存空间。
非连续分配内存
外排序:每次读一部分进来排序,然后存进外存;最后重新捋一遍。
段>页
段:访问方式和存储数据等属性想相同的一段地址空间
每个段对应一个连续的内存“块”
基于段的访问内存方式:
页式存储管理
基于页真的物理地址计算:用二元组表示
逻辑地址空间和物理地址空间的划分是类似的
中间的映射由页表完成
页内偏移 = 帧内偏移
页式存储管理机制的性能问题:
访问一个内存单元需要2次内存访问:1、获取页表项; 2、访问数据
快表:使用缓存
命中,直接得到物理地址;不命中,查内存,并存到快表
多级页表
将页号分层
访问内存k + 1次
往往进程用不到所有页表内容,所以这样方式可以减少实际页表大小
实例:
大地址系统下(比如说64位系统),页表还是会比较多,“页寄存器”可以解决这一问题。一页对应一个页寄存器。
页寄存器缺点:由于需要完成逻辑地址到物理地址的转换,所以需要搜索页寄存器存的是哪个逻辑地址页表,所搜会比较困难。
一个解决方式:哈希
虚拟存储
交换、覆盖
交换
交换技术面临的问题
局部性原理
这些数据全在内存中的话,使用时间是一样的。如果物理内存的量小,使用了虚拟存储,局部性特征不怎么好,那么时间差距是很大的。
虚拟存储做的事
把原来放到内存的进程地址空间信息,把一部分放到外存中。
由操作系统来实现,且一部分一部分(不以进程伟单位)导到外存里去。
虚拟存储的基本概念
虚拟内存的基本特征
1、不连续
2、大用户空间
虚拟页式存储管理
在页式管理的基础上,增加请求调页和页面置换
虚拟页式存储中的地址转换
页表中有一些控制位
缺页异常
概念:发现页表项中这一页不在物理内存中,产生缺页异常,缺页异常将内容读到内存中,再重新执行这条指令。
缺页异常处理大致流程:
如果没找空间夜侦,就需要利用置换算法,将当前某一页写出去,然后占用。
页面置换算法
局部置换算法:不需要考虑分配给进程的物理页面数是多少。
全局:考虑不同进程之间对进程需要量的差异。
当出现缺页异常,需要调入新页面而内存已满的情况时,置换算法选择被置换的页面。
- 最优页面置换算法
- 先进先出算法
- 最近最久未使用算法
- 时钟页面算法
- 最不常用算法
最近最久未使用算法
示例
每次都需要更新内存中页表的使用时间
Belady现象
FIFO算法会有此现象
可能出现:分配的物理页面增加时候,缺页次数反而升高的异常现场。
原因:FIFO算法的置换特征与进程访问内存的动态特征矛盾,简单点说就是换出去的正好是进程需要访问的内存。
clock算法是FIFO和LRU的折中
全局置换算法
全局置换算法为进程分配可变数目的物理页面
工作集置换算法
缺页率算法
通过调节常驻集大小,使每个进程的缺页率保持在合理范围
缺页率算法和工作集算法区别:工作集算法每次都需要更新维护,缺页率算法不需要,在时间间隔间隔足够大的时候才会更新集合。
负载控制
进程多,每个进程的物理页面少,进一步地,缺页会增多。这就需要通过调节并发进程数来进行负载控制。=> 控制内存大小
进程和线程
进程:具有一定独立功能的程序再一个数据集合上的一次动态执行过程
进程是操作系统处于执行状态程序的抽象
程序 = 文件(静态的可执行文件)
进程 = 执行中的程序 = 程序 + 执行状态
进程的特点:动态性,并发性,独立性,制约性
进行是动态的,程序是静态的
程序是有序代码的集合,进程是程序的执行,进程有核心态/用户态
进程的暂时的,程序是永久的
进程控制块PCB
操作系统管理控制进程运行所用的信息集合。操作系统用PCB来描述进程的基本情况和运行变化的过程。每个进程都有唯一一个PCB。内容:标识信息,处理机现场保护,进程控制信息;例如PID,UID,调度优先级,寄存器
进程状态
三状态进程模型:就绪、运行、等待
挂起进程模型
处于挂起状态的进程映像再磁盘上,目的是减少进程占用内存
线程
希望在一个进程内也可以进行并发操作
线程缺点:一个线程崩溃,会导致所属进程的所有线程崩溃
线程分类:用户线程、内核线程、轻权进程——一个进程可有一个或多个轻量级进程,每个轻权进程由一个内核线程支持。
实践结果来看,一对一是较好的。
进程控制(感觉不是很重要)
进程切换
中断或系统调用时:保存进程状态到PCB(进程控制块)
进程控制块的信息:状态寄存器,地址空间起头位置,第一级页表位置;占用的资源信息:存储、堆栈、和要保护现场的信息。
进程创建
unix下的进程创建:
加载:exec()用新程序重写当前进程
系统没有用户进程的情况下,CPU会进行创建空闲进程。
进程加载
指用户的应用程序通过系统调用exec()加载,来完成新的可执行文件的加载。
进程的等待与退出
等待和退出是父子进程之间的交互,完成子进程资源的回收。
处理机调度
调度策略
比较调度算法的好坏:
前两个:系统利用率角度,后三个:用户角度。
先来先服务、短进程优先和最高响应比优先调度算法
先来先服务:周转时间长
短进程优先
允许抢占线程
特点:具有最优平均周转时间
缺点:
-
可能导致饥饿,连续的短进程会使长进程无法获得CPU资源
-
需要预知未来,需要预估下一个CPU计算的持续时间
- 询问用户
- 根据过去预测未来
最高响应比优先算法
避免了饥饿;(关注进程的等待时间,避免无限期推迟)
时间片轮转、多级反馈队列、公平共享调度算法
时间片:分配处理机资源的基本时间单元
按照先来先服务的原则,每个进程执行一个时间片。
多级反馈队列算法(KLFQ)
实时调度
可调度性:
多处理机调度
同步是多处理机调度的一个大问题
优先级反置
(大于2个进程)
两种解决方案:
同步互斥
原子操作:一次不存在任何中断或失败的操作
操作系统利用同步机制在并发执行的同时,保证一些操作是原子操作。
互斥:一个进程占用资源,其他进程就不可以使用资源
死锁:多个进程占用部分资源,形成循环等待;
饥饿:其他进程可能轮流占用资源,一个进程一直得不到资源
临界区
进程中访问临界资源的一段需要互斥执行的代码
临界区的访问规则
临界区的实现方法
禁用中断:其他进程无法对当前进程做打扰,但是对系统中断响应有影响
缺点:禁用中断后,进程无法被停止,整个系统会停下来,导致其他进程处于饥饿状态;临界区可能很长,无法确定响应中断所需的时间。
软件方法:共享变量协调方式来完成
在进去区需要不断查询共享变量的状态
高级抽象同步方法
锁在等待时是消耗CPU的。
无忙等待锁
Acquire中,如果锁被占用,将线程压进等待队列中,并且执行调度程序,让出CPU使用权。
Release: 将线程从等待队列弹出,压进就绪队列中。
原子操作指令锁的优点:
信号量与管程
信号量是操作系统提供的一种协调共享资源访问的方法
- 软件同步时平等线程间的一种同步协商机制
- OS时管理者,地位高于进程,信号量由OS负责管理
- 用信号量表示系统资源的数量
(由荷兰科学家Dijkstra在20世纪60年代提出)
信号量的实现
信号量的使用
必须成对使用P和V操作
管程
改进信号量处理临界区的一些麻烦
哲学家就餐问题
死锁:比如哲学家就餐问题,五个盘子围一圈放,每两个盘子之间放一把叉子,在算法写的简单的情况下,对于每个叉子都建立信号量,在哲学家就餐时,需要对左右两个叉子进行P操作。在 五个哲学家都拿起了左边的叉子的情况下,也就是说都进行了第一个P操作,这时候没有叉子了,所有哲学家都要等下去,不会退出,就造成了死锁的情况。
改进的方案:
设一个全局的锁,把就餐的事变成临界区的操作
只能有一个人就餐
方案3
读者-写者问题
上述实现:读者优先
读者持续不断进入,则写者就处于饥饿;
饥饿状态例子:在读者-写者问题中,若采用读者优先的策略,即便是读者在写者后面进来,读者也会先读。
管程可以比较简单地实现读者优先/写者优先
写者优先:
死锁
死锁问题
由于竞争资源,两个或者更多线程在执行中出现,永远相互等待的现象
死锁处理方法
死锁预防
确保系统不会进入死锁状态,(四个条件不满足)
互斥:
互斥的共享资源封装成可同时访问
持有并等待:
进程请求资源时,要求它不持有任何其他资源;
进程在开始时候,申请所有需要的资源
非抢占:
进程请求不能立即分配的资源,则释放已占有资源
循环等待:
对资源排序,要求进程按顺序请求资源
死锁避免
在使用前进行判断,只允许不会出现死锁的进程请求资源。
利用先验信息,在分配资源时判断是否会出现死锁,只在不会死锁时分配资源。做法:
要求进程声明需要资源的最大数目
限定提供与分配的资源数量,确保满足进程的最大需求
动态检查资源分配状态,确保不会出现环形等待
死锁检测和恢复
检测到系统进入死锁后,进行恢复
一般由应用进程来处理
银行家算法
基本思路:当前剩余资源可以满足某一个线程的未来需要,迭代到最后可以满足所有线程的未来需要,那么就认为找到了一个安全系列,使得系统是安全的。
死锁检测
允许系统进入死锁状态,维护系统的资源分配图,定期调用死锁检测算法来搜索图中是否存在死锁;出现死锁时,用死锁恢复机制进行恢复。