1. 第七章 死锁
1.1. 死锁的概念
一些阻塞进程的集合,每个进程都持有资源不释放并且等待其它进程的资源
1.2. 死锁产生的四个条件
- 互斥
- 持有并等待
- 非抢占
- 循环等待
1.3. 资源分配图
- 进程 圆圈
- 资源 方框
- 死锁一定有环
- 有环不一定死锁
- 如果每个资源只有一个,那么死锁
- 如果每个资源有多个,那么可能死锁
1.4. 处理死锁的方法
- 预防
- 破坏死锁的四个条件
- 避免
- 在请求达到时进行安全性检查以明确请求是否能被满足
- 检测恢复
- 忽略
1.4.1. 预防
- 互斥条件 –无法破坏
- 持有并等待
- 一个进程run之前一次性分配所有资源
- 申请资源之前必须手上没有资源了
- 导致的问题:
- 资源浪费:有些资源只是进程用了一会就不用了,但是还是持续占有
- 饥饿:有些资源长时间被其它进程占用,可能导致它迟迟不能执行
- 非抢占
- 如果一个进程持有资源并且正在申请的资源不能得到满足,那么这个进程释放它的所有资源,相当于一种抢占
- 循环等待
- 给所有资源排序,每个进程只能请求比它所持有的资源的序号大的资源,想要获得低序号资源必须释放高序号的资源
1.4.2. 避免
当进程在申请资源时,系统会判断当前如果分配资源系统是否时安全状态,如果不是则不会分配资源。
1.4.2.1. 安全状态:
能找到一个进程的序列
1.4.2.2. 安全算法(safe algorithm)
集合Allocation[i,j] 表示当前分配给i进程的j号资源,集合Maximun[i,j]表示i进程最多需要j资源的数量,Avaiable[i]表示 i资源剩余的数量,那么Need[i,j]=Maximum[i,j]-Allocation[i,j]。令work=Available,看看能否有进程的Need[i] < work ,如果满足,就将work+=Allaction[i],直到找不到这样的进程,如果还剩下某些进程没执行,就不安全,否则就是安全的,得到的进程序列就是安全序列。
1.4.3. 死锁检测
维持一个wait-for 图(把resource方框去掉,只剩下process圆圈),周期性检测图中是否存在环
1.4.3.1. 死锁检测算法
和安全算法差不多,需要一个Allocation 集合、Available集合、Request集合,然后检测是否安全。。
1.4.4. 检测后
- 资源剥夺法
- 挂起进程,并剥夺资源,应防止进程饿死
- 撤销进程法
- 杀手死锁中的全部进程或者部分进程
- 进程回退法
- 将进程回退到解除死锁的地步,要系统记录进程历史信息。
2. 第八章内存管理
2.1. 背景
2.1.1. base 和limit寄存器
用来定义逻辑地址空间
- 绑定指令和数据到内存发生在三种情况下
- 编译时间:如果你在编译的时候知道程序将分配在内存的什么地方,那么可以产生固定的代码。如果起始地址变了就要重新编译。
- 这个只适用 单道程序环境
- 装入时间:编译的时候不知道在哪,编译的时候必须产生可重定向的代码,在装入的时候绑定地址,地址变了就重新装入
- 这个一般在多道程序环境下,多个目标模块的地址通常都是从0开始,因此装入时需要重定位,这种叫静态重定位,这种需要在装入内存时分配所有空间,进入内存后不能再内存中移动。
- 执行时间:如果程序在执行的时候会从一个内存分段移动到另一个内存分段,执行时间才能绑定地址, 这需要硬件的支持实现地址的映射
- 这种叫动态重定位,它可以将程序分配到不连续的内存中,并且在程序运行之前只需要装入部分的代码就可以运行,在程序运行期间,根据需要动态申请内存分配;便于程序段的共享,可以向用户提供一个比存储空间大的多的空间。
- 编译时间:如果你在编译的时候知道程序将分配在内存的什么地方,那么可以产生固定的代码。如果起始地址变了就要重新编译。
2.1.2. MMU (memory management unit)
- 将逻辑地址加上重定向寄存器的值,变成物理地址,用户只接触到逻辑地址
2.1.3. 动态装载
使用时再装载,这对于很少使用的代码块来说非常有用
2.1.4. 动态链接
用一个stub 桩来 代表一个模块,桩里面写着如何加载这个模块,当需要这个模块时,执行这个桩的代码将模块装入。这对于库的调用比较有用
2.2. 交换
2.3. 连续分配
2.3.1. 固定分区
系统被固定分为n个区域,每个区域都有自己的job’queue 或者 大家公用一个jobqueue
2.3.2. 可变分区
hole表示可用分区,当一个进程来临时,系统给它找个足够大的分区。
系统保存需要以下信息:
- 已经分配的内存
- 空闲的内存
2.3.2.1. 分配策略
- first-fit
- best-fit
- worst-fit:总是分配最大的
如果hole比所需要的内存大,就将其一分为二,剩下来的成为新的hole,如果新的hole会与邻居合并成一个hole
2.3.3. 碎片
- 外部碎片 – 可进行碎片整理(compaction)
- 内部碎片
2.4. 分页
- 逻辑地址划分为页(page)
- 物理地址划分为框架(frame)
- 需要用 pagetable 来将逻辑地址转化为物理地址
- 会产生内部碎片
2.4.1. 地址转换
- cpu产生的逻辑地址被划分为 page number(p) 和page offset (d)
- page table 每一项存着frame number
2.4.2. page talbe实现
- page table 本身放在内存里面
- PTBR(page table base register) 指向page table
- TLB(transaction look-aside buffers) 提高访问内存速度
- TLB表结构是 page number| frame number
2.4.3. 有效访问时间的计算
注意每种情况都要加上TLB的访问时间
2.4.4. 内存保护
- 每个frame 有一个valid和invalid位
- 或者用一个 page table length register 去存pagetable的长度,因为系统会为 每个进程分配一张页表 ,这个类似于base-limit 寄存器
2.5. 页表结构
2.5.1. 多层页表
以两层页表为例
cpu地址结构为 p1|p2|d ,d=log(页表大小),p2=log(页表大小/页表每项大小);p1=CPU长度-p2-d
2.5.2. hash 页表
这种页表存储的是表项结构是,cpu地址结构不变, page_number |frame_number ,然后通过hash的方式定位表项。
2.5.3. 反向页表结构
逻辑地址结构为 pid + |p|d ,页表每项结构为 pid|p ,第几项(下标i)对应着第几个frame
2.6. 分段
将进程空间分成几段连续空间
2.6.1. 段表
- CPU 地址结构为
段号|段内偏移
- 段表结构
limit|base
,段号就是段报表项的下标, - 通过段号找到段的起始地址,通过段内偏移加上起始地址找到物理地址 ,通过limit 判定是否越界
- 通过 STBR(segment table base register) 来指向段表本身的起始位置 和 STLR(segment table length register) 来指向段表的长度,一个进程的段号不能超过此长度,否则属于越界访问。
2.7. 段页式
将作业地址空间分成若干段,再将每个段分成若干页。
3. 第九章 虚拟内存
3.1. 背景
3.1.1. 为什么引入虚拟内存?
- 进程只需要调入部分代码就能执行
- 进程的访问具有时间局部性和空间局部性。
虚拟内存技术实际上就是建立“内存-外存“ 的两级存储器的结构,利用局部性原理实现高速缓存
3.1.2. 虚拟内存的实现
- 按需分页
- 按需分段
3.2. 按需分页(demand paging)
- 当页被需要的时候再将其调入内存
- 增加一个invaid-valid位来表示当前的页是否在内存中,为0的时候表示不在内存中,会产生一个page fault
- page-fault时,系统会判断这个page是否是合法的,合法的话就找一个空的frame然后将page对应的内容从磁盘调入,并将对应的frame号写入pagetable,将标志位置1,重新执行指令
3.2.1. 有效访问时间的计算
设p为产生pagefault的概率
注意swap out只有在内存中的内容被改变时才需要,否则直接swap in覆盖就可以
3.3. copy on write
允许父进程和子进程共享页,只有当修改页时再copy这个页,可以更加高效的创建子进程
3.4. 页面置换
当没有空闲页面的时候,需要将不用的页面置换到辅存中
3.4.1. FIFO算法
产生 belady’s anormaly ,即不满足frames越多 page fault 越少的规律
3.4.2. 最优算法OPT
每次换出将来最长一段时间不被用到的那个页
3.4.3. 最近最久未使用算法LRU
- 实现使用一个栈,每当一个页被引用就将其放到栈顶
- 需要改变6个指针,比较麻烦
3.4.4. LRU近似算法
- 用一个reference bit 位,当页面被引用时置为1,初始化为0
- 额外增加一个byte,每间隔一定的时间,reference bit | addtional byte
- 隔一段时间后就将这个byte连同reference右移一位,选最小的号作为victim
- second chance 如果一个页面的reference bit=0的话 ,就选中它,如果是1,就将它置0,然后顺时针继续看下一个,直到找到0
3.5. 虚拟内存
3.5.1. 固定分配(fixed allocation)
- 平均分
- 按进程大小分
3.5.2. 优先级分配
当进程p产生page fault 时可以从它自己和比它优先级低的进程page里进行置换
3.5.3. 全局、局部分配
局部只能总自己的进程里找frame 置换,全局可以从所有的frame里置换
3.6. 抖动
一个进程频繁的进行页面的换进换出
抖动发生的原因:某个进程频繁访问的页面数高于可用的物理页帧数 (total size of locality >total memory size)
3.6.1. work-set 工作集
某段时间内,进程要访问的页面集合
,意思是考虑最近 次引用,需要为每个page额外添加几位去记录历史,有个timer隔一段时间(这个是指隔多少个引用)产生中断,将reference内容拷贝到历史记录中并将reference清零,这么做不是很精确,因为不知道到底是中间哪次发生了引用,想要提供精度就要增加历史记录,减小timer周期
令 表示进程i在最近 的所需要的frame数量,如果其总数超过内存大小,就会发生trashing
- 如果实际的page fault 概率太高就需要给process 分配frames
- 太低就减少frames
3.7. Other consideration
3.7.1. 预分页
提前分配可能需要的页,假设 s代表预分页的数量, 表示被访问到的概率,则 s* 为减少page fault的数量,而 (1-s)* 表示浪费的页
3.7.2. 页大小
需要考虑下面这些因素
- 碎片
- 页表大小
- I/O开销
- locality
3.7.3. TLB Reach
指tlb能访问到的内存大小
- working-set 中的页要放到tlb里面
- 通过页的大小可以增加TLB Reach
- 提供多个页面的大小
3.7.4. 程序结构
3.7.5. I/Ointerlock
4. 第十章 文件系统接口
4.1. 文件的概念
连续的逻辑地址空间
4.2. 文件操作
4.2.1. Open
在文件目录结构上找到磁盘上文件入口(entry),并将文件入口的内容调入内存
4.2.2. close
将文件入口内容从内存中调出到文件目录结构对应的磁盘位置
4.3. 目录结构
- 效率–能很快的找到文件
- 命名
- 不同用户可以使用同一个名字来命名不同文件
- 不同名字可以对应同一个文件
- 分组
- 按文件属性分类
4.3.1. 单级目录结构
所有用户共享同一个文件夹
- 命名问题
- 分组问题
- 当文件多了的时候,用户很难找到文件
- 不便于文件共享
- 优点是实现起来简单
4.3.2. 两层目录结构
为每个用户分配一个文件夹
- 能解决多用户的命名重名问题
- 文件可以在目录上实现访问限制
- 但是没有分类能力
4.3.3. 树形目录结构
- 能够对文件分类
- 查询效率高
- 由于要按照路径访问中间结点,查询速度相对较慢
- 不便于文件共享
4.3.4. 无环图目录结构
- 能够文件共享
- 文件系统的管理变得相对复杂
删除一个文件不能直接删除,可以为每个结点设置共享计数器,当计数器为零时真正删除节点,否则只删除共享链接
防止有环
- 防止链接到目录,只能链接到文件
- 新产生一个链接时,用环路检测检测是否ok
5. 第十一章 文件系统实现
5.1. 重要名词
- boot control block(per volume):记录系统引导的信息,如果系统不是装在那个区,就为空
- volume control block(per volume):记录每个分区有多少个块,块多大,多少空闲块等信息
- directory structure:一般每项是一个inode号和文件名filename
- file control block
- in-memery mount table:记录挂载的分区
- in-memery directory-structure cache:目录结构的缓存
- system-wide open-file table:全局打开文件列表,每项包含FCB和其它相关信息
- per-process open-file table:每个进程自己的打开文件列表,每项指向全局打开文件列表中的一项
5.2. 文件系统结构
- 文件结构
- 逻辑存储单元
- 有关信息的结合
- 文件系统分层组织
- 文件控制块
- 由有关文件的信息组成
5.2.1. open系统调用
首先系统查看system-wide open-file table里面这个文件是不是已经被其它进程用了(system wide open-file table 记录已经打开的文件和使用它的进程数量),如果是 的,那么就将进程自己的open-file table 的文件入口指向system-wide open-file table,并将引用数加1。否则,就从目录里面找到对应的文件名和FCB号,将FCBcopy到system-wide open-file table里,然后再将自己的 open-file table 的entry 指向system-wide open-file table。
5.2.2. close 系统调用
删掉进程的Per-process open-file table 的相关项,再将system-wide open-file table 对应项的count–,当count==0的时候,就将修改的地方写回磁盘,然后将system-wide open-file table 中的相关项删除
5.3. 目录实现
- 线性表,每项有文件名和指针指向数据块
- 容易实现
- 比较耗时
- hash表
- 比较快
- 需要解决冲突问题
5.4. 分配方法
- 连续分配
- 外部碎片
- 简单,只要包含其实块号和长度就可以
- 支持随机访问
- 浪费空间
- 文件大小不能增长
- 改进: extend-based system基于分区的系统
- 将块分成很多区,每个区都是连续的
- 一个文件可能占有多个区
- 链接分配
- 简单,只需要起始块的地址
- 小的空闲空间可以利用,不浪费
- 文件可以增长
- 不能随机访问
- 每个块都需要指针,浪费空间
- 由于文件分布在磁盘各个位置,所以磁盘的寻道时间会很长
- 不是很可靠,当中间一个块出了问题后面的都找不着了
- 改进: file-allocation table(FAT)
- 将文件的块指针集中放在一个FAT中,指针指向另一个指针
- 索引分配
- 为每个文件建立一个索引块,索引块中记录了该文件所有的块的指针
- 浪费空间,索引块可能不会完全利用(内部碎片)
- 文件大小受收到索引块大小的限制
- 改进 : 多个索引块串起来、 分级索引块
5.5. 空闲空间管理
- bit vector
- 用0表示被占用,用1表示free,可以同时读32、64位,看看是不是零就可以知道有没有空闲块
- 浪费了空间
- 很容易获得连续空间
- linked-list
- 不浪费空间(指针使用的是空闲空间)
- 不能获取连续空间
- linked-list +grouping
- 每个空闲块的最后一个指针指向另一个空闲块,这个空闲块的n个指针指向的空闲块称为一个组,前n-1个都是真正的空闲块
- linked-list + Address counting
- 每个单元存的是一个连续空闲块的首地址和个数
- 容易找到连续空间
5.6. 效率和性能
- 磁盘分配算法和目录算法
- 文件数据的类型
6. 第十二章 大存储结构
6.1. Disk structure 磁盘结构
- 将磁盘的一位地址结构映射成磁盘的物理地址: 柱面cylinder、 轨道track、 扇区sector
6.2. 磁盘调度
6.2.1. 磁盘访问时间
- seek time :磁头找到对应柱面
- rotational latency
6.2.2. 磁盘调度算法
磁盘带宽是每秒传输的最大字节数
调度算法要做的就是最小化seek time
- FCFS
- sstf(shortest seek time first)
- 哪个近就优先访哪个
- 可能导致饿死
- scan算法
- 从一端往另一端扫,再从这边往那边扫,顺路把所有的任务处理完
- 优化:是否有必要走到一端(走到最远的那个就返回, look 算法)
- Cscan 算法
- 从一端到另一端,路上处理,到达另一端后,再返回原来那端,这个过程中不服务(scan算法的一半)
scan算法相对cscan性能更优,
- 从一端到另一端,路上处理,到达另一端后,再返回原来那端,这个过程中不服务(scan算法的一半)
- c-look
6.2.3. 选择一个调度算法
负载很重的时候 scan和c-scan算法比较好 ,性能取决于请求的数量和类型,
6.3. 格式化
6.3.1. 低级格式化
将磁盘分成几个扇区以实现读写
- 前导码(preamble):表示一段扇区的开始
- 数据(data)
- 纠错码(ecc)
6.3.2. 逻辑格式化
写入文件控制块
柱面斜入(cylinder skew,磁头在移动的时候磁盘在转,所以每个磁道上的0号扇区并不是在同一个半径上的,有一定的偏移)—为了提高I/o请求的效率
- 读入缓冲区到内存需要时间,这个过程不能读,但是磁盘还是在转,因此1号扇区和2号扇区可能隔了几个扇区(interleaving)。
6.3.3. 错误处理
对于坏的扇区,首先找到空闲扇区将其中的数据移入,然后重新排扇区号避过损坏的扇区
6.4. 硬盘上的引导分区 (mbr on Windows )
MBR(一般在磁盘起始位置不属于某个分区)
- BOOT CODE
- PARTIION TABLE
6.5. RAID (Redundancy Array of independent Disk)冗余
- RAID0 不备份
- RAID1 全部备份一个
- RAID2 4:3冗余(海明码)
- RAID3 增加一块磁盘(校验位)
7. 第十三章 i/o 系统
7.1. I/O硬件
- 很多不同的I/O设备
- I/O设备有地址,I/O指令可以控制设备,内存映射I/O
- I/O设备可以产生中断
- DMA –绕过CPU直接让I/O设备和内存之间传输数据
- 设备被告知要传输从磁盘某个位置传输数据给内存
- 设备告诉磁盘控制器这件事情
- 磁盘控制器初始化DMA控制器
- 磁盘控制器把数据传给DMA控制器,
- DMA控制器将数据传给内存
- 传完DMA产生中断告诉CPU OK了
7.2. 应用I/O接口
7.3. 内核I/O子系统
7.3.1. I/O方式
- 阻塞
- 非阻塞,一次读一点就返回然后接着读
- 异步
7.3.2. caching
7.3.3. spooling
7.3.4. device reservation
- 互斥访问
- 死锁检测
- 由系统分配
7.3.5. 错误处理
- 系统能从io错误中恢复
- io失败需要return error code
- 系统记录错误报告
7.3.6. I/O保护
- I/O指令必须是特权指令
- I/O必须通过系统调用