鉴于有人需要离线版的PDF文档,这里给出本文章的PDF版本,下载地址如下:https://pan.itnxd.cn/123Pan/csdn-share/operating-system.pdf
操作系统(Operating System,OS):是指控制和管理整个计算机系统的硬件和软件资源,并合理地组织调度计算机的工作和资源的分配,以提供给用户和其他软件方便的接口和环境,它是计算机系统中最基本的系统软件。
命令接口: 允许用户直接使用
程序接口: 允许用户通过程序间接使用
由一组系统调用组成(程序接口=系统调用)
系统调用 = 系统调用命令 = 广义指令
GUI: 现代操作系统中最流行的图形用户接口
需要提供的功能和目标:实现对硬件机器的拓展
没有任何软件支持的计算机称为裸机。在裸机上安装的操作系统,可以提供资源管理功能和方便用户的服务功能,将裸机改造成功能更强、使用更方便的机器。
通常把覆盖了软件的机器称为扩充机器,又称之为虚拟机。
并发:指两个或多个事件在同一时间间隔内发生。这些事件宏观上是同时发生的,但微观上是交替发生的。
并行:指两个或多个事件在同一时刻同时发生。
操作系统的并发性指计算机系统中同时存在着多个运行着的程序。
一个单核处理机(CPU)同一时刻只能执行一个程序,因此操作系统会负责协调多个程序交替执行(这些程序微观上是交替执行的,但宏观上看起来就像是在同时执行)
事实上,操作系统就是伴随着“多道程序技术”而出现的。因此,操作系统和程序并发是一起诞生的。
共享:即资源共享,是指系统中的资源可供内存中多个并发执行的进程共同使用。
所谓的“同时”往往是宏观上的,而在微观上,这些进程可能是交替地对该资源进行访问的(即分时共享)
并发和共享的关系(互为存在条件)
通过上述例子来看并发与共享的关系:
使用QQ发送文件A,同时使用微信发送文件B。
1、两个进程正在并发执行(并发性)
如果失去并发性,则系统中只有一个程序正在运行,则共享性失去存在的意义
2、需要共享地访问硬盘资源(共享性)
如果失去共享性,则QQ和微信不能同时访问硬盘资源,就无法实现同时发送文件,也就无法并发。
虚拟:指把一个物理上的实体变为若干个逻辑上的对应物。物理实体(前者)是实际存在的,而逻辑上对应物(后者)是用户感受到的。
虚拟技术:用于实现虚拟的技术
虚拟处理器(CPU):通过多道程序设计技术,采用让多道程序并发执行的方法,分时来使用一个CPU,实际物理上只有一个CPU,但是用户感觉到有多个CPU
虚拟存储器:从逻辑上扩充存储器容量,用户感觉到的但实际不存在的存储器
虚拟设备:将一台物理设备虚拟为逻辑上的多台设备,使多个用户在同一时间段内访问同一台设备,即同时共享,用户宏观上感觉是同时的,但实际上是微
观交替访问同一台设备的
虚拟技术
没有并发,就探不上虚拟性
异步: 指在多道程序环境下,允许多个程序并发执行,但由于资源有限,进程的执行不是一贯到底的,而是走走停停,以不可预知的速度向前推进,这就是进程的异步性。
显然,如果失去了并发性,则系统只能串行地处理各个进程,每个进程地执行会一贯到底。只有系统拥有并发性,才有可能导致异步性。
没有并发和共享,就谈不上虚拟和异步,因此并发和共享是操作系统的两个最基本的特征。
过程: 用户把程序写在纸带上(其实就是在纸带上打孔),然后输入到计算机中,计算机随后会处理这个程序,把输出结果又放在纸带中(其实还是打孔),展示给用户看。
由于用户在纸带上编写程序的速度很慢,纸带输入输出的速度也很慢,而计算机的处理速度快,所以系统资源的利用率极低。
主要缺点:用户独占全机、人机速度矛盾导致资源利用率极低
引入脱机输入/输出技术(用磁带完成),并使用监督程序(操作系统的雏形)负责控制作业的输入、输出。
由于磁带录入到处理器中的速度比纸带快得多,所以单道批处理系统一定程序上缓和了人机速度矛盾,资源利用率有所提升。
主要优点: 缓解了一定程度的人机速度矛盾,资源利用率有所提升。
主要缺点: 内存中仅能有一道程序运行,只有该程序运行结束之后才能调入下一道程序。CPU有大量的时间是在空闲等待I/O完成。资源利用率依然很低。
每次往内存中输入多道程序,操作系统正式诞生,并引入了中断技术,由操作系统负责管理这些程序的运行。各个程序并发执行。
主要优点: 多道程序并发执行,共享计算机资源。资源利用率大幅提升,CPU和其他资源保持“忙碌”状态,系统吞吐量增大。
主要缺点: 用户响应时间长,没有人机交互功能(用户提交自己的作业之后就只能等待计算机处理完成,中间不能控制自己的作业执行)
计算机以时间片为单位轮流为各个用户/作业服务,各个用户可通过终端与计算机进行交互。
主要优点: 用户请求可以被即时响应,解决了人机交互问题。允许多个用户同时使用一台计算机,并且用户对计算机的操作相互独立,感受不到别人的存在。
主要缺点: 不能优先处理一些紧急任务。操作系统对各个用户/作业都是完全公平的,循环地为每个用户/作业服务一个时间片,不区分任务的紧急性。
在实时操作系统的控制下,计算机系统接收到外部信号后及时进行处理,并且要在严格的时限内处理完事件。实时操作系统的主要特点是及时性和可靠性。
主要优点: 能够优先响应一些紧急任务,某些紧急任务不需时间片排队。
网络操作系统
工作方式:把计算机网络中的各台计算机有机的结合起来,提供统一、经济而有效的使用各台计算机的方法、实现各台计算机之间的数据互相传送
特点:有主从关系、网络中资源共亨、网络中的计算机通过协议通信
分布式操作系统
工作方式︰系统中的各计算机相互协同并行完成同一任务
特征
系统中任意两台计算机通过通信方式交换信息
系统中的计算机都具有同等地位,无主从关系
每台计算机上的资源为所有用户共享
系统中的任意台计算机都可以构成一个子系统,并且还能重构
任何工作都可以分布在几台计算机上、由他们并行工作、协同完成
特点:分布性和并行性
嵌入式操作系统
固话在硬件里面的系统、比如手机、路由器等
特点
个人计算机操作系统
常见的有Windows、Linux、Macintosh等
指令:就是处理器(CPU)能识别、执行的最基本命令
CPU如何判断当前是否可以执行特权指令?
用程序状态寄存器(PSW)中的某标志位来标识当前处理器处于什么状态,如0为用户态,1为核心态。
内核:计算机上配置的底层软件,是操作系统最基本、最核心的部分。
实现操作系统内核功能的那些程序就是内核程序。
时钟管理: 实现计时功能
中断处理: 负责实现中断机制
原语
对系统资源进行管理的功能 (有的操作系统不把这部分功能归为 “ 内核功能 ”。也就是说不同的操作系统,对内核功能的划分可能并不一样)
早期的计算机:各程序只能串行执行,系统资源利用率低
为了解决上述问题,人们发明了操作系统(作为计算机的管理者),引入中断机制,实现了多道程序并发执行。
本质:发生中断就意味着需要操作系统介入,开展工作。
1、当中断发生时,CPU立即进入核心态
2、当中断发生后,当前运行的进程暂停运行,并由操作系统内核对中断进行处理
3、对于不同的中断信号,会进行不同的处理
由于操作系统的管理工作(比如进程切换、分配I/O设备等)需要使用特权指令,因此CPU要从用户态转为核心态。中断可以使CPU从用户态切换为核心态,使操作系统获得计算机的控制权。有了中断,才能实现多道程序并发执行。
用户态、核心态之间的切换是怎么实现的?
用户态和核心态就是程序状态寄存器0或1标志的,因此核心态到用户态可以通过特权指令进行设置!
分类二
外中断处理过程
更详细过程
系统调用: 是操作系统提供给应用程序(程序员/编程人员)使用的接口,可以理解为一种可供应用程序调用的特殊函数,应用程序可以发出系统调用请求来获得操作系统的服务。
功能: 应用程序通过系统调用全球操作系统的服务。系统中的各种共享资源都由操作系统统一掌管,因此在用户程序中,凡是与资源有关的操作(如存储分配、I/O操作、文件管理等),都必须通过系统调用的方式向操作系统提出服务请求,由操作系统代为完成。这样可以保证系统的稳定性和安全性,防止用户进行非法操作。
系统调用相关处理涉及到对系统资源的管理、对进程的控制,这些功能需要执行一些特权指令才能完成,因此系统调用的线管处理需要在核心态下进行。
程序:就是一个指令序列
PCB: 系统为每个运行的程序配置一个数据结构,称为进程控制块(PCB),用来描述进程的各种信息(如代码存放位置)
PCB、程序段、数据段三部分构成了进程实体(进程映像)。一般情况下,我们把进程实体简称为进程,例如,所谓创建进程,实质上是创建进程实体中的PCB;而撤销进程,实质上是撤销进程实体中的PCB。PCB是进程存在的唯一标志!
定义: 强调动态性
进程是进程实体的运行过程,是系统进行资源分配和调度的一个独立单位。
严格来说,进程实体和进程不一样,进程实体是静态的,进程则是动态的。
进程(进程实体)由程序段、数据段、PCB三部分组成。
进程的组成讨论的是一个进程内部由哪些部分构成的问题,而进程的组织讨论的是多个进程之间的组织方式问题。
进程的组织方式:
进程是程序的一次执行过程。在这个执行过从中,有时进程正在被CPU处理,有时又需要等待CPU服务,可见 ,进程的状态是会有各种变化。为了方便对各个进程的管理,操作系统需要将进程合理地划分为几种状态。
三种基本状态
运行态(Running):
就绪态(Ready):
阻塞态(Waiting/Blocked,又称:等待态):
另外两种状态:
创建态(New,又称:新建态)
终止态(Terminated,又称:结束态)
根据以上即可知道处于哪个状态!
进程控制的主要功能是对系统中的所有进程实施有效的管理,它具有创建新进程、撤销已有进程、实现进程状态转换等功能。
如何实现进程控制?
用原语实现进程控制。原语的特点是执行期间不允许中断,只能一气呵成。
这种不可被中断的操作即原子操作。
原语采用“关中断指令”和“开中断指令”实现
显然,关/开中断指令的权限非常大,必然只允许在核心态下执行的特权指令。
进程通信是指进程之间的信息交换。PV操作是低级通信方式,高级通信方式是指以较高的程效率传输大量数据的通信方式。
进程是分配系统资源的单位(包括内存地址空间),因此各进程拥有的内存地址空间相互独立。
为了保证安全,一个进程不能直接访问另一个进程的地址空间。
但是进程之间的信息交换又是必须实现的。为了保证进程间的安全通信,操作系统提供了一些方法。
两个进程对共享空间的访问必须是互斥的(互斥访问通过操作系统提供的工具实现)。
操作系统只负责提供共享空间和同步互斥工具(如P、v操作)
共享存储
“管道”是指用于连接读写进程的一个共享文件,又名pipe文件。其实就是在内存中开辟一个大小固定的缓冲区
进程间的数据交换以格式化的消息(Message)为单位。进程通过操作系统提供的“发送消息 / 接收消息”两个原语进行数据交换。
消息
消息传递方式
有的进程可能需要“同时”做很多事,而传统的进程只能串行地执行一系列程序。为此,引入了“线程”,来增加并发度。
可以把线程理解为“轻量级进程”。
线程是一个基本的CPU执行单元,也是程序执行流的最小单位。
引入线程之后,不仅是进程之间可以并发,进程内的各线程之间也可以并发,从而进一步提升了系统的并发度,使得一个进程内也可以并发处理各种任务(如QQ视频、文字聊天、传文件)
引入线程后,进程只作为除CPU之外的系统资源的分配单元(如打印机、内存地址空间等都是分配给进程的)。
引入线程带来的变化
资源分配、调度
并发性
系统开销
用户级线程
内核级线程
在同时支持用户级线程和内核级线程的系统中,可采用二者组合的方式:将n个用户级线程映射到m个内核级线程上 ( n >= m)
在同时支持用户级线程和内核级线程的系统中,由几个用户级线程映射到几个内核级线程的问题引出了“多线程模型”问题。
多对一模型:多个用户及线程映射到一个内核级线程。每个用户进程只对应一个内核级线程。
一对一模型:一个用户及线程映射到一个内核级线程。每个用户进程有与用户级线程同数量的内核级线程。
多对多模型:n用户及线程映射到m个内核级线程(n >= m) 。每个用户进程对应m个内核级线程。
当有一堆任务要处理,但由于资源有限,这些事情没法同时处理。这就需要确定某种规则来决定处理这些任务的顺序,这就是“调度”研究的问题。
在多道程序系统中,进程的数量往往是多于处理机的个数的,这样不可能同时并行地处理各个进程。处理机调度,就是从就绪队列中按照一定的算法选择一个进程并将处理机分配给它运行,以实现进程的并发执行。
由于内存空间有限,有时无法将用户提交的作业全部放入内存,因此就需要确定某种规则来决定将作业调入内存的顺序。
高级调度(作业调度):按一定的原则从外存上处于后备队列的作业中挑选一个(或多个)作业,给他们分配内存等必要资源,并建立相应的进程(建立PCB),以使它(们)获得竞争处理机的权利。
高级调度是辅存(外存)与内存之间的调度。每个作业只调入一次,调出一次。作业调入时会建立相应的PCB,作业调出时才撤销PCB。高级调度主要是指调入的问题,因为只有调入的时机需要操作系统来确定,但调出的时机必然是作业运行结束才调出。
引入了虚拟存储技术之后,可将暂时不能运行的进程调至外存等待。等它重新具备了运行条件且内存又稍有空闲时,再重新调入内存。
这么做的目的是为了提高内存利用率和系统吞吐量。
暂时调到外存等待的进程状态为挂起态。值得注意的是,PCB并不会一起调到外存,而是会常驻内存。PCB中会记录进程数据在外存中的存放位置,进程状态等信息,操作系统通过内存中的PCB来保持对各个进程的监控、管理。被挂起的进程PCB会被放到的挂起队列中。
中级调度(内存调度):就是要决定将哪个处于挂起状态的进程重新调入内存。
一个进程可能会被多次调出、调入内存,因此中级调度发生的频率要比高级调度更高。
低级调度(进程调度):其主要任务是按照某种方法和策略从就绪队列中选取一个进程,将处理机分配给它。
进程调度是操作系统中最基本的一种调度,在一般的操作系统中都必须配置进程调度。
进程调度的频率很高,一般几十毫秒一次。
暂时调到外存等待的进程状态为挂起态
挂起态又可以进一步细分为就绪挂起、阻塞挂起两种状态
五状态模型 升级为 七状态模型
注意“挂起”和“阻塞”的区别
两种就绪挂起 状态都是暂时不能获得CPU的服务,但挂起态是将进程映像调到外存去了,而阻塞态下进程映像还在内存中。
有的操作系统会把就绪挂起、阻塞挂起分为两个挂起队列,甚至会根据阻塞原因不同再把阻塞挂起进程进一步细分为阻塞挂起 多个队列。
需要进行进程调度与切换的情况
不能进行进程调度与切换的情况
临界资源:一个时间段内只允许一个进程使用的资源。各进程需要互斥地访问临界资源。
临界区:访问临界资源的那段代码。
“狭义的进程调度”与“进程切换”的区别:
狭义的进程调度指的是从就绪队列中选中一个要运行的进程。(这个进程可以是刚刚被暂停执行的进程,也可能是另一个进程,后一种情况就需要进程切换)
进程切换是指一个进程让出处理机,由另一个进程占用处理机的过程。
过程
广义的进程调度包含了选择一个进程和进程切换两个步骤。
进程切换的过程主要完成了:
注意:进程切换是有代价的,因此如果过于频繁的进行进程调度、切换,必然会使整个系统的效率降低,使系统大部分时间都花在了进程切换上,而真正用于执行进程的时间减少。
非剥夺调度方式,又称非抢占方式。即,只允许进程主动放弃处理机。在运行过程中即便有更紧迫的任务到达,当前进程依然会继续使用处理机,直到该进程终止或主动要求进入阻塞态。
剥夺调度方式,又称抢占方式。当一个进程正在处理机上执行时,如果有一个更重要或更紧迫的进程需要使用处理机,则立即暂停正在执行的进程,将处理机分配给更重要紧迫的那个进程。
CPU利用率:指CPU “忙碌”的时间占总时间的比例。
利用率 = 忙碌的时间 / 总时间
系统吞吐量:单位时间内完成作业的数量
系统吞吐量 = 总共完成了多少道作业 / 总共花了多少时间
周转时间,是指从作业被提交给系统开始,到作业完成为止的这段时间间隔。
它包括四个部分:
后三项在一个作业的整个处理过程中,可能发生多次
带权周转时间与周转时间都是越小越好
等待时间,指进程 / 作业处于等待处理机状态时间之和,等待时间越长,用户满意度越低。
无I/O处理:等待时间 = 周转时间 - 运行时间
有I/O处理:等待时间 = 周转时间 - 运行时间 - I/O处理时间
一个作业总共需要被CPU服务多久,被I/O设备服务多久一般是确定不变的,因此调度算法其实只会影响作业 / 进程的等待时间。当然,与前面指标类似,也有“平均等待时间”来评价整体性能。
响应时间,指从用户提交请求到首次产生响应所用的时间。
公平
按照作业 / 进程到达的先后顺序进行服务
用于作业 / 进程调度:用于作业调度时,考虑的是哪个作业先到达后备队列;用于进程调度时,考虑的是哪个进程先到达就绪队列
非抢占式的算法
优点:公平、算法实现简单
缺点:排在长作业(进程)后面的短作业需要等待很长时间,带权周转时间很大,对短作业来说用户体验不好。即,FCFS算法对长作业有利,对短作业不利
不会产生饥饿现象
算法思想:追求最少的平均等待时间,最少的平均周转时间、最少的平均平均带权周转时间(选择当前已到达的时间最短的执行)
算法规则: 最短的作业 / 进程优先得到服务(所谓“最短”,是指要求服务时间最短)
**即可用于作业调度,也可用于进程调度。**用于进程调度时称为“短进程优先(SPF, Shortest Process First)算法”
SJF和SPF是非抢占式的算法。但是也有抢占式的版本——最短剩余时间优先算法(SRTN, Shortest Remaining Time Next)(新到达的进程的剩余时间比当前运行的进程剩余时间更短,新进程抢占处理机)
优点:“最短的”平均等待时间、平均周转时间(抢占式的SRTN更短)
缺点:不公平。对短作业有利,对长作业不利。可能产生饥饿现象。另外,作业/进程的运行时间是由用户提供的,并不一定真实,不一定能做到真正的短作业优先
会产生饥饿。如果源源不断地有短作业/进程到来,可能使长作业/进程长时间得不到服务,产生“饥饿”现象。如果一直得不到服务.川称为“饿死”
算法思想:要综合考虑作业/进程的等待时间和要求服务的时间
算法规则:在每次调度时先计算各个作业/进程的响应比,选择响应比最高的作业/进程为其服务
响应比= 等待时间 + 要求服务时间 / 要求服务时间
即可用于作业调度,也可用于进程调度
非抢占式的算法。因此只有当前运行的作业/进程主动放弃处理机时,才需要调度,才需要计算响应比
优缺点:
不会产生饥饿
算法思想:公平地、轮流地为各个进程服务,让每个进程在一定时间间隔内都可以得到响应
算法规则:按照各进程到达就绪队列的顺序,轮流让各个进程执行一个时间片(如100ms)。若进程未在一个时间片内执行完,则剥夺处理机,将进程重新放到就绪队列队尾重新排队。
用于进程调度(只有作业放入内存建立了相应的进程后,才能被分配处理机时间片)
若进程未能在时间片内运行完,将被强行剥夺处理机使用权,因此时间片轮转调度算法属于抢占式的算法。由时钟装置发出时钟中断来通知CPU时间片已到
优点:公平;响应快,适用于分时操作系统;
缺点:由于高频率的进程切换,因此有一定开销,不区分任务的紧急程度。时间片太大会退化为FCFS算法,太小进程切换开销太大!
不会产生饥饿
算法思想:随着计算机的发展,特别是实时操作系统的出现,越来越多的应用场景需要根据任务的紧急程度来决定处理顺序
算法规则:每个作业/进程有各自的优先级,调度时选择优先级最高的作业/进程
既可用于作业调度,也可用于进程调度。甚至,还会用于在之后会学习的I/O调度中
抢占式、非抢占式都有。做题时的区别在于:非抢占式只需在进程主动放弃处理机时进行调度即可,而抢占式还需在就绪队列变化时,检查是否会发生抢占。(即新到达的是否比正在运行的优先级要高,高则抢占处理机)
优点:用优先级区分紧急程度、重要程度,适用于实时操作系统。可灵活地调整对各种作业/进程的偏好程度。
缺点:若源源不断地有高优先级进程到来,则可能导致饥饿
会产生饥饿
合理设置优先级
注:与I/O型进程相对的是计算型进程(或称CPU繁忙型进程)
什么时候跳转优先级?
**算法思想:**对其他调度算法的折中权衡
算法规则:
抢占式的算法。在k级队列的进程运行过程中,若更上级的队列(1~k-1级)中进入了一个新进程,则由于新进程处于优先级更高的队列中,因此新进程会抢占处理机,原来运行的进程放回k级队列队尾。
优缺点:
对各类型进程相对公平(FCFS的优点),每个新到达的进程都可以很快就得到响应(RR的优点),短进程只用较少的时间就可完成(SPF的优点),不必实现估计进程的运行时间(避免用户作假),可灵活地调整对各类进程的偏好程度,比如CPU密集型进程、I/O密集型进程(拓展:可以将因I/O而阻塞的进程重新放回原队列,这样/o型进程就可以保持较高优先级)
会产生饥饿 (连续新进程到达,则会导致当前进程饿死)
例子
比起早期的批处理操作系统来说,由于计算机造价大幅降低,因此之后出现的交互式操作系统(包括分时操作系统、实时操作系统等)更注重系统的响应时间、公平性、平衡性等指标。而这几种算法恰好也能较好地满足交互式系统的需求。因此这三种算法适合用于交互式系统。(比如UNIX使用的就是多级反馈队列调度算法)
进程同步:同步亦称直接制约关系,它是指为完成某种任务而建立的两个或多个进程,这些进程因为需要在某些位置上协调它们的工作次序而产生的制约关系。进程间的直接制约关系就是源于它们之间的相互合作。(解决异步的不可预知性)
进程互斥:对临界资源的访问,必须互斥地进行。互斥,亦称间接制约关系。进程互斥指当一个进程访问某临界资源时,另一个想要访问该临界资源的进程必须等待。当前访问临界资源的进程访问结束,释放该资源之后,另一个进程才能去访问临界资源。
临界资源:我们把一个时间段内只允许一个进程使用的资源称为临界资源。许多物理设备(比如摄像头、打印机)都属于临界资源。此外还有许多变量、数据、内存缓冲区等都属于临界资源。
临界资源互斥访问,可划分为四个部分:
临界资源的互斥访问原则
算法思想:两个进程在访问完临界区后会把使用临界区的权限转交给另一个进程。也就是说每个进程进入临界区的权限只能被另一个进程赋予
该算法可以实现“同一时刻最多只允许一个进程访问临界区”
单标志法存在的主要问题是:违背“空闲让进”原则。
由于执行顺序一定是P1P2轮流执行,因此P1只要不触发执行,P2一定无法执行!
算法思想:设置一个布尔型数组flag[],数组中各个元素用来标记各进程想进入临界区的意愿,比如“flag[0]=ture”意味着0号进程P0现在想要进入临界区。每个进程在进入临界区之前先检查当前有没有别的进程想进入临界区,如果没有,则把自身对应的标志flag[i]设为true,之后开始访问临界区。
双标志先检查法存在的主要问题是:违背“忙则等待”原则。
第一步和第五步若发生进程切换,则对邻接资源的访问就不互斥了!
原因在于先检查的两句代码不是原子操作!
算法思想:双标志先检查法的改版。前一个算法的问题是先“检查”后“上锁”,但是这两个操作又无法一气呵成,因此导致了两个进程同时进入临界区的问题。因此,人们又想到先“上锁”后“检查”的方法,来避免上述问题。
解决了“忙则等待”的问题,但是又违背了“空闲让进”和“有限等待”
同样第一步和第五步进程切换,就会发生二者都进不去临界区!
算法思想:双标志后检查法中,两个进程都争着想进入临界区,但是谁也不让谁,最后谁都无法进入临界区。Gary L. Peterson想到了一种方法,如果双方都争着想进入临界区,那可以让进程尝试“孔融让梨”,主动让对方先使用临界区。
Peterson算法用软件方法解决了进程互斥问题,遵循了空闲让进、忙则等待、有限等待三个原则,但是依然未遵循让权等待的原则。
利用“开 / 关中断指令”实现(与原语的实现思想相同,即在某进程开始访问临界区到结束访问为止都不允许被中断,也就不能发生进程切换,因此也不可能发生两个同时访问临界区的情况)
优点:简单、高效
缺点:不适用于多处理机;只适用于操作系统内核进程,不适用于用户进程(因为开 / 关中断指令 只能运行在内核态,这组指令如果能让用户随意使用会很危险)
简称TS 指令,也有地方称为TestAndSetLock 指令,或TSL 指令 . TSL 指令是用硬件实现的,执行的过程不允许被中断,只能一气呵成。
相比软件实现方法,TSL指令把“上锁”和“检查”操作用硬件的方式变成了一气呵成的原子操作。
优点:实现简单,无需像软件实现方法那样严格检查是否会有逻辑漏洞;适用于多处理机环境
缺点:不满足“让权等待”原则,暂时无法进入临界区的进程会占用CPU并循环执行TSL指令,从而导致“忙等”。
有的地方也叫Exchange 指令,或简称XCHG 指令。
Swap 指令是用硬件实现的,执行的过程不允许被中断,只能一气呵成。
逻辑上来看Swap 和TSL 并无太大区别,都是先记录下此时临界区是否已经被上锁(记录在old量上),再将上锁标记lock 设置为true,最后检查 old,如果 old 为false 则说明之前没有别的进程对临界区上锁,则可跳出循环,进入临界区。
优点:实现简单,无需像软件实现方法那样严格检查是否会有逻辑漏洞;适用于多处理机环境
缺点:不满足“让权等待”原则,暂时无法进入临界区的进程会占用CPU并循环执行TSL指令,从而导致“忙等”。
用户进程可以通过使用操作系统提供的一对原语来对信号量进行操作,从而很方便的实现了进程互斥、进程同步。
信号量其实就是一个变量(可以是一个整数,也可以是更复杂的记录型变量) ,可以用一个信号量来表示系统中某种资源的数量,比如:系统中只有一台打印机,就可以设置一个初值为1 的信号量。
原语是一种特殊的程序段,其执行只能一气呵成,不可被中断。原语是由关中断/开中断指令实现的。
一对原语:wait(S) 原语和signal(S) 原语,可以把原语理解为我们自己写的函数,函数名分别为wait和signal,括号里的信号量S 其实就是函数调用时传入的一个参数。
wait、signal 原语常简称为P、V操作(来自荷兰语proberen 和verhogen)。因此,做题的时候常把wait(S) 、signal(S) 两个操作分别写为P(S) 、V(S)
用一个整数型的变量作为信号量,用来表示系统中某种资源的数量。
与普通整数变量的区别:对信号量的操作只有三种,即初始化、P操作、V操作
整型信号量的缺陷是存在“忙等”问题,因此人们又提出了“记录型信号量”,即用记录型数据结构表示的信号量。
wait(S) 、signal(S) 也可以记为P(S) 、V(S) ,这对原语可用于实现系统资源的“申请”和“释放”。
S.value 的初值表示系统中某种资源的数目。
对信号量S 的一次P 操作意味着进程请求一个单位的该类资源,因此需要执行S.value-- ,表示资源数减1,当S.value < 0 时表示该类资源已分配完毕,因此进程应调用block 原语进行自我阻塞(当前运行的进程从运行态 ----> 阻塞态),主动放弃处理机,并插入该类资源的等待队列S.L 中。可见,该机制遵循了“让权等待”原则,不会出现“忙等”现象。
对信号量S 的一次V 操作意味着进程释放一个单位的该类资源,因此需要执行S.value++,表示资源数加1,若加1后仍是 S.value <= 0,表示依然有进程在等待该类资源,因此应调用wakeup 原语唤醒等待队列中的第一个进程(被唤醒进程从阻塞态 ----> 就绪态)。
理解:信号量mutex 表示“进入临界区的名额”
注意:
进程同步:要让各并发进程按要求有序地推进。
比如,P1、P2 并发执行,由于存在异步性,因此二者交替推进的次序是不确定的。若P2 的“代码4”要基于 P1 的“代码1”和“代码2”的运行结果才能执行,那么我们就必须保证“代码4”一定是在“代码2”之后才会执行。
这就是进程同步问题,让本来异步并发的进程互相配合,有序推进。
用信号量实现进程同步:
技巧口诀:前V后P
进程P1 中有句代码S1,P2 中有句代码S2 ,P3中有句代码S3 …… P6 中有句代码S6。这些代码要求按如下前驱图所示的顺序来执行
其实每一对前驱关系都是一个进程同步问题(需要保证一前一后的操作)
因此:
要为每一对前驱关系各设置一个同步信号量
在“前操作”之后对相应的同步信号量执行V 操作
在“后操作”之前对相应的同步信号量执行P 操作
系统中有一组生产者进程和一组消费者进程,生产者进程每次生产一个产品放入缓冲区,消费者进程每次从缓冲区中取出一个产品并使用。(注:这里的“产品”理解为某种数据)
生产者、消费者共享一个初始为空、大小为n的缓冲区。
PV操作题目分析步骤:
semaphore mutex = 1; // 互斥信号量,实现对缓冲区的互斥访问
semaphore empty = n; // 同步信号量,表示空闲缓冲区的数量
semaphore full = 0; // 同步信号量,表示产品的数量,也即非空缓冲区的数量
能否改变相邻的PV操作顺序?
① -> ② -> ③
若此时缓冲区内已经放满产品,则empty=0,full=n。
则生产者进程执行①使mutex变为0,再执行②,由于已没有空闲缓冲区,因此生产者被阻塞。由于生产者阻塞,因此切换回消费者进程。消费者进程执行③,由于mutex为0,即生产者还没释放对临界资源的“锁”,因此消费者也被阻塞。这就造成了生产者等待消费者释放空闲缓冲区,而消费者又等待生产者释放临界区的情况,生产者和消费者循环等待被对方唤醒,出现“死锁”。
③ -> ④ -> ①
同样的,若缓冲区中没有产品,即full=0,empty=n。按③④① 的顺序执行就会发生死锁。
因此,实现互斥的P操作一定要在实现同步的P操作之后。
V操作不会导致进程阻塞,因此两个V操作顺序可以交换。
桌子上有一只盘子,每次只能向其中放入一个水果。爸爸专向盘子中放苹果,妈妈专向盘子中放橘子,儿子专等着吃盘子中的橘子,女儿专等着吃盘子中的苹果。只有盘子空时,爸爸或妈妈才可向盘子中放一个水果。仅当盘子中有自己需要的水果时,儿子或女儿可以从盘子中取出水果。
不设置互斥信号量?
原因在于:本题中的缓冲区大小为1,在任何时刻,apple、orange、plate三个同步信号量中最多只有一个是1。因此在任何时刻,最多只有一个进程的P操作不会被阻塞,并顺利地进入临界区…
如果缓冲区大小为2,则两个生产者会都进入临界区(可能造成后者将前者的数据覆盖掉),这时候就必须得设置互斥信号量了!
注意:
假设一个系统有三个抽烟者进程和一个供应者进程。每个抽烟者不停地卷烟并抽掉它,但是要卷起并抽掉一支烟,抽烟者需要有三种材料:烟草、纸和胶水。
三个抽烟者中,第一个拥有烟草、第二个拥有纸、第三个拥有胶水。
供应者进程无限地提供三种材料,供应者每次将两种材料放桌子上,拥有剩下那种材料的抽烟者卷一根烟并抽掉它,并给供应者进程一个信号告诉完成了,供应者就会放另外两种材料再桌上,这个过程一直重复(让三个抽烟者轮流地抽烟)
同步关系
桌上有组合一 → 第一个抽烟者取走东西
桌上有组合二 → 第二个抽烟者取走东西
桌上有组合三 → 第三个抽烟者取走东西
发出完成信号 → 供应者将下一个组合放到桌上
互斥关系
有读者和写者两组并发进程,共享一个文件,当两个或两个以上的读进程同时访问共享数据时不会产生副作用,但若某个写进程和其他进程(读进程或写进程)同时访问共享数据时则可能导致数据不一致的错误。
因此要求:
两类进程:写进程、读进程
互斥关系:写进程—写进程、写进程—读进程。读进程与读进程不存在互斥问题。
此种实现是读进程优先的,读进程一直进入,则会导致写进程饿死!
写优先,但不是真正的写优先,相对公平的先来先服务原则,又叫做读写公平法!
一张圆桌上坐着5名哲学家,每两个哲学家之间的桌上摆一根筷子,桌子的中间是一碗米饭。哲学家们倾注毕生的精力用于思考和进餐,哲学家在思考时,并不影响他人。只有当哲学家饥饿时,才试图拿起左、右两根筷子(一根一根地拿起)。如果筷子已在他人手上,则需等待。饥饿的哲学家只有同时拿起两根筷子才可以开始进餐,当进餐完毕后,放下筷子继续思考。
这些进程之间只存在互斥关系,但是与之前接触到的互斥关系不同的是,每个进程都需要同时持有两个临界资源,因此就有“死锁”问题的隐患。
错误方案
合理方案
第三种方案的实现:
可以保证至少有一人可以吃到饭!
为什么要引入管程
信号量机制存在的问题:编写程序困难、易出错
能不能设计一种机制,让程序员写程序时不需要再关注复杂的PV操作,让写代码更轻松呢?
1973年,Brinch Hansen 首次在程序设计语言 (Pascal)中引入了“管程”成分,即一种高级同步机制
管程是一种特殊的软件模块,由这些部分组成:
类似于 类 !
前两句类似:类对象对类的访问!
引入管程的目的无非就是要更方便地实现进程互斥和同步。
简单来说:封装思想!
哲学家进餐问题中,如果5位哲学家进程并发执行,都拿起了左手边的筷子…
每位哲学家都在等待自己右边的人放下筷子,这些哲学家进程都因等待筷子资源而被阻塞。即发生“死锁”
在并发环境下,各进程因竞争资源而造成的一种互相等待对方手里的资源,导致各进程都阻塞,都无法向前推进的现象,就是“死锁”。发生死锁后若无外力干涉,这些进程都将无法向前推进。
死锁:各进程互相等待对方手里的资源,导致各进程都阻塞,无法向前推进的现象。
饥饿:由于长期得不到想要的资源,某进程无法向前推进的现象。比如:在短进程优先(SPF) 算法中,若有源源不断的短进程到来,则长进程将一直得不到处理机, 从而发生长进程“饥饿”。
死循环:某进程执行过程中一直跳不出某个循环的现象。有时是因为程序逻辑bug导致的,有时是程序员故意设计的。
产生死锁必须同时满足以下四个条件,只要其中任一条件不成立,死锁就不会发生。
互斥条件:只有对必须互斥使用的资源的争抢才会导致死锁(如哲学家的筷子、打印机设备)。像内存、扬声器这样可以同时让多个进程使用的资源是不会导致死锁的(因为进程不用阻塞等待这种资源)。
不剥夺条件:进程所获得的资源在未使用完之前,不能由其他进程强行夺走,只能主动释放。
请求和保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源又被其他进程占有,此时请求进程被阻塞,但又对自己已有的资源保持不放。
循环等待条件:存在一种进程资源的循环等待链,链中的每一个进程已获得的资源同时被下一个进程所请求。
注意:发生死锁时一定有循环等待,但是发生循环等待时未必死锁( 循环等待是死锁的必要不充分条件)
如果同类资源数大于1,则即使有循环等待,也未必发生死锁。但如果系统中每类资源都只有一个,那循环等待就是死锁的充分必要条件了。
对系统资源的竞争。各进程对不可剥夺的资源(如打印机)的竞争可能引起死锁,对可剥夺的资源(CPU)的竞争是不会引起死锁的。
进程推进顺序非法。请求和释放资源的顺序不当,也同样会导致死锁。例如,并发执行的进程P1、P2分别申请并占有了资源R1、R2,之后进程P1又紧接着申请资源R2,而进程P2又申请资源R1,两者会因为申请的资源被对方占有而阻塞,从而发生死锁。
信号量的使用不当也会造成死锁。如生产者~消费者问题中,如果实现互斥的P操作在实现同步的P操作之前,就有可能导致死锁。(可以把互斥信号量、同步信号量也看做是一种抽象的系统资源)
总之,对不可剥夺资源的不合理分配,可能导致死锁。
互斥条件:只有对必须互斥使用的资源的争抢才会导致死锁。
如果把只能互斥使用的资源改造为允许共享使用,则系统不会进入死锁状态。比如 : SPOOLing技术。
操作系统可以采用SPOOLing 技术把独占设备在逻辑上改造成共享设备。比如,用SPOOLing技术将打印机改造为共享设备…(打印机添加任务队列即可)
该策略的缺点:并不是所有的资源都可以改造成可共享使用的资源。并且为了系统安全,很多地方还必须保护这种互斥性。因此,很多时候都无法破坏互斥条件。
不剥夺条件:进程所获得的资源在未使用完之前,不能由其他进程强行夺走,只能主动释放。
方案一:当某个进程请求新的资源得不到满足时,它必须立即释放保持的所有资源,待以后需要时再重新申请。也就是说,即使某些资源尚未使用完,也需要主动释放,从而破坏了不可剥夺条件。
方案二:当某个进程需要的资源被其他进程所占有的时候,可以由操作系统协助,将想要的资源强行剥夺。这种方式一般需要考虑各进程的优先级(比如:剥夺调度方式,就是将处理机资源强行剥夺给优先级更高的进程使用)
该策略的缺点:
请求和保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源又被其他进程占有,此时请求进程被阻塞,但又对自己已有的资源保持不放。
可以采用静态分配方法,即进程在运行前一次申请完它所需要的全部资源,在它的资源未满足前,不让它投入运行。一旦投入运行后,这些资源就一直归它所有,该进程就不会再请求别的任何资源了。
该策略实现起来简单,但也有明显的缺点:
循环等待条件:存在一种进程资源的循环等待链,链中的每一个进程已获得的资源同时被下一个进程所请求。
可采用顺序资源分配法。首先给系统中的资源编号,规定每个进程必须按编号递增的顺序请求资源,同类资源(即编号相同的资源)一次申请完。
原理分析:一个进程只有已占有小编号的资源时,才有资格申请更大编号的资源。按此规则,已持有大编号资源的进程不可能逆向地回来申请小编号的资源,从而就不会产生循环等待的现象。
该策略的缺点:
安全序列,就是指如果系统按照这种序列分配资源,则每个进程都能顺利完成。只要能找出一个安全序列,系统就是安全状态。当然,安全序列可能有多个。
如果分配了资源之后,系统中找不出任何一个安全序列,系统就进入了不安全状态。这就意味着之后可能所有进程都无法顺利的执行下去。当然,如果有进程提前归还了一些资源,那系统也有可能重新回到安全状态,不过我们在分配资源之前总是要考虑到最坏的情况。
如果系统处于安全状态,就一定不会发生死锁。如果系统进入不安全状态,就可能发生死锁(处于不安全状态未必就是发生了死锁,但发生死锁时一定是在不安全状态)
因此可以在资源分配之前预先判断这次分配是否会导致系统进入不安全状态,以此决定是否答应资源分配请求。这也是“银行家算法”的核心思想。
银行家算法是荷兰学者Dijkstra 为银行系统设计的,以确保银行在发放现金贷款时,不会发生不能满足所有客户需要的情况。后来该算法被用在操作系统中,用于避免死锁。
核心思想:在进程提出资源申请时,先预判此次分配是否会导致系统进入不安全状态。如果会进入不安全状态,就暂时不答应这次请求,让该进程先阻塞等待。
银行家算法步骤:
数据结构:
安全性算法步骤:
为了能对系统是否已发生了死锁进行检测,必须:
如果系统中剩余的可用资源数足够满足进程的需求,那么这个进程暂时是不会阻塞的,可以顺利地执行下去。如果这个进程执行结束了把资源归还系统,就可能使某些正在等待资源的进程被激活,并顺利地执行下去。相应的,这些被激活的进程执行完了之后又会归还一些资源,这样可能又会激活另外一些阻塞的进程…
如果按上述过程分析,最终能消除所有边,就称这个图是可完全简化的。此时一定不发生死锁(相当于能找到一个安全序列)
如果最终不能消除所有边,那么此时就是发生了死锁
死锁例子:
一旦检测出死锁的发生,就应该立即解除死锁。
补充:并不是系统中所有的进程都是死锁状态,用死锁检测算法化简资源分配图后,还连着边的那些进程就是死锁进程
解除死锁的主要方法有:
如何绝对对谁动手?
指令的编指一般采用逻辑地址,即相对地址
物理地址 = 起始地址 + 逻辑地址
相对地址又称逻辑地址,绝对地址又称物理地址。
链接:由连接程序将编译后形成的一组目标模块以及所需函数连接在一起, 形成一个完整的装入模块
装入:由装入程序将装入模块装入内存运行
为了使编程更方便,程序员写程序时应该只需关注指令、数据的逻辑性。而逻辑地址到物理地址的转换(这个过程称为地址重定位),应该由操作系统负责,这样就保证了程序员写程序时不需要关注物理内存的实际情况。
在编译时,如果知道程序将放到内存的哪个位置,编译程序将产生绝对地址的目标代码, 装入程序按照装入模块中的地址,将程序和数据装入内存【灵活性低, 只适合单道程序环境】
绝对装入只适用于单道程序环境。还未产生操作系统,由编译器完成!
程序中使用的绝对地址,可在编译或汇编时给出,也可由程序员直接赋予。通常情况下都是编译或汇编时再转换为绝对地址。
又称为可重定位装入。 编译, 链接后的装入模块地址都是从 0 开始的,指令中使用的地址和数据存放的地址都是相对于起始地址而言的逻辑地址。可以根据内存的当前状况将装入模块装入到内存的适当位置。装入时对地址进行"重定位",逻辑地址变换为物理地址(地址变换是在装入时一次完成的)。【早期多道批处理系统】
静态重定位的特点是在一个作业装入内存时,必须分配其要求的全部内存空间,如果没有足够的内存,就不能装入该作业。作业一旦进入内存后,在运行期间就不能再移动,也不能再申请内存空间。
又称动态运行时装入。编译、链接后的装入模块的地址都是从0开始的,装入程序把装入模块装入内存后不会立即把逻辑地址转换为物理地址,而是把地址转换推迟到程序真正要执行时才进行。因此装入内存后所有的地址依然是逻辑地址。这种方式需要一个重定位寄存器(存放装入模块的起始位置)的支持。【现代操作系统】
1、2、4 将在下方讲解!
覆盖技术:将程序分为多个段,常用的段常驻内存,不常用的段在需要的时候调入内存
内存中分为一个"固定区" 和若干个"覆盖区",常用的段放在固定区,不常用的段放在覆盖区
缺点:必须由程序员声明覆盖结构, 对用户不透明, 增加了用户的编程负担,覆盖技术只用于早期的操作系统中。
交换技术:内存空间紧张时, 系统将内存中某些进程暂时换出外存,把外存中某些已具备运行条件的进程换入内存(即进程在内存与磁盘间动态调度)
应该在外存(磁盘)的什么位置保存被换出的进程?
具有对换功能的操作系统中,通常把磁盘空间分为文件区和对换区两部分。文件区主要用于存放文件,主要追求存储空间的利用率,因此对文件区空间的管理采用离散分配方式;对换区空间只占磁盘空间的小部分,被换出的进程数据就存放在对换区。由于对换的速度直接影响到系统的整体速度,因此对换区空间的管理主要追求换入换出速度,因此通常对换区采用连续分配方式。总之,对换区的I/O速度比文件区的更快。
什么时候应该交换?
交换通常在许多进程运行且内存吃紧时进行,而系统负荷降低就暂停。例如:在发现许多进程运行时经常发生缺页,就说明内存紧张,此时可以换出一些进程;如果缺页率明显下降,就可以暂停换出。
应该换出哪些进程?
可优先换出阻塞进程;可换出优先级低的进程;为了防止优先级低的进程在被调入内存后很快又被换出,有的系统还会考虑进程在内存的驻留时间。
PCB仍然得常驻内存!
一次性:作业必须一次性全部装入内存后才能开始运行
驻留性:一旦作业被装入内存,就会一直驻留在内存中,直到作业运行结束,这样会导致内存中驻留大量的,暂时用不到的数据,浪费内存资源。
是对局部性原理的应用!
将近期会频繁访问到的数据放到更高速的存储器中,暂时用不到的数据放在更低速存储器中。
虚拟内存的最大容量是由计算机的地址结构(CPU的寻址范围)确定的
虚拟内存的实际容量 = min(内存容量+外存容量,CPU寻址范围)
特征:
多次性:无需在作业运行时一次性全部装入内存,而是允许被分成多次调入内存。
对换性:在作业运行时无需一直常驻内存,而是允许在作业运行过程中,将作业换入、换出。
虚拟性:从逻辑上扩充了内存的容量,使用户看到的内存容量远大于实际容量。
虚拟内存技术,允许一个作业分多次调入内存。如果采用连续分配方式,会不方便实现。因此,虚拟内存的实现需要建立在离散分配(非连续分配)的内存管理方式基础上。
对应传统的三种非连续分配存储管理,有三种虚拟存储的实现:
调入:在程序执行过程中,当所访问的信息不在内存时,由操作系统负责将所需信息从外存调入内存,然后继续执行程序。(操作系统要提供请求调页 / 调段功能)
调出:若内存空间不够,由操作系统负责将内存中暂时用不到的信息换出到外存。(操作系统要提供页面置换 / 段置换的功能)
与基本分页管理方式的区别:
大纲
页表机制
与基本分页管理相比,请求分页管理中,为了实现“请求调页”,操作系统需要知道每个页面是否已经调入内存;如果还没调入,那么也需要知道该页面在外存中存放的位置。
当内存空间不够时,要实现“页面置换”,操作系统需要通过某些指标来决定到底换出哪个页面;有的页面没有被修改过,就不用再浪费时间写回外存。有的页面修改过,就需要将外存中的旧数据覆盖,因此,操作系统也需要记录各个页面是否被修改的信息。
缺页中断机构
在请求分页操作系统中,每当要访问的页面不在内存时,便产生一个缺页中断, 然后由操作系统的缺页中断处理程序处理中断。
此时缺页的进程阻塞,放入阻塞队列,调页完成后再将其唤醒, 放回就绪队列。
缺页中断是因为当前执行的指令想要访问目标页面未调入内存而产生的,因此属于内中断(故障) 。
一条指令在执行期间,可能产生多次缺页中断。(如: copy A to B,即将逻辑地址A中的数据复制到逻辑地址B,而A、B属于不同的页面,则有可能产生两次中断)
地址变换机构
大纲
详细参考: https://www.itnxd.cn/posts/8398.html#五、页面置换算法
页面的换入、换出需要磁盘I/O,会有较大的开销,因此好的页面置换算法应该追求更少的缺页率!
最佳置换算法(OPT)
优先淘汰以后永不使用或者在最长时间内不会使用的页面,保证最低的缺页率。
但是操作系统无法预判页面访问序列,这种算法是无法实现的。
先进先出置换算法(FIFO)
优先淘汰最早进入内存的页面。
实现 :将调入内存的页面根据调入的先后顺序排成一个队列,需要置换页面的时候选择队首的页面。
实现简单;算法性能差,不适应进程实际运行时的规律,可能出现 Belady 异常。
Belady异常――当为进程分配的物理块数增大时,缺页次数不减反增的异常现象。
只有FIFO算法会产生Belady异常。
最近最久未使用置换算法(LRU)
优先淘汰最近最久未使用的页面。
性能好,但实现起来需要专门的硬件支持,算法开销大。
时钟置换算法(CLOCK)
又叫做 最近未用算法 NRU
为每个页面设置一个访问位,再将内存中的页面都通过链接指针链接成一个循环队列。
循环扫描各页面,第一轮淘汰访问位 = 0 的,并将扫描过的页面访问位改为1。若第一轮没选中,则进行第二轮扫描。(最多会扫描两轮!)
实现简单,算法开销小;但未考虑页面是否被修改过。
简单的时钟置换算法仅考虑到了一个页面最近是否被访问过,但是事实上,如果被淘汰的页面没有被修改过,就不需要执行I/O操作写回外存。只有被淘汰的页面被修改过时,才需要写回外存。
因此, 除了考虑一个页面最近有没有被访问过之外,操作系统还应该考虑页面有没有被修改过。
在其他条件都相同时,应该优先淘汰没有修改过的页面, 避免I/O操作,这就是改进型的时钟置换算法的思想。
改进型的时钟置换算法
利用**(访问位,修改位)** 的形式表示各页面状态
简而言之:按照00,01,10,11的优先级进行替换!
算法开销小,性能也不错。
最多扫描四轮!
大纲
驻留集:请求分页存储管理器中给进程分配的物理块的集合。
在采用虚拟存储技术的系统中, 驻留集的大小一般小于进程的总大小
策略
局部置换:发生缺页时只能选进程自己的物理块进行置换。
全局置换:可以将操作系统保留的空闲物理块分配给缺页进程,也可以将别的进程持有的物理块置换到外存,再分配给缺页进程。
页面分配&置换策略?
固定分配局部置换:系统为每个进程分配一定数量的物理块,在整个运行期间都不改变。若进程在运行中发生缺页,则只能从该进程在内存中的页面中选出一页换出,然后再调入需要的页面。这种策略的缺点是:很难在刚开始就确定应为每个进程分配多少个物理块才算合理。(采用这种策略的系统可以根据进程大小、优先级、或是根据程序员给出的参数来确定为一个进程分配的内存块数)
可变分配全局置换:刚开始会为每个进程分配一定数量的物理块。操作系统会保持一个空闲物理块队列。当某进程发生缺页时,从空闲物理块中取出一块分配给该进程;若已无空闲物理块,则可选择一个未锁定的页面换出外存,再将该物理块分配给缺页的进程。采用这种策略时,只要某进程发生缺页,都将获得新的物理块,仅当空闲物理块用完时,系统才选择一个未锁定的页面调出。被选择调出的页可能是系统中任何一个进程中的页,因此这个被选中的进程拥有的物理块会减少,缺页率会增加。
可变分配局部置换:刚开始会为每个进程分配一定数量的物理块。当某进程发生缺页时,只允许从该进程自己的物理块中选出一个进行换出外存。如果进程在运行中频繁地缺页,系统会为该进程多分配几个物理块,直至该进程缺页率趋势适当程度;反之,如果进程在运行中缺页率特别低,则可适当减少分配给该进程的物理块。
何时调入页面?
预调页策略:根据局部性原理(主要指空间局部性),一次调入若干个相邻的页面可能比一次调入一个页面更高效。但如果提前调入的页面中大多数都没被访问过,则又是低效的。因此可以预测不久之后可能访问到的页面,将它们预先调入内存,但目前预测成功率只有50%左右。故这种策略主要用于进程的首次调入,由程序员指出应该先调入哪些部分。(运行前调入)
请求调页策略:进程在运行期间发现缺页时才将所缺页面调入内存。由这种策略调入的页面一定会被访问到,但由于每次只能调入一页,而每次调页都要磁盘I/O操作,因此I/O开销较大。(运行时调入)
何处调入页面?
抖动现象?
刚刚换出的页面马上又要换入内存,刚刚换入的页面马上又要换出外存,这种频繁的页面调度行为称为抖动,或颠簸。
产生抖动的主要原因是进程频繁访问的页面数目高于可用的物理块数( 分配给进程的物理块不够)。
为了研究为应该为每个进程分配多少个物理块,Denning提出了进程“工作集”的概念
工作集?
工作集大小可能小于窗口尺寸,实际应用中,操作系统可以统计进程的工作集大小,根据工作集大小给进程分配若干内存块。如:窗口尺寸为5,经过一段时间的监测发现某进程的工作集最大为3,那么说明该进程有很好的局部性,可以给这个进程分配3个以上的内存块即可满足进程的运行需要。
一般来说,驻留集大小不能小于工作集大小,否则进程运行过程中将频繁缺页。
拓展:基于局部性原理可知,进程在一段时间内访问的页面与不久之后会访问的页面是有相关性的。因此,可以根据进程近期访问的页面集合(工作集)来设计一种页面置换算法――选择一个不在工作集中的页面进行淘汰。
连续分配:指为用户进程分配的必须是一个连续的内存空间。
在单一连续分配的方式中, 内存被分为系统区和用户区, 系统区用于存放操作系统的相关数据,用户区用于存放用户进程的相关数据。内存中只能有一道用户程序,用户程序独占整个用户区空间。
优点 : 实现简单, 无外部碎片; 可以采用覆盖技术扩充内存; 不一定需要采取内存保护
缺点 : 只能用于单用户, 单任务的操作系统中; 有内部碎片; 存储器利用率极低
内部碎片 : 分配给某进程的内存区域有一部分没有用上,即存在" 内部碎片 ".
外部碎片 : 内存中的某些空闲分区由于太小而难以利用
在产生了支持多道程序的系统后,为了能在内存中装入多道程序而互相之间不产生干扰,将整个用户区划分为若干个固定大小的分区(分区大小可以相等也可以不相等),在每个分区中只能装入一道作业, 形成了最早的可运行多道程序的内存管理方式。
固定分区分配
操作系统如何记录内存中的各个分区?
操作系统建立分区说明表来实现各个分区的分配和回收。
每个表对应一个分区,通常按分区大小排列。每个表项包括对应分区的大小,起始地址,状态(是否已分配)。
用数据结构的数组(或链表)即可表示这个表。
当某用户程序要装入内存时,由操作系统内核程序根据用户程序大小检索该表,从中找到一个能满足大小的、未分配的分区,将之分配给该程序,然后修改状态为 ” 已分配 “ 。
优点:实现简单,无外部碎片。
缺点:
动态分区分配又称为可变分区分配, 这种分配方式不会预先划分内存分区。而是在进程装入内存时根据进程大小动态地建立分区,并使得分区的大小正好适合进程的需要。系统分区的大小和数目是可变的!
系统要用什么样的数据结构记录内存的使用情况?
动态分配不会产生内部碎片, 而会产生外部碎片, 外部碎片可以通过" 紧凑"的方式解决。
当很多个空闲分区都能满足需求时,应该选择哪个分区进行分配?
把一个新作业装入内存时,须按照一定的动态分区分配算法(后面详细讲解),从空闲分区表(或空闲分区链)中选出一个分区分配给该作业。由于分配算法算法对系统性能有很大的影响,因此人们对它进行了广泛的研究。
如何进行分区的分配与回收操作?
每次都从低地址开始查找,找到第一个能满足大小的空闲分区。
实现:把空闲分区按地址递增的次序排列。
每次分配内存时顺序地查找空闲分区链,找到大小能满足要求的第一个空闲分区。
优点:综合看性能最好。算法开销小,回收分区后一般不需要对空闲分区队列重新排序。
优先使用小的空闲分区
实现: 空闲分区按容量递增次序排序。
每次分配内存时顺序查找空闲分区链, 找到大小能满足要求的第一个空闲分区。
优点:会有更多的大分区被保留下来,更能满足大进程需求。
缺点:每次都选择最小的分区进行分配,会留下越来越多的容量很小难以利用的内存块,即产生很多的外部碎片;算法开销大,回收分区后可能需要对空闲分区队列重新排序。
优先使用大的空闲分区
实现: 空闲分区按容量递减次序排序
优点:可以减少难以利用的小碎片。
缺点:每次都选用最大的分区进行分配,当较大的连续空闲区被小号之后,如果有大进程到来则没有内存分区可以利用;算法开销大。
在首次适应算法的基础上,每次都从上次查找结束的位置开始查找空闲分区链(表),找到大小能满足的第一个空闲分区。
优点:不用每次都从低地址的小分区开始检索。算法开销小。
缺点:邻近适应算法导致无论低地址还是高地址的空闲分区都有相同的概率被使用,也就导致了高地址部分的大分区更可能被使用划分为小分区,最后导致没有大分区可用。
算法开销大:最佳适应法, 最坏适应法 ( 需要经常排序)
算法开销小:首次适应算法, 邻近适应算法
连续分配管理方式缺点:
基于这一思想,产生了“非连续分配方式”,或者称为“离散分配方式”。
非连续:为用户进程分配的可以是一些分散的内存空间!
思想:把内存分为一个个相等的小分区, 再按照分区大小把进程拆分成一个个小部分.
页框:将内存大小分为一个个大小相等的分区,每个分区就是一个 “ 页框 ”,或称 “ 页帧 ”、“ 内存块 ”、“ 物理块 ”。每个页框有一个编号,即 “ 页框号 ”,或称 “ 内存块号 ”、“ 页帧号 ”、“ 物理块号 ” ,页框号从 0 开始。
页面:将用户进程的地址空间也分为与页框大小相等的一个个区域,称为 “ 页 ” 或 “ 页面 ”。每个页面也有一个编号,即 “ 页号 ”,页号也是从 0 开始。
例子:
CPU执行指令1,需要访问逻辑地址为80的内存单元,如何转化为物理地址?
逻辑地址为80的内存单元,应该在1号页,该页在内存中的起始位置为450,逻辑地址为80的内存单元相对于该页的起始地址而言,“偏移量”应该是30。
实际物理地址 = 450 + 30 = 480
如何计算:
页号 = 逻辑地址 / 页面长度(取除法的整数部分)
页内偏移量 = 逻辑地址 % 页面长度(取除法的余数部分)
页面在内存中的起始位置:操作系统需要用某种数据结构记录进程各个页面的起始位置。
页号 = 80 / 50 = 1
页内偏移量 = 80 % 50 = 30
1 号页在内存中存放的起始位置 450
为了方便计算机计算页号、页内偏移量,页面大小一般设为2的整数幂!
逻辑地址结构包含两个部分:前一部分为页号,后一部分为页内偏移量w。在上图所示的例子中,地址长度为32位,其中0 ~ 11位为“页内偏移量”,或称“页内地址”;12~31位为“页号”。
解决地址转换的第二步:计算页号对应页面在内存中的起始地址
为了能知道进程的每个页面在内存中存放的位置,操作系统要为每个进程建立一张页表。
例子
假设某系统物理内存大小为4GB,页面大小为4KB,则每个页表项至少应该为多少字节?
4GB = 2^32B,4KB = 2^12B
因此4GB的内存总共会被分为 2^32 / 2^12 = 2^20 个内存块,因此内存块号的范围应该是0 ~ 2^20 - 1 因此至少要20个二进制位才能表示这么多的内存块号,因此至少要3个字节才够 (每个字节8个二进制位,3个字节共24个二进制位)
各页表项会按顺序连续地存放在内存中
如果该页表在内存中存放的起始地址为X,则M号页对应的页表项一定是存放在内存地址为 X + 3 * M 因此,页表中的**“页号”可以是“隐含”的**。只需要知道页表存放的起始地址和页表项长度,即可找到各个页号对应的页表项存放的位置!
在本例中,一个页表项占3B,如果进程由n个页面,则该进程的页表总共会占 3 * n 个字节
大纲
用于实现逻辑地址到物理地址转换(借助进程的页表)的一组硬件机构
设页面大小为L,逻辑地址A到物理地址E的变换过程如下:
页表长度指的是这个页表中总共有几个页表项,即总共有几个页;页表项长度指的是每个页表项占多大的存储空间;页面大小指的是一个页面占多大的存储空间
在分页存储管理(页式管理)的系统中,只要确定了每个页面的大小,逻辑地址结构就确定了。
因此,页式管理中地址是一维的。即,只要给出一个逻辑地址,系统就可以自动地算出页号、页内偏移量两个部分,并不需要显式地告诉系统这个逻辑地址中,页内偏移量占多少位。
理论上,页表项长度为3B即可表示内存块号的范围,但是,为了方便页表的查询,常常会让一个页表项占更多的字节,使得每个页面恰好可以装得下整数个页表项。
是基本地址变换机构的改进版本!
局部性原理?
上小节介绍的基本地址变换机构中,每次要访问一个逻辑地址,都需要查询内存中的页表。由于局部性原理,可能连续很多次查到的都是同一个页表项。既然如此,能否利用这个特性减少访问页表的次数呢?
什么是快表TLB?
快表又成为联想寄存器(TLB),是一种访问速度比内存块很多的高速缓冲存储器,用来存放当前访问的若干页表项, 以加速地址变换的过程. 与此对应的,内存中的页表常称为慢表。
引入快表的地址变换过程?
由于查询快表的速度比查询页表的速度快很多,因此只要快表命中,就可以节省很多时间。因为局部性原理,一般来说快表的命中率可以达到90%以上。
基本地址变换机构 VS 具有快表的地址变换机构
大纲
单级页表存在的问题?
1、假设某计算机系统按字节寻址,支持32位逻辑地址,采用分页存储管理,页面大小为4KB,页表项长度为4B。
4KB = 2^12B,因此页内地址要用12位表示,剩余20位表示页号。
因此,该系统中用户进程最多有2^20 页。相应的,一个进程的页表中,最多会有 2^20 个页表项,所以一个页表最大需要2^20 * 4B = 2 ^ 22B。一个页框(内存块)大小为 4B,所以需要2^22 / 2^12 (页面大小为4KB)= 2^10个页框存储该页表。而页表的存储是需要连续存储的,
因为根据页号查询页表的方法:K号页对应的页表项的位置 = 页表起始地址 + K * 4B,所以这就要求页表的存储必须是连续的。当页表很大时,需要占用很多个连续的页框。
回想一下,当初为什么使用页表,就是要将进程划分为一个个页面可以不用连续的存放在内存中,但是此时页表就需要1024个连续的页框,与当时的目标相悖。
2、根据局部性原理可知,很多时候,进程在一段时间内只需要访问某几个页面就可以正常运行了。因此没有必要让整个页表都常驻内存。
解决方法
可将长长的页表进行分组,使每个内存块刚好可以放入一个分组,再将各组离散地放到各个内存块中。
例子,页面大小4KB,每个页表项4B,每个页面可存放1K个页表项,因此每1K个连续的页表项为一组,每组刚好占一个内存块,再讲各组离散地放到各个内存块中!
另外,为离散分配的页表再建立一张页表,称为页目录表,或称外层页表,或称顶层页表
实现地址转换
例子
细节
例子
分段:进程的地址空间会按照自身的逻辑关系划分为若干个段, 每个段都有一个段名, 每段从0开始编址。
内存分配规则:以段为单位进行分配,每个段在内存中占据连续空间,但各段之间可以不相邻。
分段系统的逻辑地址结构由段号(段名)和段内地址(段内偏移量〉所组成。
段号的位数决定了每个进程最多可以分为几个段
段内地址位数决定了每个段的最大长度是多少
段表:程序分多个段,各段离散地装入内存,为了保证程序能正常运行,就必须能从物理内存中找到各个逻辑段的存放位置。为此,需为每个进程建立一张段映射表。
例如:某系统按字节寻址,采用分段存储管理,逻辑地址结构为(段号16位,段内地址16位),因此用16位即可表示最大段长。物理内存(即基址范围)大小为4GB(可用32位表示整个物理内存地址空间)。
因此,可以让每个段表项占16+32=48位,即6B。
由于段表项长度相同,因此段号可以是隐含的,不占存储空间。
若段表存放的起始地址为M,则K号段对应的段表项存放的地址为 M + K * 6(即段号时隐含的!)
分段比分页更容易实现信息的共享和保护。(类似指针,二者短表项一致即可同时指向共享内存)
不能被修改的代码称为纯代码或可重入代码(不属于临界资源),这样的代码是可以共享的.可修改的代码是不能共享的(比如,有一个代码段中有很多变量,各进程并发地同时访问可能造成数据不一致)
简单来说:
访问一个逻辑地址需要几次访存?
分段管理中产生的外部碎片也可以用“紧凑”来解决,只是需要付出较大的时间代价
将进程按逻辑模块分段,再将各段分页(如每个页面4KB)
再将内存空间分为大小相同的内存块/页框/页帧/物理块
进程前将各页面分别装入各内存块中
由段号、页号、页内地址(页内偏移量)组成!
分段系统的端内地址拆分为:页号 + 页内偏移
" 分段 " 对用户是可见的,而将各段"分页"对用户是不可见的,系统会根据段内地址自动划分页号和段内偏移量,因此段页式管理的地址结构是 " 二维 " 的。
每一个进程对应一个段表,每一个段又对应一个页表,因此一个进程可能对应多个页表。
每个段对应一个段表项,每个段表项由段号、页表长度、页表存放块号(页表起始地址)组成。
每个段表项长度相等,段号是隐含的。
每个页面对应一个页表项,每个页表项由页号、内存存放的内存块号组成。
每个页表项长度相等,页号是隐含的。
1、由逻辑地址得到段号、页号、页内偏移
2、段号与段表寄存器的段长度比较,检查是否越界
3、由段表始址, 段号找到对应段表项
4、根据段表中记录的页表长度,检查页号是否越界
5、由段表中的页表地址, 页号得到查询页表,找到相应页表项
6、由页面存放的内存块号,页内偏移得到最终的物理地址
7、访问目标单元
需要三次访存,第一次查段表,第二次查页表,第三次访问目标单元。
文件名、标识符、类型、位置、大小、创建时间、上次修改时间、文件所有者信息、保护信息
无结构文件(流式文件):文件内部的数据是由一系列的二进制或字符流组成,如文本文件(.txt文件)
有结构文件(记录式文件):由一组相似的记录文件组成,每条记录又由若干个数据项组成,如数据库表。一般来说,每条记录有一个数据项作为关键字。根据各条记录的长度(占用的存储空间)是否相等,又可分为定长记录和可变长记录。
记录是一组相关数据项的集合
create、delete、open、close、read、write 等系统调用功能!
文件逻辑结构
逻辑结构:指在用户看来,文件内部的数据应该是如何组织起来的。
物理结构:指的是在操作系统看来,文件的数据是如何存放在外存的。
顺序文件:文件中的记录一个接一个地顺序排列(逻辑上),记录可以是定长的或可变长的。各个记录在物理上可以顺序存储(物理上连续)或链式存储(物理上可能不连续)。
顺序文件(顺序结构)的缺点是增加/删除一个记录比较困难(如果是串结构则相对简单)
解决可变长记录的快速查找问题!
索引表本身是定长记录的顺序文件。因此可以快速找到第i个记录对应的索引项。
可将关键字作为索引号内容,若按关键字顺序排列,则还可以支持按照关键字折半查找。
每当要增加/删除一个记录时,需要对索引表进行修改。由于索引文件有很快的检索速度,因此主要用于对信息处理的及时性要求比较高的场合。
另外,可以用不同的数据项建立多个索引表。如:学生信息表中,可用关键字“学号”建立一张索引表。也可用“姓名”建立一张索引表。这样就可以根据“姓名”快速地检索文件了。
索引文件的缺点:每个记录对应一个索引表项,因此索引表可能会很大。比如:文件的每个记录平均只占8B,而每个索引表项占32个字节,那么索引表都要比文件内容本身大4倍,这样对存储空间的利用率就太低了。
索引顺序文件是索引文件和顺序文件思想的结合。索引顺序文件中,同样会为文件建立一张索引表,但不同的是:并不是每个记录对应一个索引表项,而是一组记录对应一个索引表项。
若一个顺序文件有10000个记录,则根据关键字检索文件,只能从头开始顺序查找(这里指的并不是定长贡录、顺序结构的顺序文件),平均须查找5000个记录。
若采用索引顺序文件结构,可把10000个记录分为100 组,每组100个记录。则需要先顺序查找索引表找到分组(共100个分组,因此索引表长度为100,平均需要查50次),找到分组后,再在分组中顺序查找记录(每个分组100个记录,因此平均需要查50次)。可见,采用索引顺序文件结构后,平均查找次数减少为50+50= 100次。
同理,若文件共有1000000个记录,则可分为1000个分组,每个分组1000个记录。根据关键字检索一个记录平均需要查找500+500 = 1000次。这个查找次数依然很多,如何解决呢?
为了进一步提高检索效率,可以为顺序文件建立多级索引表。
套娃即可! 空间换时间!
FCB中包含
对目录进行的操作:搜索、创建文件、删除文件、显示目录、修改目录
单级目录结构
早期的操作系统并不支持多级目录,整个系统只建立一张目录表,每个文件占一个目录项。
单级目录实现了 " 按名存取 ",但是不允许文件重名。
单级目录不支持多用户操作系统。
两级目录结构
早期的多用户操作系统采用两级目录结构,分为主文件目录 (MFD,Master File Directory)和用户文件目录(UFD,User File Directory)。
优点:两级目录允许不同用户的文件重名,也可以在目录上实现访问限制(检查此时登录的用户名是否匹配)。
缺点:缺乏灵活性,用户不能对自己的文件进行分类。
多级目录结构
又称树形目录结构
用户(或用户进程)要访问某个文件时要用文件路径名标识文件,文件路径名是个字符串。各级目录之间用“/”隔开。从根目录出发的路径称为绝对路径。
从当前目录出发的路径称为相对路径。
系统根据绝对路径一层一层地找到下一级目录。刚开始从外存读入根目录的目录表;找到“照片”目录的存放位置后,从外存读入对应的目录表;再找到“2015-08”目录的存放位置,再从外存读入对应目录表;最后才找到文件“白拍.jpg”的存放位置。整个过程需要3次读磁盘I/O操作。
相对路径可以减少磁盘 I/O 操作次数。
优点:树形目录结构可以很方便地对文件进行分类,层次结构清晰,也能够更有效地进行文件的管理和保护。
缺点:树形结构不便于实现文件的共享。
对此,提出了“ 无环图目录结构 ”。
无环图目录结构
在树形目录结构的基础上,增加一些指向同一节点的有向边,使整个目录成为一个有向无环图。
可以更方便地实现多个用户间的文件共享。
可以用不同的文件名指向同一个文件,甚至可以指向同一个目录(共享同一目录下的所有内容)。
需要为每个共享结点设置一个共享计数器,用于记录此时有多少个地方在共享该结点。用户提出删除结点的请求时,只是删除该用户的FCB、并使共享计数器减1,并不会直接删除共享结点。减到0时,才能删除该节点!
对FCB的改进!
由来:其实在查找各级目录的过程中只需要用到“文件名”这个信息,只有文件名匹配时,才需要读出文件的其他信息。因此可以考虑让目录表“瘦身”来提升效率。
将除了文件名之外的文件描述信息都放到索引结点中。由于目录项长度减小,因此每个磁盘块可以存放更多各目录项,可以大大提升文件检索速度。
当找到文件名对应的目录项时,才需要将索引结点调入内存,索引结点中记录了文件的各种信息,包括文件在外存中的存放位置,根据“ 存放位置 ”即可找到文件。
存放在外存中的索引结点称为“磁盘索引结点”,当索引结点放入内存后称为“内存索引结点”。
相比之下内存索引结点中需要增加一些信息,比如:文件是否被修改、此时有几个进程正在访问该文件等。
文件物理结构(文件)分配方式
即文件数据应该怎样存放在外存中。
是对非空闲块的管理!
磁盘块
类似于内存分页,磁盘中的存储单元也会被分为一个个 ” 块 / 磁盘块 / 物理块 “。
很多操作系统中,磁盘块的大小与内存块、页面的大小相同。
内存与磁盘之间的数据交换(即 读/写操作、磁盘I/O)都是以” 块 “ 为单位进行的。
即每次读入一块,或每次写出一块
文件块
在内存管理中, 进程的逻辑地址空间被分为一个个的页面
同样的, 在外存管理中, 为了方便对文件数据的管理, 文件的逻辑地址空间也被分为了一个个的文件块
于是文件的逻辑地址也可以表示为 **(逻辑块号, 块内地址)**的形式
连续分配方式要求每个文件在磁盘上占有一组连续的块。
用户给出要访问的逻辑块号,操作系统找到该文件对应的目录项(FCB) …
物理块号 = 起始块号+ 逻辑块号(就是偏移)
当然,还需要检查用户提供的逻辑块号是否合法(逻辑块号≥长度就不合法)
操作系统可以直接算出逻辑块号对应的物理块号,因此连续分配支持顺序访问和直接访问( 随机访问 )
连续分配方式要求每个文件在磁盘上占有一组连续的块。
读取某个磁盘块时,需要移动磁头。访问的两个磁盘块相隔越远,移动磁头所需时间就越长。
优点:连续分配的文件在顺序读/写时速度最快。
缺点:采用连续分配的文件不方便拓展;存储利用率低,会产生难以利用的磁盘碎片。( 类似外部碎片,可以采用紧凑的方法来处理碎片, 但是需要耗费很大的时间代价 )
链接分配采取离散分配的方式,可以为文件分配离散的磁盘块。
隐式链接:除文件的最后一个盘块之外,每个盘块中都存有指向下一个盘块的指针。文件目录包括文件第一块的指针和最后一块的指针。
实现文件的逻辑块号到物理块号的转变
用户给出要访问的逻辑块号i,操作系统找到该文件对应的目录项(FCB) …
从目录项中找到起始块号(即0号块),将0号逻辑块读入内存,由此知道1号逻辑块存放的物理块号,于是读入1号逻辑块,再找到2号逻辑块的存放位置…
以此类推。
因此,读入i号逻辑块,总共需要i+1次磁盘I/O。
优点:很方便文件拓展 (挂到文件链尾即可,可通过结束块号找到尾);所有的空闲磁盘块都可以被利用,不会有碎片问题,外存利用率高。
缺点:只支持顺序访问,不支持随机访问,查找效率低,指向下一个盘块的指针也需要耗费少量的存储空间。
显示链接:把用于链接文件各物理块的指针显式地存放在一张表中。即文件分配表(FAT,File Allocation Table)。
实现文件的逻辑块号到物理块号的转变
结论:
优点:很方便文件拓展,不会有碎片问题,外存利用率高,并且支持随机访问。相比于隐式链接来说,地址转换时不需要访问磁盘,因此文件的访问效率更高。
缺点:文件分配表的需要占用一定的存储空间。
索引分配允许文件离散地分配在各个磁盘块中,系统会为每个文件建立一张索引表,索引表中记录了文件的各个逻辑块对应的物理块(索引表的功能类似于内存管理中的页表一―建立逻辑页面到物理页之间的映射关系)。索引表存放的磁盘块称为索引块。文件数据存放的磁盘块称为数据块。
假设某个新创建的文件“aaa”的数据依次存放在磁盘块2 -> 5 -> 13 -> 9 。7号磁盘块作为“aaa”的索引块,索引块中保存了索引表的内容。
注:在显式链接的链式分配方式中,文件分配表FAT是一个磁盘对应一张。而索引分配方式中,索引表是一个文件对应一张。
可以用固定的长度表示物理块号(如:假设磁盘总容量为1TB=2^40B,磁盘块大小为1KB,则共有 2^30 个磁盘块,则可用4B表示磁盘块号),因此,索引表中的“逻辑块号”可以是隐含的。
实现文件的逻辑块号到物理块号的转变?
用户给出要访问的逻辑块号i,操作系统找到该文件对应的目录项(FCB)
从目录项中可知索引表存放位置,将索引表从外存读入内存,并查找索引表即可知道 i 号逻辑块在外存中的存放位置。
可见,索引分配方式可以支持随机访问。文件拓展也很容易实现(只需要给文件分配一个空闲块,并增加一个索引表项即可)。
若每个磁盘块1KB,一个索引表项4B,则一个磁盘块只能存放256个索引项。
如果一个文件的大小超过了256块,那么一个磁盘块是装不下文件的整张索引表的,如何解决这个问题?
如果索引表太大,一个索引块装不下,那么可以将多个索引块链接起来存放。
建立多层索引(原理类似于多级页表)。使第一层索引块指向第二层的索引块。还可根据文件大小的要求再建立第三层、第四层索引块。
解决多层索引对于小文件仍需要多层处理的问题!
多种索引分配方式的结合。
例如,一个文件的顶级索引表中,既包含直接地址索引(直接指向数据块),又包含一级间接索引(指向单层索引表)、还包含两级间接索引(指向两层索引表)。
重点:
对空闲块的管理!
文件卷:将物理磁盘划分为一个个文件卷(逻辑卷,逻辑盘)例如:windows CDE盘
适用于连续分配方式。
空闲链表法
空闲盘块链
适用于离散分配的物理结构。
空闲盘区链
以盘区为单位组成一条空闲链
离散分配、连续分配都使用。
为一个文件分配多个盘块时效率更高
位示图:每个二进制位对应一个盘块。在本例中,“O”代表盘块空闲,“1”代表盘块已分配。
如何分配
若文件需要K个块
如何回收
空闲表法、空闲链表法不适用于大型文件系统,因为空闲表或空闲链表可能过大。
UNIX 系统中采用了成组链接法对磁盘空闲块进行管理。
文件卷的目录区中专门用一个磁盘块作为 “超级块”,当系统启动时需要将超级块读入内存。
并且要保证内存与外存中的 “超级块” 数据一致。
成组链表法详讲:https://blog.csdn.net/Ajay666/article/details/73569654
进行 " create 系统调用 "
需要提供的参数:
操作系统在处理create系统调用时,主要做了两件事:
进行 " delete 系统调用 "
需要提供的参数:
操作系统在处理delete系统调用时,主要做了几件事:
进行 ” open 系统调用 “
需要提供的要参数:
操作系统在处理delete系统调用时,主要做了几件事:
打开文件表
每个进程有自己的打开文件表,系统中也有一张总的打开文件表
进行 ” close 系统调用 “
进行 ” read 系统调用 “
需要提供的参数
操作系统在处理read系统调用时,会从读指针指向的外存中,将用户指定大小的数据读入用户指定的内存区域中。
进行 ” write 系统调用 “
需要提供的参数
索引结点:是一种文件目录瘦身策略。由于检索文件时只需用到文件名,因此可以将除了文件名之外的其他信息放到索引结点中。这样目录项就只需要包含文件名、索引结点指针。
索引结点中设置一个链接计数变量count,用于表示链接到本索引结点上的用户目录项数。
若 count =2,说明此时有两个用户目录项链接到该索引结点上,或者说是有两个用户在共享此文件。
若某个用户决定“ 删除 ”该文件,则只是要把用户目录中与该文件对应的目录项删除,且索引结点的 count 值减1。
若 count > 0,说明还有别的用户要使用该文件,暂时不能把文件数据删除,否则会导致指针悬空。
当 count = 0 时系统负责删除文件。
当 User3 访问 “ ccc ” 时,操作系统判断文件 “ ccc ” 属于 Link 类型文件,于是会根据其中记录的层层查找目录,最终找到 User1的目录表中的 “ aaa ” 表项,于是就找到了文件1的索引结点。(类似Windows快捷方式)
速度慢于硬链接!
为文件设置一个 “ 口令 ” (如: abc112233),用户请求访问该文件时必须提供 “ 口令 ”。
口令一般存放在文件对应的 FCB 或索引结点中。
用户访问文件前需要先输入“口令”,操作系统会将用户提供的口令与FCB中存储的口令进行对比。
如果正确,则允许该用户访问文件。
优点:保存口令的空间开销不多,验证口令的时间开销也很小。
缺点:正确的 “口令” 存放在系统内部,不够安全。
使用某个 “ 密码 ” 对文件进行加密,在访问文件时需要提供正确的 “ 密码 ” 才能对文件进行正确的解密。
优点:保密性强,不需要在系统中存储“密码”
缺点:编码/译码,或者说加密/解密要花费一定时间。
在每个文件的 FCB (或索引结点)中增加一个访问控制列表(Access-Control List, ACL),该表中记录了各个用户可以对该文件执行哪些操作。
从上到下依次是:文件基本操作,文件目录,文件保护,文件逻辑地址,文件物理地址,文件存储空间管理,磁盘设备管理!
用一个例子来辅助记忆文件系统的层次结构:
假设某用户请求删除文件“D:/工作目录/学生信息…xlsx”的最后100条记录。
磁盘:表面由一些磁性物质组成,可以用这些磁性物质来记录二进制数据。
磁道 : 磁盘的盘面被划分成一个个磁道, 一个圈就是一个磁道。
扇区 : 每一个磁道被划分成一个个扇区,每个扇区就是一个个 " 数据块 ",各个扇区存放的数据量相同。
最内侧磁道上的扇区面积最小, 因此数据密度最大
需要把 “ 磁头 ” 移动到想要读/写的扇区所在的磁道。
磁盘会转起来,让目标扇区从磁头下面划过,才能完成对扇区的读/写操作。
可用(柱面号,盘面号,扇区号)来定位任意一个“磁盘块”。"在“文件的物理结构”小节中,我们经常提到文件数据存放在外存中的几号块,这个块号就可以转换成(柱面号,盘面号,扇区号)的地址形式。
可根据该地址读取一个“块”
磁头可以移动的称为活动头磁盘。磁臂可以来回伸缩来带动磁头定位磁道。
磁头不可移动的称为固定头磁盘。这种磁盘中每个磁道有一个磁头。
盘片可以更换的称为可换盘磁盘。
盘片不可以更换的称为固定盘磁盘。
寻找时间(寻道时间)Ts:在读/写数据前,将磁头移动到指定磁道所花的时间。
延退睦潮Tr:通过旋转磁盘,使磁头定位到目标扇区所需要的时间。设磁盘转速为r(单位:转/秒,或转/分),则平均所需的延迟时间Tr =(1/2) * (1/r) = 1/2r
传输时间Tt:从磁盘读出或向磁盘写入数据所经历的时间,假设磁盘转速为r,此次读/写的字节数为b,每个磁道上的字节数为N。则:传输时间Tt=(1/r) * (b/N) = b/(rN)
总的平均存取时间T = Ts + 1/2r + b/(rN)
延迟时间和传输时间都与磁盘转速相关,且为线性相关。而转速是硬件的固有属性,因此操作系统也无法优化延迟时间和传输时间!
但是操作系统的磁盘调度算法会直接影响寻道时间
根据进程请求访问磁盘的先后顺序进行调度。
优点:公平;如果请求访问的磁道比较集中的话,算法性能还算过得去。
缺点:如果有大量进程竞争使用磁盘,请求访问的磁道很分散,则FCFS在性能上很差,寻道时间长。
SSTF 算法会优先处理的磁道是与当前磁头最近的磁道。可以保证每次的寻道时间最短,但是并不能保证总的寻道时间最短。(其实就是贪心算法的思想,只是选择眼前最优,但是总体未必最优)
优点:性能较好,平均寻道时间短。
缺点:可能产生 “ 饥饿 ” 现象。
产生饥饿的原因在于:磁头在一个小区域内来回来去地移动。
为了防止饥饿问题,可以规定,只有磁头移动到最外侧磁道的时候才能往内移动,移动到最内侧磁道的时候才能往外移动。
这就是扫描算法(SCAN)的思想。由于磁头移动的方式很像电梯,因此也叫电梯算法。
优点:性能较好,平均寻道时间较短,不会产生饥饿现象
缺点:
相对于扫描算法,如果在磁头移动方向上已经没有别的请求,就可以立即改变磁头移动方向。
边移动边观察,因此叫 LOOK。
优点:比起SCAN算法来,不需要每次都移动到最外侧或最内侧才改变磁头方向,使寻道时间进一步缩短。
SCAN算法对于各个位置磁道的响应频率不平均,而C-SCAN算法就是为了解决这个问题。
规定只有磁头朝某个特定方向移动时才处理磁道访问请求,而返回时直接快速移动至起始端而不处理任何请求。
优点:比起SCAN来,对于各个位置磁道的响应频率很平均。
缺点:只有到达最边上的磁道时才能改变磁头移动方向,事实上,处理了184号磁道的访问请求之后就不需要再往右移动磁头了;并且,磁头返回时其实只需要返回到18号磁道即可,不需要返回到最边缘的磁道。另外,比起SCAN算法来,平均寻道时间更长。
C-SCAN 算法的主要缺点是只有到达最边上的磁道时才能改变磁头移动方向,并且磁头返回时不一定需要返回到最边缘的磁道上。
C-LOOK算法就是为了解决这个问题。如果磁头移动的方向上已经没有磁道访问请求了,就可以立即让磁头返回,并且磁头只需要返回到有磁道访问请求的位置即可。
优点:比起C-SCAN算法来,不需要每次都移动到最外侧或最内侧才改变磁头方向,使寻道时间进一步缩短。
磁头读入一个扇区数据后需要一小段时间处理,如果逻辑上相邻的扇区在物理上也相邻,
则读入几个连续的逻辑扇区,可能需要很长的“延迟时间”(因为扇片在不停的旋转,写一次转到是才可能继续去读)
若采用交替编号的策略,即让逻辑上相邻的扇区在物理上有一定的间隔,可以使读取连续的逻辑扇区所需要的延迟时间更小。
磁盘的物理地址是 (柱面号、盘面号、扇区号)
为什么不是 (盘面号、柱面号、扇区号)?
在读取地址连续的磁盘块时,可以减少磁头移动消耗的时间。
让相邻盘面的扇区编号 “错位”。
即将多个盘面的磁头指向的扇区编号并不一致即可!
若指向的扇区编号一致:则0号扇面读完要读1号扇面时,1号扇面的磁头仍需要时间激活,因此就会将需要读的扇区错过,进行转动第二圈才能读到
若指向的扇区编号不一致:1号扇面磁头开始读时,由于编号不一致,即此时磁头的指向并不是要读的数据,完全有时间激活磁头,激活完毕顺便将需要读的扇区读了,转一圈即可完成!减少延迟时间!
计算机开机时需要进行一系列初始化的工作,
这些初始化工作是通过执行初始化程序 (自举程序) 完成的。
初始化程序 ( 自举程序 ) 如果直接放在ROM(只读存储器)中。ROM中的数据在出厂时就写入了。并且以后不能再修改,会很不方便。
解决方法:
坏了、无法正常使用的扇区就是“坏块”。
这属于硬件故障,操作系统是无法修复的。应该将坏块标记出来,以免错误地使用到它。
对于简单的磁盘,可以在逻辑格式化时(建立文件系统时)对整个磁盘进行坏块检查,
标明哪些扇区是坏扇区,比如:在FAT表上标明。(在这种方式中,坏块对操作系统不透明)
对于复杂的磁盘,磁盘控制器(磁盘设备内部的一个硬件部件)会维护一个坏块链表。
在磁盘出厂前进行低级格式化(物理格式化)时就将坏块链进行初始化。
会保留一些“备用扇区”,用于替换坏块。
这种方案称为扇区备用。且这种处理方式中,坏块对操作系统透明。
“ I/O ” 就是“ 输入/输出 ”(lnput/Output)
I/O 设备就是可以将数据输入到计算机,或者可以接收计算机输出数据的外部设备,属于计算机中的硬件部件。
UNIX系统将外部设备抽象为一种特殊的文件, 用户可以使用与文件操作相同的方式对外部设备进行操作。
Write:向外部设备写出数据。
Read:向外部设备读入数据。
按使用特性
按传输速率
按信息交换的单位
用于实现对IO设备的控制!
IO设备有机械部件和电子部件组成!
l/O 设备的机械部件主要用来执行具体 l/O 操作。
如我们看得见摸得着的鼠标/键盘的按钮;显示器的LED屏;移动硬盘的磁臂、磁盘盘面。
l/O 设备的电子部件通常是一块插入主板扩充槽的印刷电路板。
CPU 无法直接控制 I/O 设备的机械部件,因此 I/O 设备还要有一个电子部件作为 CPU 和 I/O 设备机械部分之间的 “中介”, 用于实现CPU对设备的控制。
这个电子部件就是 I/O 控制器,又称设备控制器。
CPU可控制 I/O 控制器,又由 I/O 控制器来控制设备的机械部件。
值得注意的小细节:
内存印像I/O VS 寄存器独立编址
即用什么样的方式来控制 I/O 设备的数据读/写
通过 轮询 实现,以读操作为例。
数据传送的单位:每次读/写一个字。
CPU千预的频率
很频繁,I/O操作开始之前、完成之后需要CPU介入,并且在等待l/O完成的过程中CPU需要不断地轮询检查。
数据的流向:
每个字的读/写都需要CPU的帮助。
优点:实现简单。在读/写指令之后,加上实现循环检查的一系列指令即可。(因此才称为“程序直接控制方式”)
缺点:CPU 和 I/O 设备只能I/O串行工作,CPU 需要一直轮询检查,长期处于"忙等"状态,CPU 利用率低。
引入中断机制。
由于 I/O 设备速度很慢,因此在 CPU 发出读/写命令后,可将等待 I/O 的进程阻塞,先切换到别的进程执行。当 I/O 完成后,控制器会向 CPU 发出一个中断信号,CPU检测到中断信号后,会保存当前进程的运行环境信息,转去执行中断处理程序处理该中断。
处理中断的过程中,CPU从 I/O 控制器读一个字的数据传送到 CPU 寄存器,再写入主存。接着,CPU恢复等待 I/O 的进程(或其他进程)的运行环境,然后继续执行。
注意:
CPU干预的频率:
每次 I/O 操作开始之前、完成之后需要 CPU 介入。
等待 I/O 完成的过程中 CPU 可以切换到别的进程执行。
数据传送的单位:每次读/写一个字。
数据的流向:
优点:与 “ 程序直接控制方式 ” 相比,在“中断驱动方式”中,I/O 控制器会通过中断信号主动报告I/O 已完成,CPU不再需要不停地轮询。CPU 和 I/O 设备可并行工作,CPU利用率得到明显提升。
缺点:每个字在 I/O 设备与内存之间的传输,都需要经过 CPU。而频繁的中断处理会消耗较多的CPU时间。
DMA方式(Direct Memory Access,直接存储器存取)。
主要用于块设备的 I/O 控制,相对于 “中断驱动方式 ” 有这样几个改进:
DR (Data Register,数据寄存器):暂存从设备到内存,或从内存到设备的数据。
MAR (Memory Address Register,内存地址寄存器):在输入时,MAR表示数据应放到内存中的什么位置;输出时MAR表示要输出的数据放在内存中的什么位置。
DC(Data Counter,数据计数器):表示剩余要读/写的字节数。
CR (Command Register,命令/状态寄存器):用于存放 CPU 发来的 I/O 命令,或设备的状态信息。
CPU干预的频率:仅在传送一个或多个数据块的开始和结束时,才需要CPU干预。
数据传送的单位:每次读/写一个或多个块
每次读写的只能是连续的多个块,且这些块读入内存后在内存中也必须是连续的
数据的流向:
优点:数据传输以“块”为单位,CPU 介入频率进一步降低。数据的传输不再需要先经过 CPU 再写入内存,数据传输效率进一步增加。CPU 和 I/O 设备的并行性得到提升。
缺点:CPU 每发出一条 I/O 指令,只能读/写一个或多个连续的数据块。
如果要读/写多个离散存储的数据块,或者要将数据分别写到不同的内存区域时,CPU要分别发出多条 I/O 指令,进行多次中断处理才能完成。
通道:一种硬件,可以理解为是 “ 弱鸡版的 CPU ”。通常可以识别并执行一系列的通道指令。
与 CPU 相比,通道可以执行的指令很单一,并且通道程序是放在主机内存中的,也就是说通道与CPU 共享内存。
CPU 干预的频率:极低,通道会根据 CPU 的指示执行相应的通道程序,只有完成一组数据块的读/写后才需要发出中断信号,请求 CPU 干预。
数据传送的单位:每次读/写一组数据块。
数据的流向〈在通道的控制下进行)
优点:CPU、通道、I/O 设备可并行工作,资源利用率很高。
缺点:实现复杂,需要专门的通道硬件支持。
用户层软件:实现与用户交互的接口,向上提供方便易用的库函数。
设备独立性软件:
设备驱动程序:主要负责对硬件设备的具体控制,将上层发出的一系列命令(如read/write)转化成特定设备“能听得懂”的一系列操作。包括设置设备寄存器,检查设备状态等
中断处理程序:进行中断处理。当I/O任务完成时,I/O控制器会发送一个中断信号,系统会根据中断信号类型找到相应的中断处理程序并执行。中断处理程序的处理流程如下:
硬件:执行 I/O 操作,有机械部分、电子部分组成。不同的I/O设备有不同的硬件特性,具体细节只有设备的厂家才知道。因此厂家需要根据设备的硬件特性设计并提供相应的驱动程序。
操作系统系统可以采用两种方式管理逻辑设备表(LUT) :
I/O 核心子系统要实现的功能其实就是中间三层要实现的功能。
I/O调度、设备保护、假脱机技术(SPOOLing技术)、设备分配与回收、缓冲区管理(即缓冲与高速缓存)
I/O 调度:用某种算法确定一个好的顺序来处理各个 I/O 请求。
如:磁盘调度(先来先服务算法、最短寻道优先算法、SCAN算法、C-SCAN算法、LOOK算法、C-LOOK算法)。当多个磁盘I/O请求到来时,用某种调度算法确定满足l/O请求的顺序。
同理,打印机等设备也可以用先来先服务算法、优先级算法、短作业优先等算法来确定I/O调度顺序。
设备保护:
操作系统需要实现文件保护功能,不同的用户对各个文件有不同的访问权限。
在 UNIX 系统中,设备被看做是一种特殊的文件,每个设备也会有对应的FCB。
当用户请求访问某个设备时,系统根据FCB中记录的信息来判断该用户是否有相应的访问权限,以此实现“设备保护”的功能。(参考 文件保护 小结)
手工操作阶段:主机直接从I/O设备获得数据,由于设备速度慢,主机速度很快。人机速度矛盾明显,主机要浪费很多时间来等待设备
批处理阶段引入了脱机输入/输出技术(用磁带完成)
引入脱机技术后,缓解了CPU与慢速I/O设备的速度矛盾。另一方面,即使CPU在忙碌,也可以提前将数据输入到磁带;即使慢速的输出设备正在忙碌,也可以提前将数据输出到磁带。
脱机:脱离主机的控制进行输入输出操作!
“假脱机技术”,又称“SPOOLing 技术”,用软件的方式模拟脱机技术。SPOOLing 系统的组成如下:
要实现SPOOLing 技术,必须要有多道程序技术的支持。系统会建立“输入进程”和“输出进程”。
独占式设备:只允许各个进程串行使用的设备。一段时间内只能满足一个进程的请求。
共享设备:允许多个进程“同时”使用的设备(宏观上同时使用,微观上可能是交替使用)。可以同时满足多个进程的使用请求。
打印机是一种 " 独占式设备 ",但是可以用SPOOLing技术改造成 " 共享设备 "。
独占式设备的例子:若进程1正在使用打印机,则进程2请求使用打印机时必然阻塞等待
当多个用户进程提出输出打印的请求时,系统会答应它们的请求,但是并不是真正把打印机分配给他们而是由假脱机管理进程为每个进程做两件事:
当打印机空闲时,输出进程会从文件队列的队头取出一张打印请求表,并根据表中的要求将要打印的数据从输出井传送到输出缓冲区,再输出到打印机进行打印。用这种方式可依次处理完全部的打印任务
虽然系统中只有一个台打印机,但每个进程提出打印请求时,系统都会为在输出井中为其分配一个存储区(相当于分配了一个逻辑设备),使每个用户进程都觉得自己在独占一台打印机,从而实现对打印机的共享。
设备的固有属性可分为三种:独占设备、共享设备、虚拟设备。
设备分配算法:
从进程运行的安全性上考虑,设备分配有两种方式:
1、安全分配方式:为进程分配一个设备后就将进程阻塞,本次 I/O 完成后才将进程唤醒。
一个时段内每个进程只能使用一个设备
优点:破坏了 “ 请求和保持 ” 条件,不会死锁。
缺点:对于一个进程来说,CPU 和 I/O 设备只能串行工作。
2、不安全分配方式:进程发出 I/O 请求后,系统为其分配 I/O 设备,进程可继续执行,之后还可以发出新的 I/O 请求。只有某个 I/O 请求得不到满足时才将进程阻塞。
一个进程可以同时使用多个设备
优点:进程的计算任务和 I/O 任务可以并行处理,使进程迅速推进。
缺点:有可能发生死锁(死锁避免、死锁的检测和解除)。
静态分配:进程运行前为其分配全部所需资源,运行结束后归还资源。破坏了“请求和保持”条件,不会发生死锁
动态分配:进程运行过程中动态申请设备资源
设备、控制器、通道之间的关系
一个通道可控制多个设备控制器,每个设备控制器可控制多个设备。
设备控制表(DCT)
系统为每个设备配置一张DCT,用于记录设备情况。
控制器控制表(COCT)
每个设备控制器都会对应一张COCT。操作系统根据COCT的信息对控制器进行操作和管理。
通道控制表(CHCT)
通道控制表(CHCT):每个通道都会对应一张CHCT。操作系统根据CHCT的信息对通道进行操作和管理。
系统设备表(SDT)
记录了系统中全部设备的情况,每个设备对应一个表目。
缺点:
建立逻辑设备名与物理设备名的映射机制,用户编程时只需提供逻辑设备名。
某用户进程第一次使用设备时使用逻辑设备名向操作系统发出请求,操作系统根据用户进程指定的设备类型(逻辑设备名)查找系统设备表,找到一个空闲设备分配给进程,并在LUT中增加相应表项。
如果之后用户进程再次通过相同的逻辑设备名请求使用设备,则操作系统通过LUT表即可知道用户进程实际要使用的是哪个物理设备了,并且也能知道该设备的驱动程序入口地址。
逻辑设备表的设置问题:
整个系统只有一张LUT:各用户所用的逻辑设备名不允许重复,适用于单用户操作系统
每个用户一张LUT:不同用户的逻辑设备名可重复,适用于多用户操作系统
缓冲区是一个存储区域,可以由专门的硬件寄存器组成,也可以利用内存作为缓冲区。
使用硬件作为缓冲区的成本较高,容量较小, 一般仅用在对速度要求非常高的场合。
比如联想寄存器 ( 快表 ) 就是硬件作为缓冲区。
一般情况下, 更多的是利用内存作为缓冲区。
缓冲区的作用:
假设某用户进程请求某种块设备读入若干块的数据。若采用单缓冲的策略,操作系统会在主存中为其分配一个缓冲区(若题目中没有特别说明,一个缓冲区的大小就是一个块)。
当缓冲区数据非空时,不能往缓冲区冲入数据,只能从缓冲区把数据传出;
当缓冲区为空时,可以往缓冲区冲入数据,但必须把缓冲区充满以后,才能从缓冲区把数据传出。
假设某用户进程请求某种块设备读入若干块的数据。若采用双缓冲的策略,操作系统会在主存中为其分配两个缓冲区(若题目中没有特别说明,一个缓冲区的大小就是一个块)。
结论:采用双缓冲策略,处理一个数据块的平均耗时为Max (T, C + M)
注:管道通信中的“管道”其实就是缓冲区。要实现数据的双向传输,必须设置两个管道
将多个大小相等的缓冲区链接成一个循环队列。
注:以下图示中,橙色表示已充满数据的缓冲区,绿色表示空缓冲区。
缓冲池由系统中共用的缓冲区组成。
这些缓冲区按使用状况可以分为:
根据一个缓冲区在实际运算中扮演的功能不同,又设置了四种工作缓冲区: