操作系统笔记(部分)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Fei20140908/article/details/80766424

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的概率

t i m e = ( 1 p ) ( m e m o r y a c c e s s o v e r h e a d ) + p ( p a g e f a u l t o v e r h e a d + [ s w a p o u t ] + s w a p i n + r e s t a r t o v e r h e a d )

注意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 工作集

某段时间内,进程要访问的页面集合

Δ = w o r k i n g s e t w i n d o w ,意思是考虑最近 Δ 次引用,需要为每个page额外添加几位去记录历史,有个timer隔一段时间(这个是指隔多少个引用)产生中断,将reference内容拷贝到历史记录中并将reference清零,这么做不是很精确,因为不知道到底是中间哪次发生了引用,想要提供精度就要增加历史记录,减小timer周期

W S S i 表示进程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性能更优,
  • 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必须通过系统调用

7.4. 将I/O请求 转换为硬盘操作

7.5. 流

7.6. 性能

猜你喜欢

转载自blog.csdn.net/Fei20140908/article/details/80766424