操作系统基础概念

文章目录

      • 基本概念
      • 处理器管理
        • 处理器
        • 中断
        • 进程
        • 线程
        • 处理器调度
        • 处理器调度算法
      • 并发进程的同步与互斥、死锁与饥饿
        • 临界区管理
        • 信号量与PV操作
        • 经典同步问题--哲学家进餐问题
        • 经典同步问题--生产者消费者问题
        • 经典同步问题--读者写者问题
        • 经典同步问题--睡眠理发师问题
        • 管程
        • 进程间通信
        • 死锁
      • 存储管理
        • 连续存储管理
        • 分页存储管理
        • 分段存储管理
        • 虚拟存储管理
      • 设备管理
        • 基础概念
        • I/O软件系统
        • 磁盘管理
        • 虚拟设备
      • 文件管理
        • 文件与目录
        • 文件结构
        • 文件空间管理
      • 问题
      • 参考

基本概念

操作系统(Operating System,简称OS)是管理系统资源、控制程序执行、改善人机界面、提供各种服务、合理组织计算机工作流程和为用户有效使用计算机提供良好运行环境的一种系统软件。

操作系统的主要功能是资源管理运行程序,OS直接安装在硬件上,屏蔽硬件细节,向上提供统一抽象接口。

  • 运行程序:编译链接形成可执行文件→外存→内存分配空间→加上PCB控制块形成进程→执行程序→回收内存等软硬件资源

  • 资源包括硬件与软件资源,硬件资源包括CPU、内存、外存、输入输出设备等。资源管理包括处理器管理(进程/线程)、存储管理(内存)、设备管理(I/O)、文件管理、网络与通信管理等

操作系统的资源管理技术主要包括:

  • 资源复用:指多个进程共享物理资源,包括分割资源为较多更小单位的空分复用和分时轮流使用资源的时分复用。进程是有资格获得系统资源的独立主体。
  • 资源虚化:指利用一类事物模拟另外一类事物,造成另外一类事物数量更多或容量更大的假象。如虚拟内存映射。
  • 资源抽象:指利用软件封装复杂的硬件或软件设施,简化资源应用接口的一种资源管理技术。

操作系统的主要目标为:

  • 方便用户使用:尽量屏蔽硬件细节,向上提供统一抽象接口
  • 扩充机器功能:在固有硬件的基础上安装使用各种各样的软件,提供多种服务
  • 管理系统资源:帮助管理各种软硬件资源
  • 提高系统效率:OS可自我管理
  • 构筑开放环境:平台移植性与各操作系统间的互联互操作性

操作系统特性:

  • 并发性(Concurrence):指两个或两个以上的事件或活动在同一时间间隔内发生。(对应的,并行性(parallelism)是指两个或两个以上的事件或活动在同一时刻发生。)。操作系统的并发性是指计算机系统中同时存在若干个运行着的程序,这些程序交替、穿插地执行。
  • 共享性:指操作系统中的资源可被多个并发执行的进程共同使用,而不是被其中某一个程序所独占。
  • 异步性:是并发性的表现特征,在多道程序环境中,程序的执行不是一贯到底,而是“走走停停”,何时“走”,何时“停”是不可预知的。并发执行与顺序执行效果一致
  • 虚拟性:是操作系统资源管理技术的特性,虚拟资源管理技术即资源虚化,将物理上的一个实体变成逻辑上的多个对应物,或把物理上的多个实体变成逻辑上的一个对应物。

单道程序设计与多道程序设计

涉及到操作系统的发展历史,在打卡片的年代里,程序运行时的很多步骤都手动处理,单道程序即一次只执行一个程序,多道程序设计则是指允许多个程序同时进入一个计算机系统的主存储器并启动进行交替计算的方法(并行性,如在CPU运转的时候另一个程序的输入也可同时进行)CPU利用率=(CPU运转时间/同一时间所处理的所有程序的总运行时间)

内核:OS在硬件上的首层抽象,屏蔽硬件细节,向上提供统一抽象接口

系统调用:内核中函数,获取操作系统服务的唯一途径

系统调用过程:请求系统调用→由用户态转换为核心态→发生中断,现场保护→执行完毕,内核(系统)调用返回

系统调用与过程(函数)调用的区别

(1)调用形式不同
过程(函数)使用一般调用指令,其转向地址包含在跳转语句中;系统调用不包含处理程序入口,仅仅提供功能号,按功能号调用。
(2)被调用代码的位置不同
在过程(函数)调用中,调用程序和被调用代码在同一程序内,经过连接编译后作为目标代码的一部分。当过程(函数)升级或修改时,必须重新编译连接。
系统调用的处理代码在调用程序之外(在操作系统中),系统调用处理代码升级或修改时,与调用程序无关。
(3)提供方式不同
过程(函数)由编译系统提供或用户编写,不同编译系统提供的过程(函数)可以不同;
系统调用由操作系统提供,一旦操作系统设计好,系统调用的功能、种类与数量就固定不变了。
(4)调用的实现不同
程序使用一般机器指令(跳转指令)来调用过程(函数),是在用户态运行的;
程序执行系统调用,是通过中断机构来实现的,需要从用户态转变到核心态,在管理态执行。

操作接口:是操作系统为用户提供的操作控制计算机工作和提供服务手段的集合。包括图形用户界面,命令行工具等

OS结构

  • 单体式结构:整个OS为一个大型可执行二进制文件,在内核中以单一程序方式运行

  • 层次式结构:将OS划分为内核与多个模块,垂直排列,为单向调用关系,低层为高层提供服务

  • 客户机/服务器与微内核结构:划分功能模块与微内核,功能模块为服务器进程,用户进程为客户机进程,使用消息传递进行通信,用户进程向服务器进程请求资源,内核管理服务器进程

OS运行模型

  • 非进程模式:将OS组织成一个例行程序,用户程序/数据/堆栈+内核程序/数据/堆栈构成进程映像,操作系统本身不是一个进程,在其上的软件运行时访问内核,加上内核程序构成进程映像进行执行
  • 进程模式:将OS组织成一组系统进程,OS功能由这些系统进程集合提供

处理器管理

处理器

处理器属于分时复用型的共享资源,其中的寄存器被操作系统和各个进程所共享,任意瞬时的寄存器内容构成处理器工作现场。当进程或任务发生切换时,寄存器内容必须被保存,以便进程或任务恢复执行时,还原处理器工作现场。

指令分特权指令与非特权指令:特权指令仅提供给操作系统核心程序使用

处理器状态转换

  1. 用户态转向管态:执行系统调用,请求操作系统服务;中断事件产生,运行程序被中断,操作系统接管处理器,中断处理程序开始工作。
  2. 管态转向用户态:执行加载程序状态字LPSW(Load PSW)指令时转换。

PSW即程序状态字,处理器的工作状态记录在程序状态字(PSW)寄存器中;每个正在执行的程序都有一个与其执行相关的PSW(进程与一个PSW对应);每个处理器都设置一个程序状态字寄存器。

PSW存储的主要信息包括:

  • 程序基本状态:程序计数器、处理器状态位、条件码等

  • 中断码:记录发生的中断事件

  • 中断屏蔽位:决定是否响应中断事件

中断

中断:指程序执行过程中发生某个事件时,终止CPU当前程序,转而执行事件的过程

中断是改变指令执行流程、实现操作系统并发多任务功能的重要硬件机构,也是操作系统实现计算机控制的重要途径。检测中断的方法为:在每两条指令或某些特殊指令执行期间,每当一条指令执行结束时都检测是否发生中断以此来检测并响应中断。

中断源是引起中断的事件,中断装置是发现中断源并产生中断的硬件,当中断事件发生后,它能改变处理器内操作执行的顺序。

分类

  • 强迫性中断:外部随机事件引起,如电源故障,设备出错等
  • 自愿性中断:程序自己引发的期待事件
  • 外中断:处理器与主存之外的中断,如时钟中断、I/O中断等
  • 内中断:处理器与主存内部的中断,如地址越界,算术操作溢出等,内中断不能屏蔽
  • 硬中断:由硬件设施产生的中断信号;硬中断发生时会立刻响应
  • 软中断:利用软件模拟产生的中断信号,用于实现内核与进程或进程与进程之间的通信;接收软中断的进程运行时才能响应

中断与异常的区别

(1)中断不是现行程序引发的,而是由与现行指令无关的中断信号触发的;异常是现行程序引发的
(2)中断处理程序提供的服务一般不是现行程序需要的,通常两条机器指令之间才可响应中断;异常是由程序本身产生的,异常处理一般是现行程序所需要的

中断处理

使用硬件+软件实现中断处理,中断寄存器存储中断字,中断字每一位代表一个中断事件,每条指令执行完毕后检查中断字。中断指令刺激中断源产生中断信号,中断信号置中断字对应位为1,检查中断字发现中断,CPU响应中断请求,中断位置0

发现中断源,响应中断请求→保护现场→启动中断处理程序→中断返回

中断处理程序主要用于处理事件与恢复现场,当响应中断时调用中断处理程序,然后保护现场,不同的中断源对应不同的中断处理程序,维护一个中断处理程序入口的向量表

问题1:在系统中只有一个进程运行时,该进程会不间断地运行吗?

不一定,只要发生了系统调用仍然会产生中断,同时程序运行过程中会产生各种各应的中断信号,如I/O中断,时钟中断或各种内中断,一般来说一个进程运行时不会不间断的运行,总会产生某些中断。

问题2:在系统中没有任何进程运行时,操作系统在干什么?系统会静止吗?

不会,此时会有时钟中断处理程序在运行,等待服务程序的到来;中断犹如计算机系统的心脏,无论何时不能停止跳动,否则,计算机系统将失去驱动力;中断也像刹车,可以使用户程序及系统程序适时停止运行,以便重新分配系统资源,尤其是处理器资源,避免某些程序过长时间地垄断系统资源,破坏系统资源使用的公平性。

进程

进程是一个可并发执行的具有独立功能的程序关于某个数据集合的一次执行过程,也是操作系统进行资源分配和保护的基本单位。

进程与程序的区别

(1)程序是静态的,进程是动态的,进程是按照程序运行的过程。
(2)同一个程序在一段时间内可以同时存在多个执行活动(即进程)分别对不同的数据进行处理

进程的属性

  • 结构性:进程包含了数据集合与运行于其上的程序,至少包含三个部分:程序块、数据块、进程控制快。进程=程序代码+相关数据+PCB
  • 共享性:同一程序运行于不同数据集合上构成不同的进程
  • 动态性:进程的内容(主要是数据)是动态的,随着指令的执行而变化;程序的内容是固定的。
  • 独立性:进程是系统中资源分配和保护的基本单位,也是系统调度的独立单位。
  • 制约性:并发进程之间存在同步、互斥关系;同步:存在协作关系的一个进程在某些执行点需要等待另一个进程发来的同步信号;互斥:共享某些资源的进程需要依次排队使用这些资源。
  • 并发性:在单处理器系统环境下,各个进程轮流占用处理器,即交替、穿插使用处理器。

进程状态

划分进程状态对于简洁、条理地分类管理进程、合理调度系统资源非常必要。

  1. 三态模型

进程至少包含程序代码与数据集两个部分,即包括一系列顺序指令与该指令集合要操作的数据

要让OS管理进程,则必须还加上进程控制块(PCB),进程状态

  • 运行态:占用CPU,执行指令
  • 就绪态:具备运行条件,等待OS调度分配CPU以进入运行态
  • 等待态:进程等待事件发生,目前不能继续执行,缺乏条件
  1. 五态模型
  • 新建态:进程刚被创建,尚未提交参加处理器竞争
  • 运行态:占用CPU,执行指令
  • 就绪态:具备运行条件,等待OS调度分配CPU以进入运行态
  • 等待态:进程等待事件发生,目前不能继续执行,缺乏条件
  • 终止态:进程已经终止,但是进程尚未退出主存
  1. 七态模型

在三态模型中,进程的运行、就绪、阻塞都由OS在内存中进行调度管理,随着进程数的不断增加,系统资源会被瓜分干净,为了平滑系统负荷,必须把某些进程挂起换到磁盘镜像中,因此引入挂起态:

  • 挂起就绪态:进程就绪但位于外存
  • 挂起等待态:进程阻塞并位于外存

OS控制结构:操作系统的控制结构是控制进程、管理系统资源的数据结构,同样类型的数据结构组成表,称为控制表。

  • 进程控制表:登记进程管理信息,控制管理所有进程映像
  • 存储控制表:管理主存与外存
  • I/O控制表:管理I/O设备与通道
  • 文件控制表:登记打开文件的状态、属性信息

进程实体/进程映像

进程实体包括程序块、数据块、进程控制快(PCB)、核心栈

进程控制块PCB:进程创建时建立进程控制块,进程撤销时回收进程控制块,进程控制块与进程一一对应。主要包括标志信息(唯一标识一个进程)、现场信息(让出CPU时当下的CPU现场,便于后来恢复)、控制信息(用于管理和调度进程,如调度信息,资源清单等)等。

核心栈主要记录进程运行在核心态下时跟踪过程调用和过程间参数传递信息

进程实体的内容随着进程的执行不断发生变化,某时刻进程实体的内容及其状态集合称为进程映像。

进程上下文

进程物理实体和支持进程运行的环境合称为进程上下文。进程物理实体主要包括PCB、程序块、数据块等,进程运行环境主要包括核心栈、用户栈、寄存器等

进程队列

处于同一状态的所有PCB链接在一起的数据结构称为进程队列。队列可以按照进程状态进行细分,如就绪队列、等待队列等,通过管理该队列控制进程调度

进程切换

进程切换即中断一个进程的执行转而执行另一个进程。保存被中断进程上下文到其进程控制块中,然后装入新进程上下文,使其从上次断点恢复执行,或者调度一个新的进程。

进程调度的时机

(1)当进程进入等待态时:一个进程陷入阻塞,调一个就绪态的进程来执行
(2)当进程完成其系统调用返回用户态,但不是最有资格获得CPU时:把处理器分配给最有资格获得它的进程,进程切换由此发生。
(3)当内核完成中断处理,进程返回用户态但不是最有资格获得CPU时:发生强迫性中断时的处理情况,同(2)
(4)当进程执行结束时:回收资源,调度处理其他进程

进程的调度依赖于中断,中断是激活操作系统的唯一方法,只有中断发生时操作系统才能接管处理器,才有可能发生进程上下文的切换

进程切换的步骤(假设P1被中断,调度P2开始执行):

中断、保存P1现场信息→修改P1的PCB,将运行态改为等待态或就绪态→将P1加入相关进程队列(等待队列或者就绪队列)→在就绪队列中选择一个进程来执行,如P2→修改P2的PCB,将状态改为运行态→运行P2,直接执行或恢复上次断点状态继续执行

进程切换包含两次模式切换:一个进程的用户态→管态;管态→另一个进程的用户态

进程的控制与管理

OS对进程的控制与管理通过原语来实现

**原语是在管态下执行、完成系统特定功能的不可中断的过程,具有原子操作性。**原语可以采用屏蔽中断的系统调用来实现

(1)进程的创建:

①在主进程表中增加一项,并从PCB池中取一个空白PCB,为新进程分配唯一的进程标识符

②为新进程的进程映像分配地址空间,装入程序和数据。

③为新进程分配内存空间外的其它资源

④初始化进程控制块,如进程标识符、处理器初始状态、进程优先级等

⑤把进程状态置为就绪态并加入就绪进程队列

⑥通知操作系统的某些模块,如记账程序、性能监控程序

(2)进程的阻塞:

进程阻塞是指一个进程让出处理器,去等待一个事件。

①停止进程执行,保存现场信息到PCB

②修改PCB的有关内容,如进程状态由运行改为等待,并把修改状态后的PCB加入相应等待队列

③转入进程调度程序调度其他进程运行

(3)进程的唤醒:

①从相应等待队列中移出进程

②修改进程PCB有关信息,如进程状态改为就绪并把修改PCB后的进程加入就绪队列

③若被唤醒进程优先级高于当前运行进程,则重新设置调度标志

(4)进程的撤销:

①根据撤销进程标识号,从相应队列找到它的PCB

②将该进程拥有的资源归还给父进程或操作系统

③若该进程拥有子进程,则先撤销它的所有子孙进程,以防它们脱离控制

④撤销进程出队,将它的PCB归还到PCB池

(5)进程的挂起和激活:

挂起时:检查要被挂起进程的状态,若处于活动就绪态就修改为挂起就绪态,若处于阻塞态,则修改为挂起阻塞态。被挂起进程PCB的非常驻部分要交换到磁盘对换区。

激活时:把进程PCB非常驻部分调进内存,修改它的状态,挂起等待态改为等待态,挂起就绪态改为就绪态,加入相应队列中。

挂起原语既可由进程自己也可由其他进程调用,但激活原语只能由其它进程调用。

线程

线程:运行在同一个进程中的各个并发任务称为该进程的线程。线程是处理器调度的基本单位

线程实体

线程控制块TCB:包含唯一标识符、状态信息、线程上下文等

私有存储区:包含局部变量,用户栈等

核心栈:线程在核心态工作下存放的函数调用传递的参数、返回地址等信息

线程状态

只有三个关键状态:运行、就绪、等待(阻塞)

多线程优点

切换速度快、切换开销小
易于实现任务间的通信协作
提高并发度

多线程实现

  • 用户级线程:由程序自己实现,操作系统不参与调度
  • 内核级线程:内核级线程是在操作系统内核层对进程实现的多线程功能,操作系统以线程作为处理器调度和分派的基本单位
  • 混合式线程:某些操作系统提供了同时支持用户级线程与内核级线程的混合式线程设施,线程的创建、调度和同步在用户空间进行。一个应用程序中的多个用户级线程被映射到一些(小于或等于用户级线程的数目)内核级线程上。

处理器调度

处理器调度考虑两个问题:如何从多个作业中选择一些作业加载到内存中并为其创建进程?如何从多个进程中选择一个进程交由CPU处理

高级调度:从磁盘后备作业队列中挑选若干作业进入内存,为其分配资源,创建进程,决定一个进程能否被创建及创建后能否被置位就绪态

中级调度:决定哪些进程进入挂起状态,哪些进程从挂起状态中被唤醒,即决定哪些进程参与竞争处理器资源,哪些进程换出到磁盘,哪些进程从磁盘中换入内存

低级调度:决定哪一个就绪进程占用CPU,中断是执行低级调度的时机

调度时选择调度算法的因素

  • CPU利用率 = CPU有效工作时间/CPU总的运行时间
  • 吞吐率:单位时间内CPU处理的作业数
  • 公平性:至少确保不会出现饥饿现象(饥饿即调度时有的线程长时间占用不到CPU,对于用户来时就好像进程停止工作了一样)
  • 响应时间:从提交一个请求到接收到响应之间的时间间隔,响应时间 = 命令传输到CPU的时间+CPU处理命令的时间+处理结果返回终端的时间
  • 周转时间:作业提交给系统开始,到作业完成为止的时间间隔,即周转时间=完成时刻-提交时刻
  • 平均周转时间:n个作业的总周转时间/n
  • 带权周转时间:作业周转时间(总时间)/作业运行时间
  • 平均带权周转时间:n个作业的总带权周转时间/n

作业、进程和线程

作业对应一个完整的业务处理过程,该过程包含若干个相对独立又相互关联的顺序加工步骤,每个步骤对应着一个进程或线程

处理器调度算法

主要讲低级调度算法,可以分为两种类型:

  • 剥夺方式:由OS剥夺正在运行的程序,将CPU交给其它进程/线程使用。常见的有两种:高优先级剥夺:谁优先级高先运行谁;时间片剥夺:按时分方式时间片用完了就调度一次
  • 非剥夺方式:一旦某个进程或线程开始执行便不再出让处理器,除非该进程或线程运行结束或发生了某个事件不能继续执行。

两者一般混合使用,内核关键程序使用非剥夺方式、用户进程使用剥夺方式

调度算法

(1)先来先服务算法FCFS:谁先来就先服务谁,非剥夺

(2)最短作业优先算法SJF:对每一个到来的作业进行时间估计,那个时间最少就运行谁,非剥夺

(3)最短剩余时间优先算法SRTF:对每一个到来的作业进行时间估计,对当前时刻的所有进程进行时间评估,时间少的可抢占CPU优先处理,剥夺式

(4)响应比最高者优先算法HRRF:计算响应比,响应比 = 作业的响应时间/作业处理时间,选择响应比最高的先执行,非剥夺

(5)优先级调度算法:根据确定的优先级选取进程/线程,每次总是选择就绪队列中优先级最高者运行。

(6)轮转调度算法:每次把CPU分配给就绪队列首进程/线程使用一个时间间隔(称为时间片),就绪队列中的每个进程/线程轮流运行一个时间片。

并发进程的同步与互斥、死锁与饥饿

进程并发性:进程的并发性是指一组进程的执行在时间上是重叠的,即一个进程执行的第一条指令是在另一个进程执行的最后一条指令完成之前开始的。其实质是一个CPU在多个进程间复用,消除计算机部件之间的互等现象以提高系统资源利用率

  • 无关并发:一组并发进程分别在不同的变量集合上操作,一个进程的执行与其他并发进程的进展无关,即一个并发进程不会改变另一个并发进程的变量值。
  • 交互并发:一组并发进程共享某些变量,一个进程的执行可能影响其他并发进程的执行结果。

并发带来的问题

竞争CPU处理时间带来的问题(即进程指令执行顺序的先后):

  • 结果不唯一:同时处理一个共享变量导致对共享变量的操作不是原子性的
  • 永远等待:如等待进程错过了唤醒信号而永远等待的情况

竞争资源带来的问题:

  • 死锁:两个进程互相持有对方所需要的资源的同时又想获取对方持有的资源从而陷入永久等待的问题
  • 饥饿:一些进程总是优先于另一些进程,使某些进程陷入长久等待

进程互斥:指若干进程要使用同一共享资源时,任何时刻最多允许一个进程使用,其他要使用该资源的进程必须等待,直到占有资源的进程释放该资源。

进程同步:指两个以上进程基于某个条件协调彼此的活动,一个进程的执行依赖于协作进程的消息或信号,当一个进程没有得到来自于协作进程的消息或信号时需等待,直到消息或信号到达才被唤醒。即同步进程间必须严格按照规定的某种先后次序来运行,这种先后次序依赖于要完成的特定的任务。

临界区管理

临界区:并发进程中与共享变量有关的程序段叫做临界区,即一次只能有一个进程执行的程序段

临界资源:共享变量代表的资源叫做临界资源,不可共享资源

Peterson算法

Peterson算法是一个正确的临界区管理算法

bool inside[2];//标志对方进程是否进入临界区
inside[0]=false;
inside[1]=false;
enum {0,1} turn;//标志对方进程是否到达了临界区前
cobegin
process P0( )
{
  inside[0]=true;//标志该进程开始
  turn=1;
  while(inside[1]&&turn==1);//另一个进程开始但还没有更改turn时等待,一旦更改则那个进程等待而自己执行
  {临界区};
  inside[0]=false;
} 
process P1( )
{
inside[1]=true;
  turn=0;
  while(inside[0]&&turn==0);//等待
  {临界区};
  inside[1]=false;
}
coend

指令是顺序执行的,对于turn来说在临界区前只有一个值,根据turn的值来判断哪一个进程先进入临界区,对于上述程序来说,谁先更改turn谁就先执行,先执行的进程依赖于另一个进程更改turn的值。

实现临界区管理的硬件条件

硬件条件应该保证对于临界区的访问是互斥的

  • 关中断:进程在进入临界区之前先关中断,退出临界区时开中断。虽然简单但一般不使用,长时间关中断会影响系统效率且并不适用于多CPU系统

  • TS指令:

    TS(x)
    {
    若x=true,则x=false;return true;
    否则 return false;
    }
    

    该指令配合外部临界资源相当于一把锁,进入时上锁,退出时解锁

    bool s=true;//临界资源,相当于锁
    cobegin
    process Pi( )
    { //i=1,2,...,n
    while(!TS(s));    //上锁
    		{临界区};
    		s=true;//解锁
    }
    coend
    
  • 交换指令SWAP(a,b):

bool lock=false;//锁,只有开关两种状态,通过SWAP保证互斥
cobegin
Process Pi( )
{//i=1,2,...,n
	bool keyi=true;
	do
      {
   		SWAP(keyi,lock);//将两个值互换
      	}while(keyi); //上锁
	{临界区};
	SWAP(keyi,lock);//解锁
}
coend

信号量与PV操作

信号量:多个进程间可以通过简单的信号进行合作,可强迫一个进程在某个位置停止直到接收到一个特定的信号。

通常信号量的结构可以描述为:

typedef struct semaphore
{
     int value;        //信号量值
     struct pcb *list; //信号量队列指针
 } semaphore;

value表示可以进入临界区的进程的数量,>0表示还可以使用的资源数量,<0则表示正在等待的进程数量。list为指向阻塞的进程队列指针

P操作:表示请求进入临界区的操作:

void P(semaphore &s)
{
     s.value--;//如果<=0则直接将调用进程加入阻塞队列
     if(s.value<0)  W(s.list); //将P操作调用者进程置为阻塞状态并移入s信号量队列,转进程调度
}

V操作:表示从临界区出来,释放资源的操作:

void V(semaphore &s)
{
      s.value++;
      if(s.value<=0)  R(s.list);
      //从信号量s队列中释放一个等待信号量s的进程并转换成就绪态,自己则继续执行
}

使用PV操作实现互斥的基本步骤为

semaphore mutex;
mutex=1;
cobegin
process Pi( )
{ //i=1,…,n
	P(mutex);
	{临界区};
	V(mutex);
}
coend

经典同步问题–哲学家进餐问题

为了吃面,每个哲学家必须获得两把叉子,且每人只能从自己左边或右边去取叉子。

操作系统基础概念_第1张图片

错误的解决方法:

semaphore fork[5];//表示对每个叉子上锁
for (int i=0;i<5;i++)
fork[i]=1;
cobegin
process philosopher_i( )
{ //i= 0,1,2,3,4
	while(true)
	{
		think( );
  		P(fork[i]);
	 	P(fork[(i+1)%5]);
		eat( );
  		V(fork[i]);
	 	V(fork[(i+1)%5]);
    	}
}
coend

假设5个哲学家同时执行到P(fork[i]);,相当于每个人都先拿到了左边的叉子,再也没有人能够拿到右边的叉子,导致出现了死锁。

为避免死锁,可用的解决方案有:

(1)至多允许四位哲学家同时去拿左边的叉子

因此必须新增信号量表示同时拿左边的叉子的人数

semaphore fork[5];//表示对每个叉子上锁
for (int i=0;i<5;i++)fork[i]=1;
semaphore count=4;//表示能够拿到左边1的叉子的人数
cobegin
process philosopher_i( )
{ //i= 0,1,2,3,4
	while(true)
	{
		think( );
        P(count);//相当于对count上锁,最多同时四个哲学家能够拿叉子
  		P(fork[i]);
	 	P(fork[(i+1)%5]);
		eat( );
  		V(fork[i]);
	 	V(fork[(i+1)%5]);
        V(count);
    	}
}
coend

(2)规定奇数号哲学家先拿他左边的叉子,然后再去拿右边的叉子;偶数号哲学家先拿他右边的叉子,然后再去拿左边的叉子

只要奇偶互相竞争,总会有一个哲学家空出来能同时拿到两把叉子,即先进行一次两两配对,保证有哲学家先拿到一把叉子,再进行另一把叉子的竞争

semaphore fork[5];//表示对每个叉子上锁
for (int i=0;i<5;i++)fork[i]=1;
cobegin
process philosopher_i( )
{ //i= 0,1,2,3,4
	while(true)
	{
		think( );
        if(i%2==0){
            P(fork[(i+1)%5]);
            P(fork[i]);
        }else{
            P(fork[i]);
            P(fork[(i+1)%5]);
        } 
		eat( );
  		V(fork[i]);
	 	V(fork[(i+1)%5]);
    	}
}
coend

(3)仅当哲学家的左右两把叉子均可用时,才允许他拿起叉子进餐,否则一把叉子也不取:

semaphore fork[5];//表示对每个叉子上锁
for (int i=0;i<5;i++)fork[i]=1;
semaphore mutex=1;//全局锁
bool flag[5];
for (int i=0;i<5;i++)flag[i]=true;//true表示该叉子没有被拿走
cobegin
process philosopher_i( )
{ //i= 0,1,2,3,4
	while(true)
	{
		think( );
        if(!flag[i]||!flag[i+1%5])continue;//只要有一把拿不到就不拿
        P(mutex);
        if(flag[i]&&flag[i+1%5]){//只有两把叉子都能拿时才设置拿走状态,否则就一把也不拿
            flag[i]=false;
            flag[i+1%5]=false;
            V(mutex);
        }else{
            V(mutex);
            continue;
        }
        
        if(!flag[i]&&!flag[i+1%5]){//达到正确状态,可以直接拿左右两把叉子
            P(fork[i]);
            P(fork[i+1%5]);
        	eat( );
  			V(fork[i]);
	 		V(fork[(i+1)%5]);
            P(mutex);//重置状态
            flag[i]=true;
            flag[i+1%5]=true;
            V(mutex);
        }else continue;
}
coend

经典同步问题–生产者消费者问题

item B[k];
semaphore empty=k;//可以使用的空缓冲区数
semaphore full=0;//缓冲区内可以使用的产品数
semaphore mutex=1; //互斥信号量,B的锁
int in=0;		//写缓冲区指针
int out=0;     //读缓冲区指针
cobegin
process producer_i( )
//生产者进程
{
  while(true)
  {	
      produce( ); 
	  P(empty); //可添加槽减一
	  P(mutex); 
	  append to B[in];//向全局资源中添加产品   
	  in=(in+1)%k;  
	  V(mutex); 
	  V(full); //可用资源加一
   }
} 
process consumer_j( )
//消费者进程
{
  while(true)
  {
	P(full);//可用资源减一
	P(mutex);
	take( ) from B[out];
	out=(out+1)%k;
	V(mutex);
	V(empty);//可添加槽加一
	consume( );
  }
}
coend

经典同步问题–读者写者问题

一次可以有多个读者,但最多只能有一个写者,读写不能同时进行

读者优先解决方案:

int readcount=0;  //读进程计数
semaphore writeblock=1;//写锁,一次只能有一个
semaphore mutex=1;//资源锁
cobegin
process reader_i( )//读者进程 
{
  	P(mutex);
 	readcount++; 	
  	if(readcount==1)  P(writeblock);//可以有多个读者,一旦读者进入,则写者不能进入
	V(mutex);
  	{读文件};
  	P(mutex);
  	readcount--;
  	if(readcount==0) V(writeblock);//没有读者则释放写锁
   	V(mutex);
}
process writer_j( )//写者进程
{
	P(writeblock);//上写锁
	{写文件};
	V(writeblock);
}
coend

上述解决方案可能导致写者的饥饿,为避免写者的饥饿,可使用写者优先方案,在写者想要写时不允许读者在进入以避免写者的饥饿。

写者优先方案:

int readcount=0;  //读进程计数
semaphore readblock=1;//读锁,标志是否有读进程
int writecount=0;//写进程技术
semaphore writeblock=1;//写锁,标着是否有写进程

semaphore readmutex=1;//资源锁,保证readcount的原子性
semaphore writemutex=1;//资源锁,保证writecount的原子性
semaphore mutex=1;//资源锁,读写资源的加锁
cobegin
process reader_i( )//读者进程 
{
    P(readblock);//一次只允许一个读进程尝试进行一次操作
    P(writeblock);//写者优先,读者只能先获得写者的锁进行一次操作,如果写者获取到了,则读者不可以在进入
  	P(readmutex);
 	readcount++; 	
  	if(readcount==1)P(mutex);//没有写者,读者可以有多个
    V(readmutex);
    V(writeblock);
    V(readblock);

  	{读文件};
    
  	P(readmutex);
  	readcount--;
  	if(readcount==0) V(mutex);//没有读者则释全局资源锁
   	V(readmutex);
}
process writer_j( )//写者进程
{
	P(writemutex);
    writecount++;//写者数
    if(writecount==1)P(writelock);//有写者进来,因为写者优先,读者不能进来,但是写者可以进来
    V(writemutex);
    
    P(mutex);//写者虽然进来了,但是还是要竞争唯一资源锁
	{写文件};
    V(mutex);
    
    P(writemutex);
    writecount--;
    if(writecount==0)V(writelock);//没有写者了,释放写者标志锁
	V(writemutex);
}
coend

经典同步问题–睡眠理发师问题

理发店有一位理发师、一把理发椅和n把椅子供顾客等候理发休息。如果没有顾客,理发师便在理发椅上睡觉。
一个顾客到来时,他必须叫醒理发师。如果理发师正在理发时又有顾客到来,则如果有空椅子可坐,顾客就坐下来等待,否则离开。

int waiting=0;   //等候理发顾客坐的椅子数
int CHAIRS=N;   //为顾客准备的椅子数
semaphore customers=0;
semaphore barbers=0;
semaphore mutex=1;//waiting的锁
cobegin
process barber( ) 	//理发师进程
{
    while(true) 
    {
           P(customers);//理发师需要顾客来进行唤醒,尝试获取顾客
           P(mutex);   //若有顾客时,以顾客当产品,取一个顾客消费
           waiting--;       //等候顾客数少一个
           V(barbers);    //理发师喊一个顾客来准备为他理发,即理发师可用
           V(mutex);      //退出临界区
          cut_hair();     //理发师正在理发(非临界区)
    }
}
process customer_i( ) //顾客进程
{
	P(mutex);    //进入临界区
  	if(waiting<CHAIRS)
	{//若有空椅子,则等候顾客数加1,否则顾客离开
  		waiting++;
  		V(customers); //可用顾客数增1,即顾客可用
  		V(mutex);    //退出临界区
  		P(barbers);  //尝试获取理发师
  		get_haircut();    //否则顾客坐下理发
  	}else V(mutex);   //人满了,走吧!
}
coend

管程

管程是由局部于自己的若干公共变量及其说明和所有访问这些公共变量的过程所组成的软件模块。将共享资源放入管程中由管程进行保护,管程确保同一时间只有一个进程进入临界区,其余进程进入队列

  • 共享性:管程可被系统范围内的进程互斥访问,属于共享资源
  • 安全性:管程的局部变量只能由管程的过程访问,不允许进程或其它管程直接访问,管程也不能访问非局部于它的变量
  • 互斥性:多个进程对管程的访问是互斥的。任一时刻,管程中只能有一个活跃进程
  • 封装性:管程内的数据结构是私有的,只能在管程内使用,管程内的过程也只能使用管程内的数据结构。进程通过调用管程的过程使用临界资源

管程的一般结构为

  • 局部数据和条件变量组成管程内的数据结构
  • 过程/函数1~过程/函数k组成管程内的一组过程,对管程内的数据结构进行操作
  • 初始化代码对管程内的数据结构进行初始化

管程内部的一般部件为

  • 管程入口处的等待队列:管程是互斥进入的,当一个进程试图进入一个已被占用的管程时,它需在管程入口处队列中等待。
  • 管程内的资源等待队列:管程是用于管理资源的,进入管程的进程等待资源时加入资源等待队列,该队列由条件变量维护。资源等待队列可以有多个,每种资源一个队列。
  • 条件变量:条件变量是管程内的一种数据结构,只有在管程中才能被访问,它对管程内的所有过程是全局的,只能通过两个原语操作来控制它:c.wait( )与c.signal( ),在资源队列中阻塞或唤醒关于资源c的进程
  • 管程内的紧急等待队列:当管程内的进程P唤醒等待队列中的另一个进程Q时,管程中便存在两个可运行进程。其中就绪进程需要加入就绪队列,设置在管程中的就绪队列称为紧急等待队列。紧急等待队列的优先级高于入口等待队列的优先级。

条件变量与P、V操作中信号量的区别

条件变量是一种非计数信号量,维护队列时不对其中的等待进程计数。因此在使用条件变量x时,通常需要定义一个与之配套使用的整型变量x-count用于记录条件变量x所维护等待队列中的进程数。而P、V操作中的计数信号量不仅维护相关队列,还记录队列中进程数。

管程的一般实现

在管程内部使用PV操作完成互斥

typedef struct InterfaceModule//管程内部维护的数据结构
{
semaphore mutex1=1;//进程调用管程过程前使用的互斥信号量
semaphore next=0; //发出signal的进程挂起自己的信号量
int next_count=0;//在next上等待的进程数
};
//进入管程
void enter(InterfaceModule &IM)
{
     P(IM.mutex);//P操作进入管程
}
//退出管程
void leave(InterfaceModule &IM)
{
    if(IM.next_count>0) V(IM.next);//优先唤醒管程内紧急等待队列中的等待进程
    else V(IM.mutex);//当管程内无等待进程时再唤醒管程外入口处的等待进程
}
//管程内部的资源等待(阻塞)操作,让一个进程来获取资源x_sem,自己则阻塞于x_sem等待被唤醒
void wait(semaphore&x_sem, int&x_count, InterfaceModule&IM)
{
  	x_count++;//等待资源的进程数增1,等待资源的进程将加入x_sem所管理的资源等待队列
  	if(IM.next_count>0) V(IM.next);//等待资源的进程睡眠之前,优先唤醒管程内紧急等待队列中的就绪进程
 	else V(IM.mutex);//若管程内紧急等待队列空,则唤醒管程入口处等待进入管程的队列中的进程
    P(x_sem);//唤醒其它进程之后,等待资源的进程加入x_sem所管理的资源等待队列睡眠,下面的x_count--会暂停执行,待该进程被唤醒后再执行
    x_count--;
}
//管程内部的唤醒操作,释放自己持有的x_sem资源并将自己阻塞于紧急等待队列等待自己被唤醒
void signal(semaphore&x_sem, int&x_count, InterfaceModule&IM)
{
  	if x_count > 0 {//若有等待资源的进程,即x_sem资源等待队列若有进程
    	IM.next_count++;//执行signal操作的进程将加入紧急等待队列,因此该队列进程数增1
    	V(x_sem);//执行signal操作的进程自己阻塞前,先释放一个等待资源的进程	
        P(IM.next);//执行signal操作的进程进入管程内紧急等待队列,下面的IM.next_count--暂停执行,只有进程被唤醒后才会执行 
    	IM.next_count--;
    }
}

使用管程解决生产者消费者问题

type producer_consumer=monitor	//管程定义
{
    item B[k];     //缓冲区个数
    int in,out;    //存取指针
    int count;     //缓冲中产品数
    semaphore notfull,notempty; //条件变量
    int notfull_count,notempty_count;
    InterfaceModule IM;
   //封装两个生产与消费操作     
   void append(item x){
   	enter(IM);//进入管程
	if(count==k) wait(notfull,notfull_count,IM);//缓冲已满,在notfull队列等待空白缓冲区
	B[in]=x;
	in=(in+1)%k;
	count++; //增加一个产品
	signal(notempty,notempty_count,IM);//唤醒notempty队列中的等待消费者,自己入紧急等待队列
  	leave(IM);//离开管程
	}
	void take(item &x){
     	enter(IM);
		if(count==0) wait(notempty,notempty_count,IM);//在notempty队列中等待满缓冲区
      	x=B[out];
      	out=(out+1)%k;
      	count--;//减少一个产品
      	signal(notfull,notfull_count,IM);//唤醒notfull队列中的等待生产者
      	leave(IM);
     }
}
cobegin
process producer_i()	//进程定义
{
	item x;
	produce(x);//生产
	producer_consumer.append(x);//追加
}
process consumer_j()
{
	item x;
	producer_consumer.take(x);//取出
	consume (x);//消费
}
coend

进程间通信

使用管道

管道是连接读写进程的一个共享文件,允许进程以先进先出(FCFS)方式写入和读出数据,并对读写操作进行同步。发送进程以字符流形式把大量数据送入管道尾部,接收进程从管道头部接收数据。

操作系统基础概念_第2张图片

管道可借助文件系统实现,包括(管道)文件的创建、打开、关闭和读写。

管道应互斥使用,管道读写不能同时进行;读写双方必须能够知道对方是否存在,只有存在才能通信;管道空间大小通常是固定的,读写操作时需要考虑写满和读空问题。

  • 匿名管道:仅存在于内存中的临时对象,仅用于具有共同祖先进程的父子进程或兄弟进程之间的通信。引用于内存中的临时管道
  • 有名管道:又称为FIFO,具有与之关联的文件名、目录项和访问权限,无亲缘关系的进程可以通过引用有名管道的名字进行通信。有名管道存于外存,使用时调入内存。

使用共享内存

JVM的内存模型也使用该思想,每个线程都有自己的工作内存,存储主内存中的变量拷贝,只能在自己的工作内存中工作,线程间无法互相访问,只能通过主内存进行传递赋值。

共享内存是允许两个或多个进程共同访问的物理内存区域,是实现进程通信的一种手段。共享内存区映射到进程中未使用的虚地址区,以免与进程映像发生冲突。共享内存区属于临界资源,读写共享内存区的代码属于临界区。

共享内存需要有标识符进行标识,第一个进程创建共享内存,其它进程则通过创建操作获得共享内存标识符,并据此读写共享内存(进行系统调用创建共享内存)。通信进程将先前创建的共享内存映射到自己的虚拟地址空间,使共享内存成为进程地址空间的一部分。通信结束时,解除共享内存到通信进程虚地址空间的映射。当所有进程不再需要共享内存时删除共享内存。

使用消息传递
消息是格式化的数据,在计算机网络中称报文。消息由消息头和消息体组成。

消息传递通信机制由信箱、发送原语(send)和接收原语(receive)组成。

  • 信箱:存放信件的存储区域,每个信箱可分成信箱头和信箱体两部分。信箱头:指出信箱容量、信件格式、信件位置指针等;信箱体:用来存放信件,可分成若干个区,每个区容纳一个信件。
  • 原语send(A,信件):若信箱未满,则把一封信件(消息)发送到信箱A,同时唤醒信件等待者进程,否则发送者阻塞。
  • 原语receive(A,信件):若信箱不空,则从信箱A接收一封信件(消息),同时唤醒等待发送者进程;否则接受者阻塞。

Linux消息队列通信机制

Linux消息队列通信机制属于消息传递通信机制。消息队列是内核地址空间中的内部链表,每个消息队列具有唯一的标识符。消息可以顺序地发送到队列中,并以几种不同的方式从队列中获取。

套接字通信

套接字(Socket)通信允许互联的位于不同计算机上的进程之间实现通信功能。套接字用于标识和定位特定计算机上特定进程的地址,以便数据准确传输给目标进程。

Socket一般包含三个参数:

  • 通信的目的IP地址
  • 使用的传输层协议(TCP或UDP)
  • 使用的端口号

Socket的连接过程为:

  1. 服务器监听:服务端套接字并不定位具体的客户端套接字,而是处于等待连接的状态(socket→linsten)
  2. 客户端请求:客户端套接字提出连接请求,连接服务器端套接字(socket→connect)
  3. 连接确认:服务器端套接字监听到客户端套接字的连接请求时,响应请求,建立一个新的线程,把服务器端套接字的信息发送给客户端,客户端确认后连接即可建立(accept→send/receive message→close)

死锁

操作系统中的死锁是指:如果在一个进程集合中的每个进程都在等待只能由该集合中的其它进程才能引发的事件,而无限期陷入僵持的局面称为死锁。

死锁产生的四个必要条件

  • 互斥条件:进程互斥使用资源,一旦某个资源被占用,欲使用该资源的进程必须等待
  • 占有和等待条件:进程申请新资源得不到满足而等待时,不释放已占有资源
  • 不剥夺条件:一个进程不能抢夺其它进程占有的资源
  • 循环等待条件:存在一组进程循环等待资源的现象。该条件是上述三个条件可能引发的结果,前三个都是必要条件

只要破坏这四个条件之一,死锁就可防止

死锁的防止

死锁防止通过破坏产生死锁的四个条件之一来实现

  • 破坏互斥条件:使资源可同时访问而不是互斥使用。该办法对于磁盘适用,对于磁带机、打印机等多数资源不仅不能破坏互斥使用条件,还要加以保证,因此一般不这么做。
  • 破坏占有和等待条件:静态分配可以破坏占有和等待条件。静态分配是指一个进程必须在执行前就申请它所需要的全部资源,并且直到它所需要的资源都得到满足后才开始执行。但是这样资源利用率低。
  • 破坏不剥夺条件:即采用剥夺式调度方法。当进程申请资源未获准许时,在等待前主动释放资源。剥夺调度方法目前只适用于内存资源和处理器资源。
  • 破坏循环等待条件:采用层次分配策略可以破坏循环等待条件。层次分配策略将资源被分成多个层次,进程按照由低到高的层次顺序申请和得到资源,按照由高到低的层次顺序释放资源。当进程得到某一层的一个资源后,如果需要申请该层的另一个资源,则必须先释放该层中的已占资源(即对于可能产生死锁的资源进行分层次管理确保其不会有两个进程互相拿到而不释放)。

死锁的避免
死锁避免方法允许系统中同时存在死锁的三个必要条件,即互斥、占有且等待和非抢占;
每当进程提出资源申请时,系统分析满足该资源请求时系统是否会发生死锁,若不会发生则实施分配,否则拒绝分配。

避免死锁的银行家算法

问题描述:

一个银行家拥有资金M,被N个客户共享,银行家对客户提出下列约束条件:

(1)每个客户必须预先说明自己所要求的最大资金量;

(2)每个客户每次提出部分资金量申请和获得分配;

(3)如果银行满足了客户对资金的最大需求量,则客户在资金运作后一定可以很快归还资金。

在上述问题中,银行家就是操作系统;资金就是系统资源;客户就是进程

银行家算法的步骤为:

(1)系统中的所有进程进入进程集合

(2)在安全状态下对进程请求的资源进行试探性分配

(3)系统用剩余的可用资源和进程集合中其它进程还要的资源数作比较,找到剩余资源能满足最大需求量的进程A,保证A运行完毕并归还全部资源

(4)把进程A从集合中去掉,相当于回收其资源。如果进程集合非空,则返回(2)

(5)若进程集合为空,则系统处于安全状态,可实施本次分配;否则,系统处于不安全状态,本次资源分配暂不实施,申请进程等待

银行家算法缺乏实用价值:很难在进程运行前知道其所需的资源最大量;同时算法要求系统中的进程必须是无关的,相互间没有同步要求;并且进程的个数和分配的资源数目应该是固定的。资源总是会动态变化的,预先分配的方案会导致资源利用率降低

死锁的检测

系统定时运行一个“死锁检测”程序,如果检测到系统发生了死锁,再采取措施解除它。

进行死锁检测时,系统维护一个进程-资源图,进程-资源分配图是描述进程和资源间申请与分配关系的一种有向图,由进程结点P、资源结点R和有向边组成,可用以检测系统是否处于死锁状态。

  • 请求边:从进程指向资源的有向边Pi→Rj为请求边,表示进程Pi申请资源类Rj中的一个资源。
  • 分配边:从资源指向进程的有向边Rj→Pi为分配边,表示Rj类中的一个资源已分配给进程Pi。
操作系统基础概念_第3张图片

死锁在逻辑上的判定可由下图说明(前提是进程间互相不孤立):

操作系统基础概念_第4张图片

死锁解除

(1)立即结束所有进程的执行,并重新启动操作系统。以前工作全部作废,损失可能很大。

(2)剥夺陷于死锁的进程占用的资源,但并不撤销它,直至死锁解除。

(3)撤销陷于死锁的所有进程,解除死锁继续运行。

(4)逐个撤销陷于死锁的进程,回收其资源,直至死锁解除。

(5)根据系统保存的检查点,使所有进程回退,直到足以解除死锁。

(6)当检测到死锁时,如果存在某些未卷入死锁的进程,且它们会进一步建立一些新的抑制进程能执行到结束,则它们可能释放足够的资源来解除死锁。

存储管理

即OS对于内存与外存的管理

基础概念

  • 地址重定位/地址变换:在执行程序时,将其中的逻辑地址转变为物理地址的过程。
  • 逻辑地址:逻辑地址是与程序在内存中的物理位置无关的访问地址。在执行对内存的访问之前必须把逻辑地址转换为物理地址。
  • 物理地址:物理地址是程序运行时中央处理器实际访问的内存单元地址。
  • 相对地址:相对地址是逻辑地址的一个特例,是相对于已知点(通常是程序的开始处)的存储单元的地址。

静态重定位与动态重定位

静态重定位:根据程序所装入的内存位置由装入程序依据重定位信息一次性将程序中所有的逻辑地址转变为物理地址,然后程序开始执行,这种重定位方式称为静态重定位。静态重定位无须硬件支持,易于实现,但静态重定位不允许程序在内存中移动位置。

动态重定位:地址转换工作穿插在指令执行的过程中,每执行一条指令,CPU对指令中涉及的逻辑地址进行转换,这种重定位方式称为动态重定位。动态重定位必须借助硬件地址转换机构实现,动态重定位允许程序在内存中移动位置。

存储保护

  • 地址越界保护:对进程执行时所产生的所有主存访问地址进行检查,确保进程仅访问自己的主存区。各道程序只能访问自己的主存区而不能跳转到另一个进程中,尤其不能访问操作系统的任何部分。
  • 信息存取保护:进程访问分配给自己的主存区时,系统要对访问权限进行检查,如检查是否允许读、写、执行等,从而确保数据的安全性和完整性,防止有意无意的误操作而破坏主存信息。

连续存储管理

连续存储管理对每个进程分配一个连续的存储区域。

固定分区存储管理

固定分区存储管理将内存空间划分为若干个位置和大小固定的连续区域,每一个连续区域称为一个分区,各分区大小可相同,也可不同。每当进程进来时都直接分配一个可用的分区给它。

缺点很明显:

(1)分区数目和大小在系统启动阶段已经确定,限制了系统中活动进程的数目,也限制了当前分区方案下可运行的最大进程。

(2)当分区长度大于其中进程长度时,造成存储空间浪费。

固定分区会产生内部碎片。内部碎片:由于进程所在分区大于进程大小而造成的分区内部浪费部分称为内部碎片。

可变(动态)分区存储管理

按照作业的大小划分分区,划分的时间、大小和位置都是动态的,属于一种动态分区方法。

每当进程进入时,系统根据当前内存状况动态分配一块分区给它。随着时间的推移,会产生很多外部碎片。外部碎片:**内存中的小到难以利用的分区,这种分区称为外部碎片。**可使用压缩技术合并外部碎片,但不相邻空闲分区的移动合并消耗CPU大量时间

伙伴系统
也称为buddy算法,是固定分区和可变分区折中的主存管理算法,由Knuth在1973年提出。伙伴系统采用称为伙伴的可以分割、合并的不同规格的内存块作为分区单位。利用二叉树结构进行内存分区的划分。

伙伴:两个大小相等且由同一个尺寸为2i的空闲块分割而来的内存块互为伙伴。

分配:运用伙伴系统分配内存空间的过程是一个对空闲内存区不断对半切分,直到切分出的内存块为大于或等于进程大小的最小伙伴为止的过程。

回收:伙伴系统回收内存的过程是不断将相邻空闲伙伴合并为更大伙伴单位,直到伙伴不空闲,无法合并为止的过程。

操作系统基础概念_第5张图片

分页存储管理

分页存储管理将全部内存划分为长度相等的若干份,每一份称为一个物理块或页框。

作业自动被分页系统划分为与每个物理块相等的若干等份,每一份称为一页或一个页面。

使用分页存储时,一个作业的任一页可以装入到内存任一空闲物理块,并不要求逻辑上相邻的页所在内存物理块也相邻。此时,逻辑地址结构=页号+页内位移;物理地址结构=物理块号+块内位移
若 页 面 大 小 设 置 为 4 K B , 地 址 总 线 宽 度 为 32 位 , 则 页 号 为 2 20 , 业 内 位 移 为 2 12 ( 4 K B ) 若页面大小设置为4KB,地址总线宽度为32位,则页号为2^{20},业内位移为2^{12}(4KB) 4KB线32220212(4KB)
由逻辑地址映射物理地址,物理块号=页号,物理块内位移=业内位移

操作系统需为每个作业建立一张页表,页表用于实现地址变换,页表记载了逻辑地址到物理地址的对应关系,系统通过页表可以准确访问内存中属于一个作业的所有页面。

操作系统基础概念_第6张图片

例题:若页面大小设置为4KB,地址总线宽度为32位,则页内位移位数为12位,页号位数=32-12=20,逻辑地址60000在第几页?页内偏移是多少?若该页被装进物理块1280中,则物理地址是多少?
60000 = ( 0000 E A 60 ) 16 , 16 进 制 中 , 前 5 位 为 页 号 , 后 3 位 为 页 偏 移 。 页 号 = ( E ) 16 , 页 内 偏 移 = ( A 60 ) 16 。 60000=(0000EA60)_{16},16进制中,前5位为页号,后3位为页偏移。页号=(E)_{16},页内偏移=(A60)_{16}。 60000=(0000EA60)16,1653=(E)16,=(A60)16

物 理 地 址 同 理 , 物 理 块 号 为 1280 = ( 500 ) 16 , 物 理 偏 移 = 页 偏 移 , 则 物 理 地 址 为 ( 00500 A 60 ) 16 物理地址同理,物理块号为1280=(500)_{16},物理偏移=页偏移,则物理地址为(00500A60)_{16} 1280=(500)16,=(00500A60)16

快表

引入快表的原因是CPU每存取一个指令/数据时,需要两次访问内存

操作系统基础概念_第7张图片

快表同样使用缓冲的思想以加快速度。为了减少分页存储管理系统中的内存访问速度下降一倍问题,在存储管理部件中增设一个专用的高速缓冲存储器,用来存放最近访问过的部分页表项,这种高速缓冲存储器称为快表或联想存储器。

有了快表,根据页号查找对应的物理块号时,首先查找快表中的局部页表,若找到则将物理块号和页内地址(也就是偏移地址)拼接形成物理地址,根据该物理地址访问相应的内存单元。若在快表中未找到物理块号,则再查找内存页表,获取物理块号一方面形成物理地址,另一方面将该表项抄到快表中,以备下次再次访问该页面时从快表中获得物理块号。查快表和查内存页表是同时进行的,一旦从快表中找到了对应项,则立即停止对内存页表的查找。

操作系统基础概念_第8张图片

多级页表

如果页表很大,页表占用的内存物理块也允许是离散的,而且页表也可以按需装入内存,这样就需要再建立页表的页表,即页目录,这就是二级页表机制。需要的还可建立更多级的页表。无论多少级页表顶级页表必须完全驻留内存。

在二级页表中,页目录表是一级页表,页表页是二级页表,其逻辑地址就变为:页目录号(一级页表号)+页号(二级页表号)+页内位移。其实就是个一对多、一对一的映射

分段存储管理

分段存储管理以段为单位进行存储分配,作业每一段被分配一个连续的主存空间,各段存储位置不一定相邻,各段大小不一。即将程序进行分段,由OS维护进程段表,其逻辑地址=段号+段内位移。

由段号查找进程段表,获取起始位,物理地址=起始位+偏移量(段内位移)

操作系统基础概念_第9张图片
若 段 长 最 大 为 16 K B , 计 算 机 地 址 总 线 为 32 位 , 则 段 号 最 多 2 18 , 段 内 位 移 最 多 2 14 若段长最大为16KB,计算机地址总线为32位,则段号最多2^{18},段内位移最多2^{14} 16KB线32218214
例题:若段长最大为16KB,计算机地址总线为32位,逻辑地址60000在第几段?段内偏移是多少?若该段被装进物理起始地址1280中,则该逻辑地址对应的物理地址是多少?
60000 = ( E A 60 ) 16 = ( 1110101001100000 ) 2 ( 前 导 0 省 略 ) 60000=(EA60)_{16}=(1110 1010 0110 0000)_2(前导0省略) 60000=(EA60)16=(1110101001100000)20
前18位为段号,即占第三段,后14位为段内偏移,即
10101001100000 = ( 2 A 60 ) 16 10 1010 0110 0000= (2A60)_{16} 10101001100000=(2A60)16
物理地址=物理起始地址(由段号查找段表而来)+段内偏移,即
1280 = ( 500 ) 16 , 物 理 地 址 = ( 500 ) 16 + ( 2 A 60 ) 16 = ( 2 F 60 ) 16 1280=(500)_{16},物理地址=(500)_{16}+(2A60)_{16}=(2F60)_{16} 1280=(500)16=(500)16+(2A60)16=(2F60)16
分段地址是二维的,即必须先将程序分段,分配段号与段偏移,再根据段号从段表中去查找物理起始地址,加上段偏移地址获得真正物理地址。

虚拟存储管理

也就是虚存管理。虚拟内存指加载进程进入内存时只加载进程运行所必须的几个块(页/段),需要时再进行I/O,以提供给用户看起来多的内存空间。以时间换空间,以较小的内存空间运行大的进程或较多的进程将消耗较多的进程对换时间。

局部性原理

在一段时间内,程序访问的存储空间仅限于某个区域(这称为空间局部性),或者最近访问过的程序代码和数据很快会再次被访问(这称为时间局部性)。局部性原理描述了进程中程序和数据的集簇倾向,表明虚拟内存方案是可行的。

虚拟内存的分页式管理

在进程开始运行之前,装入全部页面集合中的一个或几个页面,进程运行过程中,访问的页面不在内存时,再装入所需页面;若内存空间已满,而又需要装入新的页面时,则根据某种算法淘汰某个页面,以便装入新的页面。

缺页中断与普通中断的区别

(1)普通中断在两条指令之间才会响应;缺页中断涉及的指令在执行期间就需要响应缺页中断。
(2)当指令本身或者指令所处理的数据跨页时,在执行一条指令的过程中可能发生多次缺页中断。

缺页中断的处理过程

  1. 挂起请求缺页的进程
  2. 根据页号查外页表,找到该页存放的磁盘物理地址
  3. 查看主存是否有空闲页框,如有则找出一个,修改主存管理表和相应页表项内容,转步6
  4. 如主存中无空闲页框,则按替换算法选择淘汰页面,检查它是否曾被写过或修改过,没有修改则转步6
  5. 该淘汰页面被写过或修改过,则把它的内容写回磁盘原先位置
  6. 进行调页,把页面装入主存所分配的页框中,同时修改进程页表项
  7. 返回进程断点,重新启动被中断的指令

总的来说,处理过程为:①查看内存是否有空闲物理块,如有则装入页面到空闲物理块,同时修改页表相应项以及内存分配表②如果内存中没有空闲物理块,则按替换算法选择一个页面淘汰,若该页面被写过或修改过,则写回外存;否则只简单淘汰该页面。淘汰页面之后要修改页表相应项,然后调入页面到淘汰页面释放的物理块中

缺页中断率:对于进程P的一个长度为A的页面访问序列,如果进程P在运行中发生缺页中断的次数为F,则f = F/A称为缺页中断率。

抖动(颠簸):在请求分页虚拟存储管理系统中,刚被淘汰的页面立即又要访问,而调入不久即被淘汰,淘汰不久再被调入,如此反复,使得系统的页面调度非常频繁,以致大部分时间消耗在页面调度上,而不是执行计算任务,这种现象称为“抖动”(或者颠簸)。

虚拟内存的分段式管理

对于程序来说有两种分配方式:

  • 固定分配:固定分配使进程在生命周期中保持固定数目的页框(即物理块)。进程创建时,根据进程类型和程序员的要求决定页框数。
  • 可变分配:进程分得的页框数可变。进程执行的某阶段缺页率较高,说明目前局部性较差,系统可多分些页框以降低缺页率,反之说明进程目前的局部性较好,可减少分给进程的页框数

页面淘汰算法

(1)最佳页面淘汰算法(OPT)

调入一页而必须淘汰一个旧页时,所淘汰的页是以后不再访问的页或距现在最长时间后再访问的页。OPT是逻辑上的理想算法,实际上不可能实现。
OPT是可用于衡量各种具体算法的标准

(2)先进先出页面淘汰算法(FIFO)

先进先出页面淘汰算法总是淘汰最先调入主存的那一页,或者说在主存中驻留时间最长的那一页(常驻的除外)。

(3)最近最久未使用页面淘汰算法(LRU)

淘汰的页面是在最近一段时间里较久未被访问的那一页

维护一个栈,每当命中时就将命中的页放到栈尾,每当缺页时就替换掉栈顶的页并将换进来的页放到栈尾

(4)时钟页面替换算法

①页面首次装入主存时其“引用位”置1

②主存中的任何页面被访问时,“引用位”置1

③淘汰页面时,从指针当前指向的页面开始扫描循环队列,把遇到的“引用位”是1的页面的“引用位”清0,跳过这个页面;把所遇到的“引用位”是0的页面淘汰掉并换进来新的页,指针推进一步

④扫描完循环队列,若未淘汰任何页,则再次扫描

设备管理

基础概念

设备类别

从数据交换单位来分可分为块设备与字符设备

  • 块设备:块设备将信息存储在固定大小的块中,并且每个块都有地址,因此可独立寻址。所有传输以一个或多个完整的块为单位。硬盘、CD-ROM和USB盘是最常见的块设备。
  • 字符设备:字符设备以字节为单位发送或接收一个字符流,且不可寻址。键盘、打印机、鼠标、网络接口,以及大多数与磁盘不同的设备都可以看做字符设备。

从设备访问方式来分可分为顺序设备与随机设备:

  • 顺序设备:顺序设备上数据的逻辑顺序与物理存储顺序保持一致。
  • 随机设备:随机设备上数据的逻辑顺序与物理存储顺序可以不一致。

I/O控制方式

(1)轮询

轮询也称为忙等待,CPU向设备下达操作命令后,不断查询设备操作完成状态。在轮询控制方式下,CPU与设备的工作完全是串行(顺序)的,而不是并发的。

操作系统基础概念_第10张图片

(2)中断控制

中断控制下,CPU向设备发出读写命令后,不再查询设备执行状态,转而执行其它计算任务。当设备完成读写操作后以中断的方式主动向CPU报告完成情况。CPU响应中断执行一个中断处理程序,将设备从外界获得的数据取走放到内存或者相反。

操作系统基础概念_第11张图片

(3)DMA直接存储器访问

DMA(直接存储器访问)方式允许I/O设备与内存之间直接交换一个连续的信息块,在传输期间无需CPU的干预,而是由专用处理器–DMA控制器完成具体传输控制操作。

在开始DMA传输时,CPU对DMA芯片进行设置,说明需要传送的字节数、有关的设备和内存地址以及操作方向,接着启动DMA。当DMA芯片完成设备I/O时,引发一个中断。在控制设备传输期间,DMA挪用指令周期,控制总线,CPU暂时不能访问内存。周期挪用会减少CPU计算时间,但是,DMA对CPU时间的占用远少于轮询方式中的循环检测和中断方式中的中断处理程序执行开销。DMA将设备I/O中断CPU的频率由字节中断降低为块中断,即每传送一个连续的信息块才中断CPU一次。

操作系统基础概念_第12张图片

(4)通道

通道也叫输入输出处理器,是独立于CPU专门负责数据输入/输出传输工作的处理机,能执行自己的指令程序,代替CPU完成复杂的输入/输出操作,完成主存和外围设备间的信息传送,与CPU并行操作。

在通道方式下,当进程需要执行I/O操作时,CPU只需启动通道,即可返回执行其它进程,通道则执行通道程序,对I/O操作进行控制。

一个CPU可以连接若干通道,一个通道可以连接若干控制器,一个控制器可以连接若干台设备。

I/O通道与CPU的主要区别

(1)通道指令类型单一,主要局限于与I/O操作有关的指令。
(2)通道所执行的通道程序是放在主机内存中的,因此,通道与CPU共享内存。这样,通道与CPU对内存的使用是分时的。

I/O软件系统

所谓I/O软件系统,就是要对硬件设备进行分层抽象,将I/O软件组织成层次结构,低层软件屏蔽硬件细节,高层软件提供简洁、友好的界面。设计I/O软件系统时要考虑的主要问题有:

  • 设备无关性:屏蔽设备的具体细节,向高层提供抽象的逻辑设备,并完成逻辑设备和具体物理设备的映射
  • 出错处理:尽可能在接近硬件的层面处理错误。低层软件能够处理的硬件I/O错误不要让高层软件感知
  • 同步(阻塞)/异步(中断驱动)传输:进程在启动设备执行I/O操作后可继续执行其它工作,直至中断到达,称为异步传输;如果进程在启动设备后便被挂起,称为同步传输
  • 缓冲技术:建立数据缓冲区使数据的到达率和离去率相匹配

分为四个层次:用户空间的I/O软件、设备无关I/O软件、设备驱动程序、中断处理程序

设备驱动程序

每类设备控制器都是不同的,需要不同的软件进行控制。专门与控制器对话,发出命令并接收响应的软件称为设备驱动程序。

由于设备供多个进程共享,进程对设备的访问必须由操作系统仲裁,因此,设备驱动程序通常必须是操作系统内核的一部分。如果添加了一个新设备,该设备的驱动程序必须安装到操作系统内核中。

设备驱动程序的功能是接收设备无关软件的抽象读写请求,并监视请求的执行,以及设备初始化、对电源需求和日志事件进行管理。

设备驱动程序的典型工作过程如下为:驱动程序启动时要检查输入参数是否有效,若无效则返回错误信息,否则将抽象请求中的抽象参数转换为物理参数。接着,驱动程序检查设备当前是否正在使用。如果正在使用,请求被加入等待队列,否则启动设备开始处理请求。驱动程序依次将所需命令写入设备控制器寄存器,依次执行。

设备无关I/O软件

设备无关I/O软件的基本功能是执行所有设备公共的I/O功能,并向用户层软件提供统一的接口。主要功能包括:

  1. 为设备驱动程序提供统一接口,实现设备名到驱动程序的映射和设备保护功能
  2. 进行缓冲管理,缓冲使得数据传输成批进行,而不是按字或按字节进行,节约操作时间。
  3. 输出错误报告。I/O错误很常见,许多错误必须由适当的驱动程序来处理,设备无关软件提供错误处理框架。
  4. 分配与释放设备:某些设备必须互斥使用,例如CD-ROM刻录机。申请使用该类设备的进程可能会被拒绝或者排队等候。
  5. 提供与设备无关的块尺寸,不同磁盘的扇区大小可能不同,设备无关软件隐藏这一事实并向高层软件提供一个统一的块大小。

磁盘管理

磁盘由若干个涂有磁性介质的圆形盘面构成,经过低级格式化,每个盘面(包括正反两面)被划分成一系列同心圆,每个同心圆称为一个磁道,每个磁道再划分为若干等份,称为扇区,数据就以扇区为存储单位保存在磁盘上。扇区是磁盘访问的基本单位,访问扇区的某些字节意味着访问整个扇区。所有盘面上同等大小的磁道构成柱面

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XTNxKcax-1578144306486)(https://s2.ax1x.com/2020/01/04/l0Arsf.md.png)]

磁盘块物理地址:访问磁盘上的数据时需要给出物理块(扇区)的物理存储地址,该地址是三维的,采用(柱面号,磁头号,扇区号)表示。

磁盘块逻辑地址:磁盘物理块(扇区)的逻辑地址是一维线性的,称为逻辑块号(扇区号)。所有扇区依次排列,从0开始连续编号。扇区编排顺序是自外向里对各个柱面内的扇区排序,同一柱面内的各个扇区先按磁道顺序、再按磁道内扇区顺序排列。

访问扇区时,可以给出逻辑块号,系统自动将逻辑块号转换为(柱面号,磁头号,扇区号)构成的三维物理地址。

磁盘调度算法

读写一个磁盘块时,影响其访问的时间因素主要有三个方面:

  • 寻道时间:磁头移动到指定磁道所需时间
  • 旋转延迟时间:等待指定扇区到达磁头下的旋转时间
  • 数据传输时间:数据在磁盘与内存之间的传输时间

当多个磁盘I/O请求到来时,磁盘驱动程序需要安排I/O请求的处理顺序,这称为磁盘调度或移臂调度。

  • 先来先服务算法(FCFS):先来先服务算法根据磁道访问请求到来的先后顺序完成请求。
  • 最短寻道时间优先算法(SSTF):最短寻道时间优先算法总是优先满足距离磁头当前位置最近的访问请求。
  • 电梯调度算法:对于先后到达的磁盘访问请求,电梯调度算法首先选择移臂方向,磁臂在该方向上移动的过程中依次处理途经的各个访问请求,直到该方向上再无请求时,改变移臂方向,依次处理相反方向上遇到的各个请求。
  • 循环扫描算法(C-SCAN):在该算法中,磁头仅在一个移动方向上提供访问服务。磁臂从磁盘开始端柱面至结束端柱面移动的过程中依次处理途经请求,然后,直接返回开始端柱面重复进行,归途中并不响应请求。开始端与结束端柱面构成了一个循环。
  • N步扫描算法:N步扫描算法将磁盘请求队列分成若干个长度为N的子队列,磁盘调度按先来先服务算法依次处理这些队列。

虚拟设备

为了提高设备利用率,尤其是提高独占设备的利用率,减少作业周转时间,系统利用共享设备模拟独占设备的功能,使得独占设备成为能够共享的设备,这就是设备虚拟。SPOOLing(同时外围操作,假脱机技术)就是一种具体的设备虚拟技术。

SPOOLing是对脱机输入/输出系统的模拟。该技术利用一类物理设备模拟另一类物理设备,使独占设备变成可共享设备。

实例

在联机方式下,打印机正在打印一个作业时,其它打印请求必须等待。
在脱机方式下,进程的打印请求不是等待提交给打印机,而是提交到磁盘缓冲区中保存起来,进程即可返回执行其它计算任务。真正的打印动作待打印机空闲时,由专门的打印进程取出各个作业逐个打印。

当用户进程请求打印输出时,SPOOLing并不立即把打印机分配给它,只是做两件事:
(1)输出进程在输出井中为该进程申请一个空闲磁盘块区,将要打印的数据送入其中;
(2)输出进程再为用户进程申请一张空白的用户请求打印表,将用户的打印要求填入其中,再将该表挂到请求打印队列上。
打印机空闲时,输出进程从请求打印队列队首取出一张请求打印表,根据其中的要求将要打印的数据从输出井送到内存缓冲区,再由打印机打印。
打印完成后,SPOOLing再取出下一张请求打印表,打印下一组数据,直到请求打印队列为空时,输出进程阻塞;当有新的打印请求到来时被唤醒。

文件管理

文件与目录

文件:文件是记录在外存上具有名称的相关信息的集合。文件是对存储设备的抽象,它将以存储介质物理块为单位的信息存储单元抽象为以文件为单位的逻辑存储单元。完成物理到逻辑上的映射,从而向用户提供一个简单的外存信息访问接口。

文件属性

  • 基本属性:包括文件名和扩展名、标识符、文件属主ID、文件所属组ID等
  • 类型属性:表明文件类型。如普通文件、目录文件、系统文件、隐式文件、设备文件、pipe文件、 socket文件等;ASCII码文件、二进制文件等
  • 控制属性:包括文件的位置信息、逻辑记录长、文件当前长、文件最大长、关键字位置、关键字长度、文件打开次数等。
  • 管理属性:包括文件创建时间、最后访问时间、最后修改时间等,用于保护、安全和使用跟踪。
  • 保护属性:包括用户对文件允许执行的访问操作,如可读、可写、可执行、可更新、可删除等;上锁标志和解锁标志;口令等;许可访问者。

文件内容访问方式

  • 顺序访问:顺序访问从文件开头顺序读取文件的全部字节或记录,不能跳过某一些内容,文件后面的内容不能先于文件前面部分的内容读取出来。写入与此类似。后面的访问起点依赖于前面访问后确定的文件指针位置。
  • 随机访问(直接访问):能够以任意次序读取其中字节或记录的文件称为随机存取文件或直接访问文件。数据库系统使用的文件往往属于随机存取文件。磁盘文件可以直接访问,因为磁盘访问可以指定物理块地址。
  • 索引访问:索引访问建立在直接访问方式上。索引访问需要为文件创建索引,这样的文件称为索引文件。索引类似文件内容目录,包含指向各内容块的指针。查找索引文件时,首先查找索引块,获得目标内容块的指针,再从目标内容块中找到所需记录。

目录:目录是查找文件的实体,用于帮助查找文件

目录提供了访问文件的入口,每个文件在目录表中都有一个目录项(文件控制块FCB),目录项用于记载文件的属性信息,如名称、位置、大小和类型等

全部目录项也可构成文件,称为目录文件。目录文件非空,至少包含当前目录项和父目录项。文件目录的作用是将逻辑名转换为物理名,即将文件名转换为文件的磁盘地址。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eQOGuSQ6-1578144306487)(https://s2.ax1x.com/2020/01/04/l0m1bV.md.png)]

层次目录系统允许用户创建多级目录,各级目录形成树型结构,也称为目录树。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J99CU54D-1578144306488)(https://s2.ax1x.com/2020/01/04/l0mfKI.md.png)]

文件结构

文件逻辑结构

文件的逻辑结构是从用户角度看到的反映一定逻辑意义的文件内容组成单位及各部分之间的关系。

  • 流式文件(无结构文件):流式文件将文件内容看做字节流,即整个文件由一个字节流组成。这种看法忽略了可能存在的文件内在的逻辑结构,实际上将文件看成是无结构的。大多数现代操作系统对用户仅仅提供流式文件。
  • 记录式文件(有结构文件):记录式文件是一种有结构的文件,包含若干逻辑记录。逻辑记录是文件中按信息的逻辑含意划分的信息单位。记录式文件是数据库管理系统支持的基本文件类型

文件物理结构

文件的物理结构是指文件内容及其各部分之间逻辑关系在物理存储空间中的存储和实现方法。这时文件看作物理文件,是相关物理块的集合。

构造文件物理结构的实质是建立逻辑记录与其物理存储位置之间的对应关系。主要有两种方法:

  • 计算法:利用哈希函数等设计映射算法,建立逻辑记录到物理记录地址之间的映射关系,以记录键的计算结果作为对应的物理块地址。直接寻址文件、计算寻址文件、顺序文件均采用计算法构造。
  • 指针法(链表法):指针法以链表形式反映各记录的物理地址及各记录之间的逻辑顺序关系。索引文件、索引顺序文件、连接文件等均采用指针法构造。

文件空间管理

内存管理已经说过了,此处的文件空间管理主要是指对文件外存(即磁盘块)的管理。

文件空间管理不仅需要分配并记录存放文件的磁盘块,还需要记录空闲磁盘块的情况。

文件空间分配方法

实际上就是给文件分配磁盘块的方法

(1)连续分配

给文件分配一个连续的物理块,OS需要登记各个空闲区的位置和大小以供分配使用。因此前提是OS必须事先知道文件长度才能分配,同时对文件的修改、插入、增加会变得困难,适用于已知长度且不需要经常修改的情况,如光盘刻录。

(2)链接分配

给文件分配物理上不连续的物理块,但是每个物理块有一个指向下一块的指针,因此逻辑上文件是连续的(实际上就是链表了)

(3)索引分配

为每个文件都建立一张索引表,也就是一个map,文件包含索引块地址,由索引块地址找到索引表,索引表内顺序存储了该文件的物理块地址

(4)多级索引分配

当索引块很多时,索引表也变得较大,因此使用多级索引分配。

在顶级索引表中,前几项直接指向物理块,称为直接寻址;后几项划为多个部分,分别是一级、二级、三级……等多级索引,一级索引项指向一个新的索引表,在该索引表中的索引项直接指向物理块,称为一次间接寻址;二级索引项指向一个二级索引表,该索引表的索引项又指向一个索引表,这个索引表的索引项才指向一个实际的物理块,称为二次间接寻址……以此类推

文件外存管理

也就是要记录空闲块的数量和位置

  • 位示图(位向量或字位映象表):系统中的每个盘块采用一个二进制位(0或1)表示该盘块是否空闲。分配和回收时只需修改二进制位值即可。
  • 空闲块链:空闲块链方法将所有空闲块链接在一起,形成一个链表。
  • 空闲区表:一个空闲区由若干位置连续的空闲盘块构成。空闲区表为外存上的所有空闲区建立一张空闲表,每个空闲区占一个表项,表项内容包括空闲块位置和连续空闲的块数。
  • 成组空闲块链:将空闲盘快分成固定的组进行管理。举个例子:UNIX/Linux将系统中的所有空闲盘块分成若干组,每组100个盘块,每组第一块登记下一组空闲块的盘物理块号和空闲总数,形成成组空闲块链。访问成组空闲块链进行分配操作时,每次仅需装入其中的一组到内存,待该组中登记的盘块分配完毕后,再从外存装入下一组空闲盘块列表到内存。回收空闲盘块时,将空闲盘块号登记在位于内存的空闲盘块列表中,仅当该表满(达到100)时,将其加入成组空闲块链中并写回外存,然后在内存中建立一个新的盘块组并加入成组空闲块链中。

操作系统基础概念_第13张图片

当分配49个空闲块出去时:

操作系统基础概念_第14张图片

此时要是再分配一个空闲块则先将300#的内容复制到专用管理块filesys中,然后再将300#分配出去

操作系统基础概念_第15张图片

问题

1.进程轮流使用处理器与多个打印任务共享打印机的区别。

进程轮流使用处理器属于同时共享、资源分时复用,也叫并发共享,并发进程释放处理器时,其任务通常并未结束。多个进程可以同时使用处理器。
多个打印任务共享打印机属于资源的互斥共享,也叫顺序共享,在一个打印任务结束之前,另一个打印任务不能开始。即多个打印任务不能同时使用打印机。

2.资源虚化与资源抽象的区别

(1)资源虚化强调从逻辑上扩充资源数量。
(2)资源抽象强调封装、隐藏软硬件内部细节,简化资源应用接口。

3.并发性、并行性与顺序性的区别

(1)并发性是指多个事件或活动在同一时间段内(一个时间段)开始或者说发生,注意不是完成或者结束。
(2)并行性是指多个事件或活动在同一时刻(一个时间点)开始或者说发生,注意不是完成或者结束。
(3)顺序性是指一个事件或活动结束前,另一个事件或活动不可以开始。在任何一个时刻或者时间段内,只会有一个事件或活动在进行。其它事件或活动要么已经结束,要么尚未开始。

4.说明特权指令、处理器状态、程序状态字寄存器之间的关系

(1)处理器指令系统分为特权指令和非特权指令,处理器状态分为用户态和核心态。程序状态字寄存器记录有处理器状态信息。
(2)特权指令在核心态下执行,中断是处理器由用户态切换到核心态的唯一途径。特权指令与系统共享资源的分配和使用相关。当多个进程竞争公共资源时,其仲裁权在于操作系统。
因此,特权指令的执行权在于操作系统。操作系统向用户提供了大量为其使用公共资源服务的系统调用处理程序。用户程序通过执行访管中断可以委托操作系统为其提供公共资源服务。
在用户态下执行特权指令是非法的,引起中断后,系统会捕捉到这种非法操作并中止其执行。

5.剥夺调度与非剥夺调度和中断的关系

(1)剥夺调度:在进程自身未出现等待事件的情况下,由于时间片用完或者出现了优先级更高的进程而被迫让出处理器。
(2)非剥夺调度:一旦获得处理器,只有进程自身出现等待事件时才会让出处理器。
(3)无论剥夺调度还是非剥夺调度,进程都有可能出现等待事件而中断,让出处理器。

6.虚拟存储器大小有什么限制?

虚拟存储器是利用外存的一部分(对换区),通过在内外存之间对换进程,从逻辑上扩充内存容量。
外存容量远大于内存容量,但能够用来扩充内存容量的外存仅是其中的一小部分,因为虚拟存储器的容量受地址总线宽度的制约。地址总线宽度决定了程序可访问的最大地址空间,超过该空间的存储区或者说地址是无法访问的。
虚存受到地址总线宽度的制约,因此即使理论上虚存可以无限大,但是实际上同时最多能用的也还是内存大小,只不过使用虚存可以通过I/O来回换数据,速度将低了很多

7.DMA与中断的区别

DMA 传送方式与程序中断对CPU的干扰程度不同。程序中断请求不但使CPU停下来,而且还要CPU执行中断服务程序,需要完成断点和处理器现场保护和CPU与外设之间的数据传送工作,消耗CPU较多时间。
DMA请求仅仅使CPU暂停一下,该暂停不属于中断,不需要执行中断处理程序,不需保存断点和处理器现场信息。DMA挪用CPU指令周期控制外设与主存之间的数据传送,无需CPU的干预。
DMA与CPU交替控制总线,交替执行指令和数据传输工作。在整个数据块传输结束时,DMA才向CPU发中断信号,CPU执行中断处理程序。

8.SPOOLing系统的虚拟设备原理

SPOOLing系统即假脱机系统。将原来以联机方式使用的独占设备改造成为脱机使用的虚拟共享设备。
在联机方式下,每个需要设备I/O的进程排队等待向设备提交输入输出作业。
在SPOOLing系统中,每个需要设备I/O的进程并不直接向设备提交输入输出作业,也不控制设备操作,而是向SPOOLing系统提交输入输出作业,SPOOLing系统再向设备提交作业。SPOOLing系统类似于批处理系统中的操作员。

9.虚拟文件系统的原理是什么,体现了什么资源管理技术思想?

(1)虚拟文件系统(VFS)定义了一个代表不特定文件系统通用特征和行为的文件模型。
VFS抽象出所有文件系统的公共部分,形成一个简单、统一的抽象文件系统接口提供给用户。
用户仅通过抽象文件系统接口层表达文件操作意图,文件操作的具体执行则由底层的实际文件系统来完成。
从抽象文件系统到某一具体文件系统的转换工作由映射模块完成。
(2)虚拟文件系统体现了资源抽象的资源管理技术思想。不同文件系统有着不同的实现方法和细节,虚拟文件系统则封装、隐藏了各种具体文件系统的细节差异,使得用户仅需面对一种统一的、抽象的文件系统,即虚拟文件系统,简化了用户对文件系统的学习和使用。

参考

《操作系统原理与Linux实践教程》申丰山 王黎明 著

《操作系统——精髓与设计原理》William Stallings 著

你可能感兴趣的:(基础知识)