操作系统考研笔记

操作系统考研笔记:

    • 操作系统考研笔记:
        • 一、概念篇:
        • 二、技术篇:

操作系统考研笔记:

一、概念篇:
  • 操作系统的特征:

    • 并发:并发是同一时间间隔,并行是同一时刻;
    • 共享:并发和共享是操作系统最基本的两个特征
    • 虚拟:把一个物理上的实体转化为若干逻辑上的对应物,如虚拟处理器、虚拟内存、虚拟设备等;
    • 异步:以不可预知的速度向前推进;
  • 操作系统的接口:

    • 命令接口:分为联机和脱机两种;
    • 程序接口:由一组系统调用组成,用户通过在程序中使用系统调用来请求操作系统提供服务;
    • 图形接口:最终是通过调用程序接口实现的;
  • 操作系统的基本类型:

    • 批处理操作系统:
      • 单道批处理:内存中始终保持一道作业,具有顺序性的特征;
      • 多道批处理:宏观上并行,微观上串行,资源利用率和系统吞吐量大,但是没有交互能力且用户响应较长;
    • 分时操作系统:利用时间片轮转方式,支持多个用户同时与计算机进行交互,且能够及时得到响应;
    • 实时操作系统:采用高优先级抢占方式,某个动作必须在规定的时间内或者时刻完成,保证及时性和可靠性,但是资源利用率低;
  • 中断处理过程:

    • 中断隐指令(硬件):关中断 -> 保存断点(PC) -> 中断服务程序寻址;
    • 中断服务程序(软件):保存现场(PSWR,及某些通用寄存器)和屏蔽字 -> 开中断(中断嵌套) -> 执行中断服务程序 -> 关中断(中断嵌套) -> 恢复现场和屏蔽字 -> 开中断 -> 中断返回;
  • 子过程调用

    • 当主程序转去执行子程序时,首先将断点地址压入堆栈中保存,再将子程序的入口送入PC;
    • 进入子程序前,先把参数保存到参数寄存器里面,超过6个需要保存到堆栈;然后还要保存主程序现场(某些子程序需要的通用寄存器),然后再执行子程序,子程序完成后,恢复主程序现场,然后将断点地址从栈顶送回给PC;
  • 系统调用过程

    • 正在运行的进程先传递系统调用参数;
    • 由trap指令负责将用户态转化为内核态,并将返回地址压栈;
    • CPU执行相应内核态服务程序;
    • 中断返回,由内核态切换回用户态;
  • 微内核:

    • 将内核中最基本的功能保留在内核,将不需要在核心态执行的功能移到用户态,降低了内核的设计复杂性;
    • 移出内核的服务程序之间的交互通过微内核进行通信,使得维护代价降低,保证了操作系统的可靠性;
    • 但是需要频繁地在核心态和内核态之间切换,增大了系统开销;
  • 进程:

    • PCB结构:

操作系统考研笔记_第1张图片

  • 进程状态:运行态;就绪态;阻塞态;挂起态;

  • 进程控制

    • 创建原语(分配PID,申请PCB和分配资源);
    • 撤销原语(删除PCB,终止子进程,归还资源);
    • 阻塞原语(PCB插入等待队列);
    • 唤醒原语(PCB移出等待队列,插入就绪队列);
    • 进程切换:保存处理机上下文,包括程序计数器和其他寄存器;更新PCB信息,插入相应队列;调度另一个进程执行,更新其PCB;更新内存管理的数据结构;恢复处理机上下文;
    • 父子进程
      • 通过fork()调用创建子进程,调用一次,返回给子进程0,返回给父进程子进程的PID;
      • 子进程与父进程的区别有进程号、资源使用情况和计时器等
      • 在fork()之后exec()之前:两个进程用的是相同的物理空间(内存区),子进程的代码段、数据段、堆栈都是指向父进程的物理空间,也就是说,两者的虚拟空间不同,但其对应的物理空间是同一个;
      • 写时复制:当父子进程中有更改相应段的行为发生时,再为子进程相应的段分配物理空间,如果不是因为exec(),内核会给子进程的数据段、堆栈段分配相应的物理空间(至此两者有各自的进程空间,互不影响),而代码段继续共享父进程的物理空间(两者的代码完全相同);而如果是因为exec(),由于两者执行的代码不同,子进程的代码段也会分配单独的物理空间;
      • 父进程撤销后,子进程可能:① 一并被撤销;② 成为孤儿进程,并被init进程领养;
  • 进程通信

    • 共享存储:
      • 通信进程之间存在一个共享空间,在这片空间上读写实现进程间的信息交换;
    • 消息传递:
      • 进程间的数据是以格式化的消息为单位,并利用系统提供的发送消息和接收消息的原语进行数据交换;
      • 发送进程直接发送消息给接收进程,消息挂在接收进程的消息缓冲队列,等待接收进程从队列中取得消息;
    • 管道通信:(管程不是进程通信机制
      • 实际上是一个固定大小的缓冲区,容量通常为一页
      • 发送进程以字符流形式将大量数据写入管道,接收进程从管道中接收数据;
      • 管道半双工,写满之后才能且只能读,读空之后才能且只能写;
      • 管道是一个单独构成的文件系统,并且只存在于内存中;
  • 处理器调度:

    • 作业调度(从辅存中选择作业进入内存);
    • 中级调度(将暂时不能运行的进程调到外存挂起,或者将挂起的进程调入内存就绪);
    • 进程调度(按照某种策略从就绪队列中选取一个进程执行,I/O密集型进程优先级高于计算密集型
    • 进程饥饿:进程由于进程间资源分配的不公平,造成某些进程会长时间等待(如设置优先级),或者资源分配的机会公平但是互相谦让,导致全部进程都在无限等待(如双标志后检查);
  • 进程互斥同步:

    • 四大准则
      • 空闲让进:当无进程处于临界区,可允许一个请求进入临界区的进程立即进入自己的临界区;
      • 忙则等待:当已有进程进入自己的临界区,所有企图进入临界区的进程必须等待;
      • 有限等待:对要求访问临界资源的进程,应保证该进程能在有限时间内进入自己的临界区;
      • 让权等待:当进程不能进入自己的临界区,应释放CPU;
      • 前三条互斥机制必须遵守的原则;
      • 除了信号量和管程外,其他机制无法实现也不必实现让权等待;
    • 软件实现方法;硬件实现方法;信号量机制;
    • 管程机制
      • 管程的共享数据结构只能被管程内的过程访问;
      • 一个进程只有通过调用管程内的过程才能进入管程访问共享数据;
      • 每次仅允许一个进程在管程内执行某内部过程,从而实现进程互斥;
      • 条件变量仅保存一个等待队列,用于记录因该条件变量被阻塞的所有进程;
  • 线程的实现方式

    • 用户级线程:线程管理由应用程序完成,对内核透明

    • 内核级线程:线程管理由内核完成,应用程序只有一个到内核级线程的接口,内核为进程及内部每个线程维护上下文

    • 多线程模型:

      • 多对一:
        • 将多个用户级线程映射到一个内核级线程,
        • 优点是:线程管理在用户空间完成用户级线程对操作系统不可见;
        • 缺点是:当一个线程阻塞后,所有线程都会被阻塞(因为整个进程都被阻塞);
      • 一对一:每个用户级线程映射到一个内核级线程,并发能力强,但是创建线程的开销大;
      • 多对多:将n个用户级线程映射到m个内核级线程上( m ≤ n m\le n mn),兼具多对一和一对一的优点,但是管理复杂;
  • 死锁:

    • 死锁预防:(破坏4个必要条件其中之一即可)

      • 破坏互斥条件:进程对分配的资源进行排他性控制,称互斥条件;破坏互斥条件只需允许资源共享使用;问题是某些资源只能互斥使用,没办法共享,如打印机;
      • 破坏不可剥夺条件:进程获得资源在未使用外之前,不能被剥夺,称不可剥夺条件;破坏不可剥夺条件只需剥夺进程正在使用的资源即可;问题是会增加系统开销从而降低吞吐量;
      • 破坏请求保持条件:进程保持至少一个资源,提出新的资源请求的资源又被其他进程保持,称请求保持条件;破坏请求保持条件的办法是一次性申请完所需的全部资源,运行期间申请资源始终归为己有;问题是会造成资源的严重浪费和饥饿问题
      • 破坏循环等待条件:互相等待所请求资源的进程组成环,称循环等待条件;破坏循环等待条件可以使用顺序资源法,对资源编号,必须按照编号顺序请求资源,问题是编号必须稳定,且会造成资源浪费,不利于用户编程;
    • 死锁避免:在系统进行资源分配之前,先计算此次分配的安全性,若此次分配会进入不安全状态(可能会死锁),则阻塞进程;

    • 死锁检测和解除

      • 死锁定理:系统为死锁状态的充要条件为此时资源分配图是不可完全简化的;

      • 简化资源分配图可以检测系统状态是否为死锁状态:

        1)找到资源分配图中既不阻塞(系统有足够的空闲资源分配给它)又不孤立的进程Pi2)消去它所有的请求边和分配边;
        3)重复1),2),直到找不到可以消除的进程Pi4)当且仅当所有进程都是孤立点时(完全简化),系统不处于死锁状态;
        
      • 死锁解除的主要方法:

        • 资源剥夺法:挂起某些死锁进程,抢占其资源;
        • 撤销进程法:强制撤销部分死锁进程并剥夺其资源;
        • 进程回退法:根据记录的历史信息,让若干进程回退到足以避免死锁的地步;
  • 程序的链接和装入

    • 链接:
      • 静态链接:程序运行前,将各模块及所需库函数链接成一个完整的可执行程序;
      • 装入时动态链接:装入内存时,边装入边链接;
      • 运行时动态链接:运行需要该模块时,再链接;
    • 装入:
      • 绝对装入:编译时产生绝对地址,装入时按照绝对地址装入内存;只适合单道程序环境;
      • 可重定位装入:装入时一次性完成重定位(根据内存的情况对目标程序中的指令和数据的地址进行转换),生成绝对地址;作业装入时,必须一次性分配要求的全部内存空间,且整个运行期间不能移动,也不能再申请空间;
      • 动态运行时装入:重定位推迟到程序真正要执行的时候,因此装入内存的都是相对地址;需要有重定位寄存器的支持;可以将程序分配到不连续的存储区,即可部分装入代码投入运行,且运行期间还可以动态申请分配内存;
  • 内存管理方式:

    • 覆盖技术:将用户空间分为一个固定区和若干覆盖区,将经常活跃的部分放在固定区,将即将访问的部分放在覆盖区,其他部分放在外存,在需要调用其他某部分时再将其调入覆盖区,替换覆盖区原有的部分;覆盖技术主要用于同一个进程;
    • 交换技术:把处于等待状态的进程暂时挂到外存,以腾出内存空间(换出),以及把外存中已具备运行条件的挂起进程调入内存(换入);交换技术主要用于不同进程之间;
    • 连续分配管理:
      • 单一连续:内存只有一个进程;采用覆盖技术,无外部碎片但有内部碎片,存储器利用率低;
      • 固定分区:分区大小可以相等也可以不等,无外部碎片但会产生内部碎片;存储器利用率低,且无法运行大于最大分区的程序;
      • 动态分区:进程装入时动态建立分区;产生外部碎片,最后导致存储器利用率降低;采用紧凑技术可以缓解外部碎片的问题;
    • 非连续分配管理:
      • 基本分页:
        • 主存空间分为大小相等且固定的块,以块为单位申请主存中的块空间,会产生页内碎片,无外部碎片;
        • 页面大小
          • 页面大小要适中;
          • 页面过小,页面数过多,页表过大,增加内存占用,降低地址转换速度;
          • 页面过大,页内碎片过多,降低内存利用率;
        • 逻辑地址:由页号+页内偏移量组成(一维地址);
        • 页表:每个进程建立一个页表,按照逻辑地址的页号作为索引,查找该页面在内存中的物理块号;页表通常置于内存中,部分置于快表中;
        • 页表基址寄存器:存储页表的起始地址和页表长度;
        • 两级分页:为页表建立一个索引表,即页目录表;每个进程有一个页目录表(一级页表),根据页(目录)表基址寄存器,找到页目录项,即二级页表的基址;再根据二级页号在页表中找到页表项,进而得到页框号;
        • 抖动现象:频繁地调页换页,以至于大部分时间都置于页面置换上,造成系统性能下降,此时需要撤销部分进程
      • 基本分段:
        • 按照用户进程的自然段划分逻辑空间,如代码段、数据段、堆栈段;
        • 逻辑地址:由段号+段内偏移量组成(二维地址);
        • 段表:每个进程建立一个段表,按照逻辑地址的段号,查找该地址所处的段的段长和段首地址;
        • 段表基址寄存器:存储段表首地址和段表长度;
        • 段的共享
          • 两个作业的段表中相应表项指向被共享的段的同一个物理副本,当一个作业读取共享段时,必须防止另一个作业修改其中的数据;
        • 段的保护
          • 存取控制保护:除了存入、读取的控制保护之外,还需要考虑数据共享的保护:在段表中设立相应的共享位用于判断该段是否正被某个进程调用;
          • 地址越界保护:段号不能超过段表长度,段内偏移不能超过该段长度;
      • 段页式:
        • 地址空间分成若干逻辑段,段内再分成若干页;
        • 逻辑地址:由段号+页号+页内偏移量组成;
        • 段表/页表:每个进程建立一个段表,每个分段建立一个页表;段表项中存放段号所对应段的页表长度和页表基址;
  • 虚拟内存管理:

    • 请求分页:
      • 在基本分页的基础上,为支持虚拟存储器功能而增加请求调页功能和页面置换功能;
      • 页表结构:页号 | 页框号 | 有效位P | 访问字段A | 修改位M | 外存地址;
      • 缺页中断机制:检索快表和页表都不命中后,产生缺页中断,将所缺页调入内存(若无空闲页则置换),并更新页表和快表,再次进行地址转换;
      • 工作集:
        • 某段时间t内进程要访问的页面集合,若窗口大小为k,则窗口中包含最近k次要访问的页面(可重复包含相同页面);
        • 落入工作集的页面需要调入驻留集,反之可以从驻留集换出;
      • 调入页面的时机:
        • 预调页策略:将预计在不久之后便会访问的页面预先调入,成功率为50%,主要用于进程的首次调入
        • 请求调页策略:当访问页面不在内存时提出请求,调入页面;
      • 调入页面的位置:
        • 对换区(曾经运行过又被换出,或者可能会被修改的页面);
        • 文件区(未运行过或者不会被修改的页面);
  • 文件系统层次结构

    • 用户调用接口:文件系统提供给用户的文件及目录操作,如新建、打开、读写、删除等;
    • 文件目录系统:管理文件目录,包括活跃文件目录表、用户进程打开文件表;
    • 存取控制验证模块:将用户的访问要求同FCB的访问控制权限进行比较,以确保访问合法性;
    • 逻辑文件系统与文件信息缓冲区:根据文件的逻辑结构将用户要读写的逻辑记录转换成文件逻辑结构内的相应块号;逻辑记录是对文件进行存取操作的基本单位
    • 物理文件系统:将逻辑记录所在的相对块号转换成实际的物理地址;
    • 辅助分配模块:管理辅存空间,辅助分配辅存空闲空间和回收辅存空间;
    • 设备管理程序模块:分配设备读写缓冲区、磁盘调度、启动设备、处理设备中断;
  • 文件基本操作:

    • 创建文件:① 在文件系统中找到分配空间;② 在目录中创建条目,分配FCB;
    • 打开文件:首次打开文件,使用open系统调用,参数包括文件路径名和文件名称,将指明文件的FCB从外存复制到内存打开文件表中的一个表项,并将该表项的索引返回给用户,用此索引指针完成所有I/O操作再也不需要文件名,省去了搜索环节;
    • 读/写文件:① 执行read/write系统调用,指明文件描述符、缓冲区首址、传送字节数;② 系统搜索目录查找文件位置;③ 为文件分配一个读/写指针;
    • 文件重定位:按照条件搜索目录;
    • 删除文件:① 删除目录项;② 回收文件存储空间;
    • 截断文件:将文件内容删除但保持其他属性不变;
  • 文件打开表

    • 操作系统维护一个包含所有打开文件信息的表;
    • 每个打开文件的表项都记录:文件指针、文件打开计数、文件磁盘位置、访问权限;
    • 当用户需要一个文件操作时可以通过该表的一个索引指定文件(不需要文件名,只需要索引);
    • 系统打开文件表为每个打开的文件维护一个文件打开计数器,以记录多少进程打开了该文件;
    • 当某个文件的打开计数器为零时,系统回收分配给该文件的内存空间,若文件被修改过,还要回写外存,并将系统文件打开表上对应的条目删除,最后释放文件的FCB
    • 当一个进程打开文件时,系统打开文件表会为打开的文件增加相应的条目,当另一个进程再打开该文件时,只是在其进程打开表中增加一个条目,指向系统文件打开表的相应条目;
  • 文件控制块:

    • 创建一个新文件,系统分配一个FCB并存放在文件目录中,成为目录项;FCB的集合就是文件目录;
    • FCB包括文件的:
      • 基本信息(文件名、物理位置、逻辑结构等);
      • 存取控制信息(存取权限);
      • 使用信息(建立时间、修改时间)等;
    • 索引结点:
      • 每个文件名对应一个唯一的磁盘索引节点,存放文件描述信息;
      • 文件目录中每个目录项仅由文件名和指向该文件所对应索引结点的指针构成;
      • 主要包括:文件类型、文件存取权限、文件长度和文件物理地址;
      • 文件物理地址:共13个地址项,即iaddr(0)~iaddr(12),前10个为直接地址,指向数据文件所对应的盘块号,第11个是1级间接索引,第12个是2级间接索引,第13个是3级间接索引;
  • 文件共享

    • 硬链接:
      • 在索引结点中保存链接计数,用于表示链接到本索引结点上的用户目录项的数目,即多少个用户在共享该文件,共享同一个索引结点;
      • 创建硬链接时,引用计数加1
      • 引用计数为0时才能删除该文件,否则只能删除不使用该文件的用户的目录中该文件对应的目录项,并且将引用计数减1;
      • 每个进程维护自己的文件描述符,指向各自的用户打开文件表的一项,共享同一文件的两个进程的读写指针一般不同;
    • 软链接:
      • 使用符号链,为了让用户B共享A创建的文件F,系统创建一个LINK类型的文件,也取名为F并存入B的用户目录中;
      • 在LINK文件中只包含指向被链接文件F的路径名
      • 只有文件拥有者才拥有指向其索引结点的指针,共享者只拥有路径;
      • 共享者读共享文件只能根据路径名逐级查找目录;
      • 创建软链接时,LINK文件的链接计数直接复制,并且不随原文件链接计数的改变而改变,当原文件被删除时,也不需要删除LINK文件,当以后通过LINK文件访问失败时,再删除LINK文件
  • 文件保护:口令保护;加密保护;访问控制;

  • 文件磁盘块分配方式

    • 连续分配:(需要访问磁盘1次)
      • 每个文件占用磁盘上一组连续的块;
    • 链接分配:(需要访问磁盘n次)
      • 隐式链接:除最后一个盘块外,链接指针放在每个物理块的末尾;缺点是只能通过指针顺序访问;
      • 显式链接:将链接指针显式地存放在内存的唯一一张文件分配表(在挂载文件系统时读入内存)中,每个表项包括本盘块号和下一块的盘块号,使用特殊数字**-1表示文件最后一块**,-2表示空闲块;此时每个文件目录中只需包含其起始盘块号即可;
    • 索引分配:(m级索引需要访问磁盘m+1次)
      • 每个文件的所有盘块号组成一个或多个(多级或者混合)索引块,索引块的第i个条目指向第i个文件块(或者下一级索引块);
  • 文件空闲块管理:

    • 空闲表法:系统为空闲区建立一个空闲盘块表,每个表项记录一个空闲区的第一个盘块号和空闲盘块数;
    • 空闲链表法:将所有的空闲盘区/盘块拉成一条空闲链;
    • 位示图法:使用一个二进制位矩阵描述所有的盘块,一个位对应一个盘块的空闲情况;
    • 成组链接法:使用一个空闲扇区作为一个空闲区组表,顺序保存n个空闲区的地址,最后一个地址是链接指针,指向下一个空闲区组表;系统需要保存第一个空闲区组表的指针;
  • 磁盘管理

    • 磁盘初始化:
      • 低级格式化(物理分区):
        • 将磁盘分成多个柱面组成的分区,每个分区作为一个单独的磁盘,如C盘;
        • 将分区又分成若干扇区,为磁盘的每个扇区采用特别的数据结构,包括校验码
      • 逻辑格式化:
        • 创建文件系统,将初始文件系统数据结构存储到磁盘上;
        • 数据结构包括:存储空闲/已分配磁盘块信息的数据结构,以及初始为空的目录
    • 引导块:启动时的初始化程序,存储在ROM上,引导向磁盘固定位置的启动块,初始化CPU、寄存器、设备控制器、内存,接着启动操作系统
    • 超级块:主要存放了该物理磁盘中文件系统结构的相关信息,并且对各个部分的大小进行说明,在系统启动时读入内存
    • 自举程序:
      • CPU的程序计数器指向内存ROM中自举程序第一条指令所对应的位置,当计算机被加电时,CPU开始读取并执行自举程序;
      • 而这个自举程序只有一个任务:就是将操作系统(不是全部,只是需要启动计算机的那部分程序)装入RAM中
      • 装入完成后,CPU的程序计数器就被设置为RAM中操作系统的第一条指令所对应的位置,接下来CPU将开始执行操作系统的指令;
    • 磁臂黏着:反复地请求对一个磁道进行了I/O请求,出现磁臂停留在某个位置不动的情况(除了FCFS外,其他磁盘调度算法都有可能导致磁臂黏着,注:磁臂黏着不同于饥饿,SCAN/C-SCAN算法可以避免饥饿);
  • 磁盘高速缓存:

    • 第一种:在内存中开辟一个单独的存储空间作为磁盘高速缓存;
    • 第二种:把未利用的内存空间作为一个缓冲池,供请求分页系统和磁盘I/O共享;
  • 缓冲区技术:

    • 单缓冲;双缓冲;循环缓冲(多个大小相等的缓冲区组成循环链表);
    • 缓冲池
      • 三个队列:空缓冲区队列;输入缓冲区队列;输出缓冲区队列;
      • 四种缓冲区:收容输入数据的缓冲区、提取输入数据的缓冲区;收容输出数据的缓冲区、提取输出数据的缓冲区;
      • 三种进程:输入进程、计算进程和输出进程;
      • 工作方式:
        • 输入进程需要输入数据时,从空缓冲区队列摘取一个空缓冲区作为收容输入数据的缓冲区,将数据输入装满后,挂到输入缓冲区队列;
        • 计算进程需要提取输入数据时,从输入缓冲区队列摘取一个提取输入数据的缓冲区,将缓冲区读空后,归还给空缓冲区队列;
        • 计算进程需要输出数据时,空缓冲区队列摘取一个空缓冲区作为收容输出数据的缓冲区,将数据输出后装满后,挂到输出缓冲区队列;
        • 输出进程需要提取输出数据时,从输出缓冲区队列摘取一个提取输出数据的缓冲区,将缓冲区读空后,归还给空缓冲区队列;
    • 缓冲区非空时不能输入数据,只能输出数据;缓冲区空时只能输入数据,等到缓冲区满后才能输出数据;
  • I/O控制方式:程序直接控制方式;中断驱动方式;DMA方式;通道方式;

  • I/O子系统的层次结构

    • 用户层I/O软件:实现与用户交互的接口,调用I/O库函数;
    • 设备独立性软件:
      • 执行所有设备的公有操作,包括设备分配回收、逻辑设备名映射到物理设备名、设备保护、逻辑块
      • 向用户层提供统一接口,如读写操作都是read/write命令;
      • 独立于具体的物理设备,使用逻辑设备名请求某类设备;
      • 设备独立性的优点:方便用户变成;是程序运行不受具体机器环境的限制;便于程序移植;
    • 设备驱动程序:
      • 与硬件直接相关,实现系统对具体设备的操作命令,并驱动设备工作;
      • 每个设备配置一个设备驱动程序,以进程形式存在
      • 设备驱动程序的功能
        • 接受上层软件的抽象I/O要求,如read/write命令,并转化为具体要求,发送给设备控制器
        • 检查用户IO的合法性、设备状态、传递参数
        • 发出IO操作命令,启动IO设备,完成IO操作,但不能独立控制IO操作;
        • 响应通道发来的中断请求,调用中断处理程序;
        • 构造通道程序;
      • 如磁盘驱动程序需要计算磁盘的柱面号、磁头号、扇区号等,因为这个因具体设备而异;
    • 中断处理程序;
    • 硬件设备:
      • 一般包括机械部分(设备本身)和电子部件(设备控制器);
      • 设备控制器的功能:
        • 接受和识别CPU或通道发来的命令,如磁盘控制器能接收读、写、查找等命令;
        • 实现数据交换,包括与设备之间,和通过数据总线或通道与主存之间;
        • 记录设备的状态信息,供CPU处理使用;
        • 设备地址识别;
  • 设备分配:

    • 设备按使用方式分类:
      • 独占式使用设备:如打印机;
      • 分时式共享使用设备:如磁盘;
      • 以SPOOLing方式使用外部设备;
    • 设备分配的数据结构
      • 系统设备表(SDT):记录已经连接系统中的所有物理设备的情况,每个物理设备占一个表目,表目中有一个指向该物理设备的DCT的指针;
      • 设备控制表(DCT):
        • 一个设备控制表表征一个设备,其中的表项描述设备的各个属性;
        • 设备的类型、标识符、状态;
        • 指向控制器控制表的指针(设备控制表和控制器控制表一一对应);
      • 控制器控制表(COCT):
        • 控制器:负责解析上层传达的命令并控制机械部分运作;
        • 一个控制器控制表表征一个控制器,其中的表项描述控制器的各个属性;
        • 控制器的标识符、状态;
        • 指向通道控制表的指针;
      • 通道控制表(CHCT):
        • 一个通道控制表表征一个通道,其中的表项描述通道的各个属性;
        • 通道标识符、状态;
        • 指向所连接的多个设备的控制器表的首地址(一个通道对应多个设备控制器);
    • 设备分配策略:
      • 分配方式:静态分配(一次性把设备分配给相应作业,直到作业结束,适合于独占设备);动态分配(进程执行过程中根据需要进行分配,适合于共享设备);
      • 分配算法:先请求先分配;优先级高者分配;
    • 设备分配的安全性:
      • 安全分配:进程发出I/O请求后进入阻塞态,直到I/O结束才被唤醒;
      • 不安全分配:进程发出I/O请求后继续运行,仅当请求设备被占用时才进入阻塞态;进程推进迅速,但是可能产生死锁;
    • 逻辑设备名到物理设备名的映射
      • 目的:实现I/O重定向,引入设备独立性;
      • 实现方式:系统设置一张逻辑设备表LUT,表项包括逻辑设备名,对应的物理设备名以及其设备驱动程序的入口地址;进程使用逻辑设备名请求I/O时,系统为其分配相应物理设备并建立一个LUT表项,以后进程再访问时,通过查找LUT找到对应的物理设备和驱动程序;
  • SPOOLing技术

    • 利用内存的缓冲区,将低速设备上的数据传送到高速磁盘或者相反;
    • 特点:将独占设备改造成共享设备,实现了虚拟设备功能,提高了I/O速度(如共享打印机);
    • 输入井/输出井:磁盘中用于收容I/O设备输入 / 用户程序输出的数据的区域;
    • 输入缓冲区/输出缓冲区:内存中暂存输入设备送来的数据,以后传送到输入井中 / 输出井送来的数据,以后传送到输出设备;
    • 输入进程/输出进程:将用户需要的数据从输入设备通过输入缓冲区送到输入井,当CPU需要输入数据时,直接将数据从输入井读入内存 / 把用户要输出的数据先从内存写入输出井,等到输出设备空闲时再将输出井的数据经过输出缓冲区送到输出设备;

二、技术篇:
  • 各种处理器调度算法的执行过程(甘特图)以及周转时间等指标的相关计算;

    • 周转时间 = 作业完成时间 - 作业提交时间;平均周转时间 = 各个作业周转时间的平均值;
    • 带权周转时间 = 周转时间 / 实际运行时间;平均带权周转时间 = 各个带权周转时间的平均值;
    • 等待时间 = 甘特图中某条横轴虚线的时间和;平均等待时间 = 各个作业等待时间的平均值;
    • CPU/设备利用率 = 各个作业使用CPU/设备的总时间 / 所有作业全部完成时间;
    • 处理器调度算法
      • 先来先服务:顺序队列,仅支持非抢占式;
      • 短作业优先:平均等待时间、平均周转时间最短,但是长作业会出现饥饿现象;支持抢占式和非抢占式,默认非抢占式;
      • 优先级调度:一般计算密集型作业的优先级低于I/O密集型作业的优先级;静态优先级会造成饥饿现象;支持抢占式和非抢占式,默认非抢占式;
      • 高响应比调度:响应比 = (等待时间+要求服务时间) / 要求服务时间;支持抢占式和非抢占式,默认非抢占式;
      • 时间片轮转:时间片过长退化为FCFS,时间片过短则频繁进程切换,开销过大;仅支持抢占式;
      • 多级反馈队列:
        • 新进程先进入第1队列,若在第1队列的时间片内不能运行完,则加入第2队列,依次类推;
        • 仅当第1队列为空时,才调度第2队列,依次类推;
        • 调度低优先级队列时,若高优先级队列此时加入进程,则抢占执行高优先级队列的进程;
        • 优先级越高的队列,执行时间片越小;
  • 利用软件实现方法实现进程互斥

    • 单标志法:

      process Pi{
      	while(turn!=i);
      	critical section;
      	turn = j;
      	remainder section;
      }
      
      • 设置公用整型变量 t u r n turn turn,若 t u r n turn turn i i i,允许 P i P_i Pi进程进入临界区;
      • 确保每次只允许一个进程进入临界区,但是进程必须交替使用临界区;
      • 当进程 P j P_j Pj不再进入临界区,而进程 P i P_i Pi本次使用临界区后将 t u r n turn turn设置成 j j j P i P_i Pi将无法再次进入临界区,违背空闲让进,造成资源浪费;
    • 双标志先检查法:

      process Pi{
      	while(flag[j]);
      	flag[i] = TRUE;
      	critical section;
      	flag[i] = FALSE;
      	remainder section;
      }
      
      • 设置数组flag,若 f l a g [ j ] flag[j] flag[j] t r u e true true,则说明 P i P_i Pi进程正在使用临界区,不能进入;否则则设置自己的 f l a g [ i ] flag[i] flag[i] t r u e true true,然后进入;
      • 不用交替进入,可连续使用,但是 P i P_i Pi P j P_j Pj检查和修改不能一次进行,可能出现都检查通过再同时修改自己的flag,然后同时进入临界区的情况,违背忙则等待
    • 双标志后检查法:

      process Pi{
      	flag[i] = TRUE;
      	while(flag[j]);
      	critical section;
      	flag[i] = FALSE;
      	remainder section;
      }
      
      • 设置数组flag,首先设置自己的 f l a g [ i ] flag[i] flag[i] t r u e true true,表示自己需要使用临界区,然后检查对方 f l a g flag flag,若为 t r u e true true,则等待;
      • 当双方依次设置自己的 f l a g flag flag t r u e true true后,检测到对方也为 t r u e true true后互相谦让,双方都无法进入临界区违背有限等待导致饥饿现象
    • Peterson算法

      process Pi{
      	flag[i] = TRUE; 
      	turn = j;
      	while(flag[j]&&turn==j);
      	critical section;
      	flag[i] = FALSE;
      	remainder section;
      }
      
      • 在双标志后检查法的基础上,为了避免饥饿现象,结合单标志法,设置 t u r n turn turn变量,在置 f l a g flag flag t r u e true true之后设置 t u r n turn turn j j j,表示自己想进,但是先谦让一次且仅一次,让对方先进;彼此都是这样,那样在后谦让的进程谦让后,先谦让的进程心安理得地进入临界区
      • 既利用了 f l a g flag flag后检查解决了临界资源的互斥访问,又利用 t u r n turn turn解决了饥饿问题;
      • 满足了空闲让进、忙则等待、有限等待,但是无法做到让权等待
  • 利用硬件实现方法实现进程互斥

    • 硬件方法:也称低级方法,或称元方法;

    • 硬件方法的优点:适合于任意数目的进程,支持进程内多个临界区,简单易用;

    • 硬件方法的缺点:不能实现让权等待,由于随机选择等待进程,有的倒霉进程因此可能导致饥饿现象

    • 中断屏蔽方法:

      • 当进程使用临界区时,直接关中断,因为CPU只在发生中断时才引起进程切换,这样能保证当前运行进程让临界区代码顺利执行完,再开中断
      • 缺点是限制了CPU交替执行程序的能力,且将关中断权力交给用户不妥,若没有开中断则系统终止;
    • T e s t A n d S e t TestAndSet TestAndSet指令法:

      bool TestandSet(bool &lock){
      	bool old = lock;
      	lock = true;
      	return old;
      }
      
      process Pi{
      	while(TestandSet(&lock));
      	critical section;
      	lock = false;
      	remainder section;	
      }
      
      • 为临界区设置一个共享布尔变量 l o c k lock lock,为 t r u e true true表示临界区正在被占用,初值为 f a l s e false false
      • T e s t a n d S e t TestandSet TestandSet为原子操作指令,进程 P i P_i Pi访问临界区之前检查 l o c k lock lock,若已经被占用则循环等待,若没有被占用则为自己所用,进入临界区;
    • S w a p Swap Swap指令法:

      Swap(bool &a,bool &b){
      	bool tmp = a;
      	a = b;
      	b = tmp;
      }
      
      process Pi{	
      	key = true;	
      	while(key!=false)		
      		Swap(&lock,&key);	
      	critical section;	
      	lock = false;	
      	remainder section;
      }
      
      • 同样设置一个共享布尔变量 l o c k lock lock,初值为 f a l s e false false,然后设置一个局部变量 k e y key key,用于和 l o c k lock lock交换信息;
      • S w a p Swap Swap同样为原子操作指令,在进入临界区之前,进程 P i P_i Pi先用 S w a p Swap Swap交换 l o c k lock lock k e y key key,然后检查 k e y key key的状态,即检查 l o c k lock lock的旧值,若为 t r u e true true说明其他进程正在使用,则重复交换和检查;
  • 利用信号量解决进程互斥同步

    • P P P操作 / w a i t wait wait操作:

      void wait(semaphore S){	
      	S.value--;	
      	if(S.value < 0){		
      		add this process to S.L;		
      		block(S.L);	
      	}
      }
      
      • 首先试图从 S S S中分配一个资源,但是当 S . v a l u e < 0 S.value<0 S.value<0,说明已经没有资源,分配失败,进程调用 b l o c k block block原语,自我阻塞并插入 S S S的等待队列 S . L S.L S.L
      • 遵循了让权等待原则
    • V V V操作 / s i g n a l signal signal操作:

      void signal(semaphore S){	
      	S.value++;	
      	if(S.value <= 0){		
      		remove a process P from S.L;		
      		wakeup(P);	
      	}
      }
      
      • 首先试图归还一个资源给 S S S,但是当 S . v a l u e ≤ 0 S.value\le0 S.value0,说明有等待该资源的进程被阻塞,因此从等待队列 S . L S.L S.L中调用 w a k e u p wakeup wakeup原语,唤醒队首进程 P P P
    • 进程同步:

      • 声明S = 0的同步变量,假设语句x必须先于y执行,则P(S)置于y之前、V(S)置于x之后,检查y执行之前x是否执行;
      • 声明S = N的同步变量,表示可用资源的数量,并在某个行为要使用该资源前P(S),在某个行为提供该资源后V(S);
    • 进程互斥:

      • 声明S = 1的互斥变量,将临界区置于P(S)和V(S)之间,可以使一次只有一个进程进入临界区;
    • 前驱关系:

      • 画出各个进程的AOV网(进程为结点,边由依赖变量决定);
      • 然后为每一条边设置一个初值为0的信号量,对于每一个进程,如果它有N个前驱边,则在程序开始前P各个前驱边对应的信号量;如果它有M个后驱边,则在程序结束后V各个后驱边对应的信号量;
  • 利用管程解决进程同步/进程互斥等问题:

    monitor Demo{
    	share S;
    	condition c;
    
    	init_code(){
    		S = n;
    	}
    
    	take_away(){
    		if(S<=0)
    			c.wait();
    		several taking operations on S;
    		S--;
    	}
    
    	give_back(){
    		several returning operations on S;
    		S++;
    		if(process is waiting)
    			c.signal();
    	}
    }	
    
    • 进程同步:使用条件变量,每个条件变量保存一个等待队列,可以对该条件变量执行 w a i t wait wait s i g n a l signal signal操作
    • 进程互斥:每次允许一个进程进入管程, t a k e a w a y takeaway takeaway / g i v e b a c k giveback giveback共享数据结构 S S S
  • 经典进程同步互斥问题

    • 生产者-消费者问题

      • 问题描述:一组生产者进程和一组消费者进程共享一个初始为空、大小为n的缓冲区,只有缓冲区不满时生产者才能把消息放入缓冲区,否则等待;只有缓冲区不空时,消费者才能从中取出消息,否则等待;缓冲区是临界区,一次只允许一个生产者或者一个消费者操作;

      • 并发代码:

        semaphore mutex = 1; // 临界区互斥信号量
        semaphore empty = n; // 缓冲区空闲资源,生产者消耗这个资源,消费者归还这个资源
        semaphore full = 0; // 缓冲区满资源,生产者归还这个资源,消费者消耗这个资源
        
        // 生产者进程
        process producer(){
          while(1){
            produce(); // 生产数据
            P(empty); // 获取空闲资源
            P(mutex); put(); V(mutex); // 互斥访问,把数据放入缓冲区
            V(full); // 归还满资源
          }
        }
        
        // 消费者进程
        process consumer(){
          while(1){
            P(full); // 获取满资源
            P(mutex); get(); V(mutex); // 互斥访问,把数据从缓冲区取出
            V(empty); // 归还空闲资源
            consume(); // 消费数据
          }
        }
        
    • 读者-写者问题

      • 问题描述:一组读者进程和一组写者进程共享一个文件,读者和读者可以同时访问,写者只有在没有读者读且没有其他写者写时,才能访问文件;

      • 并发代码:

        • 读者优先版:只要有一个读者访问文件,其他读者可以纷至沓来,写者可能饥饿;

          semaphore rw = 1; // 互斥读者写者、写者写者访问文件
          int count = 0; // 记录正在访问文件的读者数量
          semaphore cmutex = 1; // 互斥对count值的操作
          
          // 写者进程
          process writer(){
            while(1){
              P(rw); // 获取文件访问权力,有其他写者或者读者先进入则阻塞自己,否则阻塞其他写者或读者
              write(); // 写文件
              V(rw); // 归还文件访问权力
            }
          }
          
          // 读者进程
          process reader(){
            while(1){
              P(cmutex); // 互斥操作count 
              if(count == 0) // 若自己是第一个进入的读者,则自任“管理员”
                	// 若之前有写者,阻塞等待;当写者退出后,占据文件访问权力,
              		// 后续写者会阻塞,读者不会,即只允许且无限允许读者进入,直到所有读者退出
                	P(rw); 
              count++; // 读之前,读者数量+1
              V(cmutex);
              
              read(); // 读文件
              
              P(cmutex); // 互斥操作count 
              count++; // 读完后,读者数量-1
              if(count == 0) // 若自己是最后一个退出的读者,则自任“管理员”
                	// 归还文件访问权力,允许后续写者或读者进入
                	V(rw); 
              V(cmutex);
            }
          }
          
        • 读写公平版:写者要求访问文件时,阻塞后续的读者或写者到同一个队列,直到该写者退出,然后按队列先后次序接纳读者或写者;

          semaphore rw = 1; // 互斥读者写者、写者写者访问文件
          int count = 0; // 记录正在访问文件的读者数量
          semaphore cmutex = 1; // 互斥对count值的操作
          
          semaphore q = 1; // 读者写者共同排队的阻塞队列
          
          // 写者进程
          process writer(){
            while(1){
              P(q); // 入队,看有没有其他读者或者写者排在前面,若有则等待
              
              P(rw); // 获取文件访问权力,有其他写者或者读者先进入则阻塞自己,否则阻塞其他写者或读者
              write(); // 写文件
              V(rw); // 归还文件访问权力
              
              V(q); // 出队,后续读者写者按顺序访问
            }
          }
          
          // 读者进程
          process reader(){
            while(1){
              // 入队,看有没有其他读者或者写者排在前面
              // 若是写者则等其写完再进,若是读者则等其检查”管理员“身份之后放自己一起进入
              P(q); 
              
              P(cmutex); // 互斥操作count 
              if(count == 0) // 若自己是第一个进入的读者,则自任“管理员”
                	// 若之前有写者,阻塞等待;当写者退出后,占据文件访问权力,
              		// 后续写者会阻塞在rw队列,等待已进入的读者全退出,读者不会,可以同时进入
                	// 但是若后续写者w在后续若干读者之前,这些读者会阻塞在q队列,等待w写完
                	P(rw); 
              count++; // 读之前,读者数量+1
              V(cmutex);
              
              V(q); // 出队,后续被阻塞在q队列的第一个写者之前的读者可以一起进入
              
              read(); // 读文件
              
              P(cmutex); // 互斥操作count 
              count++; // 读完后,读者数量-1
              if(count == 0) // 若自己是最后一个退出的读者,则自任“管理员”
                	// 归还文件访问权力,允许后续写者或读者进入
                	V(rw); 
              V(cmutex);
            }
          }
          
        • 写者优先版:写者要求访问文件时,分别阻塞后续的读者写者到不同队列,写者可以依次排队进入,但只有所有写者访问完后才打开读者阻塞队列,让读者依次进入,直到又一个写者到来,再次关闭读者阻塞队列;

          int wcount = 0; // 记录正在排队或者访问文件的写者数量
          int rcount = 0; // 记录正在访问文件的读者数量
          semaphore wmutex = 1; // 互斥对wcount的操作
          semaphore rmutex = 1; // 互斥对rcount的操作
          semaphore rw = 1; // 互斥读者写者、写者写者访问文件
          semaphore rq = 1; // 读者阻塞队列
          
          // 写者进程
          process writer(){
            while(1){
              P(wmutex); // 互斥操作wcount
              if(wcount == 0) // 若前面没有其他写者在排队或已进入,则自任“管理员”
                	// 关闭读者阻塞队列,此时后续读者只能排队,直到没有写者在访问或者排队才能开放队列
                	P(rq);
              wcount++; // 写者排队或进入数量+1
              V(wmutex);
              
              P(rw); // 获取文件访问权力,有其他写者或者读者先进入则阻塞自己,否则阻塞其他写者或读者
              write(); // 写文件
              V(rw); // 归还文件访问权力
              
              P(wmutex); // 互斥操作wcount
              wcount--; // 写者排队或进入数量-1
              if(wcount == 0) // 若自己退出后暂无其他写者排队,则自任“管理员”
                	// 开放读者阻塞队列,此时后续读者可以排队进入,直到下一个写者来任“管理员”关闭队列
                	V(rq);
              V(wmutex);
            }
          }
          
          // 读者进程
          process reader(){
            while(1){
              P(rq); // 入队,看看有没有其他读者排在前面,或者此通道已经被前面的写者关闭
              
              // 此时确定没有写者在自己之前排队或者访问文件
              P(rmutex); // 互斥操作rcount 
              if(rcount == 0) // 若自己是第一个进入的读者,则自任“管理员”
                	// 后续连续到达的读者可以同时进入,直到下一个写者w到达
              		// w会阻塞在rw队列,等待已进入的读者全退出才能进入,同时关闭rq队列
                	// w之后到来的读者只能等再次没有写者在排队或访问文件后,才能进入
                	P(rw); 
              rcount++; // 读之前,读者数量+1
              V(rmutex);
              
              V(rq); // 出队,让下一个写者w到来前的后续读者一起进入
              
              read(); // 读文件
              
              P(rmutex); // 互斥操作rcount 
              rcount--; // 读之后,读者数量-1
              if(rcount == 0) // 若自己是最后一个离开的读者,则自任“管理员”
                	// 归还文件访问权力,允许后续写者或读者进入
                	V(rw); 
              V(rmutex);
            }
          }
          
    • 哲学家就餐问题

      • 问题描述:一张圆桌边围坐着5个哲学家,每两个哲学家之间有一根筷子,圆桌中间是米饭,每个哲学家每次思考之后需要进餐,此时需要使用左右两根筷子,就餐后放下该两根筷子继续思考;

      • 并发代码:

        • 方案①:至多允许4个哲学家同时入座;

           semaphore chopsticks[5] = {1,1,1,1,1}; // 5根筷子资源
          semaphore seat = 4; // 4个座位资源,至多允许4个哲学家入座
          
          // 哲学家i进程
          process Pi(){
            while(1){
              think(); // 思考,此时相邻哲学家才可能进餐
              
              P(seat); // 获取座位资源
              
              P(chopsticks[i]); // 拿左边筷子
              P(chopsticks[(i+1)%5]); // 拿右边筷子
              
              eat(); // 进餐
              
              V(chopsticks[i]); // 放下左边筷子
              V(chopsticks[(i+1)%5]); // 放下右边筷子
              
              V(seat); // 归还座位资源
            }
          }
          
        • 方案②:仅当一个哲学家左右筷子都可用且没有其他哲学家也准备拿筷子时,才允许拿筷子;

          semaphore chopsticks[5] = {1,1,1,1,1}; // 5根筷子资源
          semaphore mutex = 1; // 互斥哲学家拿筷子权力
          
          // 哲学家i进程
          process Pi(){
            while(1){
              think(); // 思考,此时相邻哲学家才可能进餐
              
              P(mutex); // 获取拿筷子权力
              // 拿左边筷子,若已经被拿,说明左邻哲学家正在进餐,此时其余4个哲学家或在思考或被阻塞
              P(chopsticks[i]);
              // 拿右边筷子,同上理
              P(chopsticks[(i+1)%5]); 
              V(mutex); // 归还拿筷子权力,此时哲学家i同时得到了两根筷子
              
              eat(); // 进餐
              
              V(chopsticks[i]); // 放下左边筷子
              V(chopsticks[(i+1)%5]); // 放下右边筷子
            }
          }
          
        • 方案③:奇数号哲学家先拿左筷子后拿右筷子,偶数号哲学家相反;

          semaphore chopsticks[5] = {1,1,1,1,1}; // 5根筷子资源
          
          // 哲学家i进程
          process Pi(){
            while(1){
              think(); // 思考,此时相邻哲学家才可能进餐
              
              if(i%2){ // 奇数号哲学家
                // 先拿左边筷子
              	P(chopsticks[i]);
              	// 再拿右边筷子
              	P(chopsticks[(i+1)%5]); 
              }
              else{ // 偶数号哲学家
                // 先拿右边筷子
              	P(chopsticks[(i+1)%5]); 
              	// 再拿左边筷子
              	P(chopsticks[i]);
              }
              
              eat(); // 进餐
              
              V(chopsticks[i]); // 放下左边筷子
              V(chopsticks[(i+1)%5]); // 放下右边筷子
            }
          }
          
    • 吸烟者问题

      • 问题描述:有三个抽烟者和一个供应者,总共三种材料:烟草、纸和胶水,每个抽烟者各自无限拥有其中一种资源,但没有任何另外两种资源,供应者无限随机生产其中两种资源并放在桌子上,并等待缺乏该两种资源的抽烟者取走并抽完烟,再开始生产下一轮;

      • 并发代码:

          semaphore material[3] = {0,0,0}; // 三种材料的资源量,初始均为0
          semaphore finish = 0; // 取走材料并抽完烟资源,初始为0
          
          // 供应者进程
          process offer(){
            while(1){
              rand = rand()%3; // 随机选择两种材料生产
              if(rand == 0){
                offer(1); offer(2); // 生产1,2号材料
              	V(material[0]); // 允许需要1,2号材料的抽烟者前来抽烟
              }
              else if(rand == 1){
                offer(0); offer(2); // 生产0,2号材料
              	V(material[1]); // 允许需要0,2号材料的抽烟者前来抽烟
              }
              else if(rand == 2){
                offer(0); offer(1); // 生产0,1号材料
              	V(material[2]); // 允许需要0,1号材料的抽烟者前来抽烟
              }
              
              P(finish); // 等待相应抽烟者取走材料并抽完烟
            }
          }
          
          // 抽烟者i进程
          process smoker_i(){
            while(1){
              P(material[i]); // 判断是否刚好有需要的两种材料
              get(); smoke(); // 取走材料、卷成烟并抽完
              V(finish); // 提示供应者可以继续供应下一批材料
            }
          }
        
  • 利用银行家算法进行死锁避免

      1)检查请求向量是否不大于请求进程Pi的需求向量和可利用资源向量;
      2)检查成功后预分配,然后执行安全性算法,判断此次分配是否会让系统进入不安全状态;
      3)安全性算法:
      	① 从所有进程的需求向量中选择一个不大于可利用资源向量;
        ② 将其对应进程加入安全序列,删去该进程,并且将可资源向量加上该进程的已分配向量;
        ③ 重复①、②,直到所有进程都加入安全序列(此时判断处于安全状态),或者无法找到可加入安全序列的进程		(此时判断处于不安全状态);
    
  • 计算必不会发生死锁的最少资源数/最大进程数:

    • 假设有N个进程,每个进程需要Mi个同类资源,则必不会发生死锁的最少资源数为 ∑ ( M i − 1 ) + 1 \sum (M_i-1) + 1 (Mi1)+1
    • 假设有N个资源,每个进程需要M个同类资源,则必不会发生死锁的最大进程数为 ⌊ ( N − 1 ) / ( M − 1 ) ⌋ \lfloor(N-1)/(M-1)\rfloor (N1)/(M1)
  • 动态分区分配策略:

    • 首次适应算法:按照地址递增顺序查找到第一个可以装下的分区进行分配;实际使用效果最好
    • 最佳适应算法:按照容量递增顺序查找到第一个可以装下的分区进行分配;回收分区后需要对空闲分区队列重新排序;最容易产生外部碎片
    • 最坏适应算法:按照容量递减顺序查找到第一个可以装下的分区进行分配;可以减少难以利用的小外部碎片,但同时可利用的大分区也减少,不利于大进程
    • 邻近适应算法:从上次查找结束的位置继续顺序查找;容易导致内存的末尾分配空间分配成小碎片
  • 分页/分段/段页式管理方式下逻辑地址到物理地址的转换过程;

  • 请求分页虚拟管理方式下虚实地址的转换过程;

  • 需要几级页表的计算:

    • 假设逻辑地址中除了页内偏移的长度为 L L L,每页能放 2 N 2^N 2N个页表项,即每页能放 2 N 2^N 2N页,而最高级页表必须放进一页之中
    • 故需要 L / N L/N L/N级页表,每一级的地址长度为 N N N
  • 页面置换算法:

    • 最佳置换算法:选择以后最长时间不再使用的页面淘汰;可以保证最低的缺页率

    • FIFO置换算法:优先淘汰最早进入内存的页面;(可能会出现Belady现象缺页次数随着分配给进程的页框个数增加而增加

    • LRU置换算法:选择最近最长时间没被访问的页面进行淘汰,需要每个页面设置一个访问字段,标识上次被访问的时间戳;

    • CLOCK置换算法

      1)将页面按照访问顺序构成环,每一个新调入的页面的访问位A的初始值为12)时针从上一次换出的下一个页面开始转动,直到找到访问位A=0的页面,进行替换;
      3)每当遇到一个访问位A=1的页面,则将其访问位置04)最多转过一圈,则所有页面的访问位均置0,因此重复2即可;
      
    • 改进CLOCK置换算法

      1)将页面按照访问顺序构成环,每一个新调入的页面的访问位A的初始值为1,修改位M的初始值为02)时针从上一次换出的下一个页面开始转动,寻找(A,M)=(0,0)的页面,进行替换(此过程不修改访问位);
      3)若转过一圈仍没有(A,M)=(0,0)的页面,则寻找访问位(A,M)=(0,1)的页面,进行替换(此过程需要将跳过的访问位置0);
      4)若转过一圈仍没有(A,M)=(0,1)的页面,则由于上一圈已经将所有的访问位置0,因此一定有(A,M)=(0,0)(0,1)的页面,因此重复2,有必要再重复3即可;
      
  • 页面分配策略:

    • 固定分配+局部置换:每个进程分配固定的物理页数,缺页时在所分配的物理页中换页;
    • 可变分配+全局置换:进程分配一定的物理页,缺页时从系统维护的一个空闲物理页队列中选择一个分配
    • 可变分配+局部置换:进程分配一定的物理页,缺页时在所分配的物理页中换页;频繁换页时,系统为进程再分配若干页;缺页率低于阈值时,系统适当减少分配的页数;
  • 索引结点相关计算:

    • 单个文件最大长度计算
      • 假设一个磁盘块的大小为 N N N,一个索引结点有 a a a个直接索引、 b b b个一级间接索引、 c c c个二级间接索引、 d d d个三级间接索引,每个地址项的大小 m m m
      • 直接索引文件最大长度: L 0 = a × N L_0 = a\times N L0=a×N
      • 一级间接索引文件最大长度: L 1 = b × ( N / m ) × N L_1 = b\times (N/m)\times N L1=b×(N/m)×N
      • 二级间接索引文件最大长度: L 2 = c × ( N / m ) × ( ( N / m ) × N ) L_2 = c\times(N/m)\times( (N/m)\times N ) L2=c×(N/m)×((N/m)×N)
      • 三级间接索引文件最大长度: L 3 = d × ( ( N / m ) × ( ( N / m ) × N ) ) L_3 = d\times( (N/m)\times( (N/m)\times N ) ) L3=d×((N/m)×((N/m)×N))
      • 单个文件最大长度: L = L 0 + L 1 + L 2 + L 3 L = L_0+L_1+L_2+L_3 L=L0+L1+L2+L3
    • 访问文件访问磁盘次数计算:
      • 假设找到文件的 F C B FCB FCB需要 K K K次访问磁盘;
      • F C B FCB FCB中,最好情况下文件访问位置在直接索引中,能直接读到文件块地址,因此只需要 K + 1 K+1 K+1次访问;
      • 如果文件存在 n n n级索引中,需要逐级搜素到直接索引,因此需要 K + ( n + 1 ) K+ (n+1) K+(n+1)次访问;
    • 文件实际占用空间
      • 假设文件数据大小为 M M M,一个磁盘块的大小为 N N N,一个索引结点有 a a a个直接索引、 b b b个一级间接索引、 c c c个二级间接索引、 d d d个三级间接索引,每个地址项的大小 m m m;且假设文件大到需要用到三级索引(但用不完);
      • F C B FCB FCB地址块大小: s 1 = ( a + b + c + d ) × m s_1 = (a+b+c+d)\times m s1=(a+b+c+d)×m
      • 一级间接索引所指向的直接索引大小: s 2 = b × N s_2 = b\times N s2=b×N
      • 二级间接索引所指向的一级间接索引大小: s 3 = c × N s_3 = c\times N s3=c×N
      • 二级间接索引所指向的直接索引大小: s 4 = c × N 2 s_4 = c\times N^2 s4=c×N2
      • 三级索引所指向的二级间接索引个数: n u m = ( M − a × N − b × ( N / m ) × N − c × ( N / m ) 2 × N ) ( ( N / m ) 2 × N ) num = \cfrac{( M-a\times N-b\times(N/m)\times N-c\times(N/m)^2\times N )}{( (N/m)^2\times N )} num=((N/m)2×N)(Ma×Nb×(N/m)×Nc×(N/m)2×N)
      • 三级索引所指向的索引大小: s 5 = n u m × ( N + N 2 ) s_5 = num\times(N+N^2) s5=num×(N+N2)
      • 文件实际占用空间 = 文件数据 + 文件索引 = M + ( s 1 + . . + s 5 ) M + (s_1+..+s_5) M+(s1+..+s5)
  • 位示图法空闲块管理方式地址转换:

    • 假设位示图矩阵起始位置为 ( 0 , 0 ) (0,0) (0,0),一行有 n n n位;
    • 位示图第 i i i行、第 j j j列的盘块号 b = n × i + j b = n\times i + j b=n×i+j
    • 盘块号为 b b b的行数 i i i和列数 j j j i = b / n ; j = b % n i = b / n;j = b \% n i=b/nj=b%n
  • 空闲块成组链接法查找过程;

  • 磁盘读写时间计算

    • 寻找时间 T s T_s Ts:将磁头移动到指定的磁道所需要的时间(磁头径向移动的时间,一般时间最长),设跨越 n n n条磁道,启动磁臂的时间为 s s s,跨越每条磁道的速度是 m m m,则 T s = m n + s T_s = mn+s Ts=mn+s
    • 延迟时间 T r T_r Tr:磁头定位到某一磁道的扇区的时间,设磁盘旋转速度为 r r r,则 T r = 1 / 2 r T_r = 1/2r Tr=1/2r
    • 传输时间 T t T_t Tt:从磁盘读出/写入数据的时间,设读/写的字节数为 b b b,磁盘旋转速度为 r r r(转/秒), N N N为一个磁道上的字节数,则 T t = b / r N T_t = b/rN Tt=b/rN
    • 总平均存取时间 T a : T a = T s + T r + T t T_a:T_a = T_s + T_r + T_t TaTa=Ts+Tr+Tt
  • 磁盘物理地址转换

    • 假设磁盘有 A A A个柱面,每个柱面有 B B B个磁道,每个磁道有 C C C个扇区,每个文件簇对应 D D D个扇区大小;
    • N N N簇对应的柱面为: N ( B × C / D ) \cfrac{N}{(B\times C/D)} (B×C/D)N
    • 第N簇对应的磁道为: N % ( B × C / D ) ( C / D ) \cfrac{ N\%(B\times C/D) }{ (C/D)} (C/D)N%(B×C/D)
    • 第N簇对应的扇区为: ( ( N % ( B × C / D ) ) % ( C / D ) ) × D ( ( N\%(B\times C/D) ) \% (C/D) ) \times D ((N%(B×C/D))%(C/D))×D
  • 磁盘调度算法:

    • F C F S FCFS FCFS算法(唯一不会造成磁臂黏着现象的算法);
    • S S T F SSTF SSTF算法;(会导致饥饿现象);
    • S C A N SCAN SCAN算法 / L O O K LOOK LOOK算法;
    • C − S C A N C-SCAN CSCAN算法 / C − L O O K C-LOOK CLOOK算法;
  • 缓冲区机制下磁盘I/O操作的时间计算

    • 假设从磁盘读入数据到缓冲区需要时间 T T T,从缓冲区读入用户区需要时间 M M M C P U CPU CPU处理用户区数据需要时间 C C C
    • 单缓冲:
      • 初始状态:工作区满,缓冲区空;
      • 处理每一块数据的时间为 max ⁡ ( T , C ) + M \max(T,C)+M max(T,C)+M
      • 处理 n n n块数据的总时间为: { n ( T + M ) + C , T > C n ( C + M ) + T , C > T \begin{cases}n(T+M)+C,&T>C\\n(C+M)+T,& C>T \end{cases} {n(T+M)+C,n(C+M)+T,T>CC>T
    • 双缓冲:
      • 初始状态:工作区空,一个缓冲区满,另一个缓冲区空;
      • 处理每一块数据的时间为 max ⁡ ( T , C + M ) \max(T, C+M) max(T,C+M)
      • 处理 n n n块数据的总时间为: { n T + ( C + M ) , T > C + M n ( C + M ) + T , C + M > T \begin{cases}nT+(C+M),&T>C+M\\n(C+M)+T,& C+M>T \end{cases} {nT+(C+M),n(C+M)+T,T>C+MC+M>T


你可能感兴趣的:(408考研笔记系列,操作系统,408考研)