个人博客:https://uesugier11.gitee.io/uesugi-er11/

认识操作系统

操作系统目标及作用

现代计算机中的计算机硬件:输入设备+输出设备+存储器+运算器+控制器

  • 操作系统(Operating System,OS):是管理计算机硬件与软件资源的系统软件,也是计算机系统的内核与基石。

    • 操作系统处理管理与配置内存
    • 决定系统资源供需的优先次序
    • 控制输入与输出设备
    • 操作网络与管理文件系统等基本事务
    • 提供一个让用户与系统交互的操作界面

操作系统的目标

  1. 方便性
  2. 有效性
  3. 可扩充性
  4. 开放性

操作系统的作用

  1. 作为用户与计算机硬件系统之间的接口:OS处于用户与计算机硬件系统之间,用户通过OS来使用计算机系统。
  2. 作为计算机系统资源的管理者:管理计算机资源,这些资源包括CPU、内存、磁盘驱动器、打印机等。
  3. 实现了对计算机资源的抽象:为其他软件软件提供服务,操作系统与软件进行交互,以便为其分配运行所需的任何必要资源。

操作系统发展过程

未配置操作系统计算机系统

  1. 人工操作
  2. 脱机输入/输出(Off-Line I/O)方式
    1. 脱机IO:事先将装有用户程序和数据的纸带装入纸带输入机,在一台外围机的控制下,把纸带上的数据输入到磁带上。当CPU需要这些程序和数据时,再从磁带上高速地调入内存;
    2. 联机IO:在主机的直接控制下进行输入/输出的方式,称为联机输入/输出(On-Line I/O)方式

单道批处理系统

  • 具体的工作过程是首先由监督程序将磁带上的第一个作业装入内存,并把运行控制权交给作业;该作业处理完时,又把控制权交给监督程序,再有监督程序把磁带的第二个作业调入内存等等。可以看成是串行的。

  • 优点:解决人机矛盾和CPU与IO设备速度不匹配问题,提高系统资源的利用率和系统吞吐量。

  • 缺点:不能充分的利用系统资源,现很少使用。

多道批处理系统

  • 用户所提交的作业先放在外存上,并排成一个对列(后备对列),由作业调度程序按照一定的算法,从后备对列中选择若干个作业调入内存,使其共享CPU和系统中的各种资源。同时在内存中装入若干程序,这样可以在A程序运行时,利用其IO操作而暂停的CPU空挡时间,再调度另一道程序B运行,同样可以利用B程序在IO操作时调用CPU空档调用程序C运行,使用多道程序交替运行,始终保持CPU忙碌的状态。
  • 优势:资源利用率高,使CPU始终处于忙碌的状态,提高内存的利用率,提高IO利用率;系统吞吐量大(CPU和其资源始终保持忙碌的状态,仅在作业完成时或者运行不下去的时候才切换,系统开销小)。
  • 缺点:平均周转时间长,无交互能力。

分时系统(Time Sharing System)

分时系统概念

在一台主机上连接了多个配有显示器和键盘的终端并由此所组成的系统,该系统允许多个用户同时通过自己的终端,以交互方式使用计算机,共享主机中的资源。

分时系统特征

  1. 多路性:多用户同时在各自终端上使用同一CPU。
  2. 独立性:用户可彼此独立操作,互不干扰,互不混淆。
  3. 及时性:用户在短时间内可得到系统的及时回答。
  4. 交互性:用户与系统进行人机对话。

实时系统(Real Time System)

实时系统概念

系统能及时响应外部事件的请求,在规定的时间内完成对该事件的处理,并控制所有实时任务协调一致地运行。

实时系统的类型

  1. 工业(武器)控制系统
  2. 信息查询系统
  3. 多媒体系统
  4. 嵌入式系统

实时任务的类型

  1. 周期性实行任务和非周期性实时任务。
  2. 硬实时任务和软实时任务。

实时任务特征

  1. **多任务:**由于真实世界的事件的异步性,能够运行许多并发进程或任务是很重要的。多任务提供了一个较好的对真实世界的匹配,因为它允许对应于许多外部事件的多线程执行。系统内核分配CPU给这些任务来获得并发性。
  2. **抢占调度:**真实世界的事件具有继承的优先级,在分配CPU的时候要注意到这些优先级。基于优先级的抢占调度,任务都被指定了优先级,在能够执行的任务(没有被挂起或正在等待资源)中,优先级最高的任务被分配CPU资源。换句话说,当一个高优先级的任务变为可执行态,它会立即抢占当前正在运行的较低优先级的任务。
  3. **任务间的通讯与同步:**在一个实时系统中,可能有许多任务作为一个应用的一部分执行。系统必须提供这些任务间的快速且功能强大的通信机制。内核也要提供为了有效地共享不可抢占的资源或临界区所需的同步机制。
  4. **任务与中断之间的通信:**尽管真实世界的事件通常作为中断方式到来,但为了提供有效的排队、优先化和减少中断延时,我们通常希望在任务级处理相应的工作。所以需要在任务级和中断级之间存在通信。

操作系统的基本特征

并发(Concurrence)

  • 并行:指两个或多个事件在同一时刻发生

  • 并发:指两个或多个事件在同一时间间隔内发生

  • 具体地说:并发指在一段时间内宏观上有多个程序在同时运行,但在单处理机系统中,每一时刻却仅能有一道程序执行,故在微观上这些程序是分时地交替执行。
    若计算机系统有多个处理机,这些可以并发执行的程序便可以被分配到多个处理机上,实现并行执行。即利用每一个处理机来处理一个可并发执行的程序。

  • 引入概念**【进程】**:指在系统中能独立运行 并作为资源分配的基本单位,它是由一组机器指令、数据和堆栈等组成的,是一个能独立运行的活动实体。

  • 在一个没有引入进程的系统中,属于同一个应用程序的计算机程序和 I/O程序之间只能是顺序执行,也就是计算机程序执行告一段落后,才允许I/O程序执行;反之,在程序执行I/O操作时,计算程序也不能执行。-----------为计算程序和I/O程序分别建立一个进程(Process)后,这两个程序就可以 并发执行。

共享(Sharing)

  • 在OS环境下的资源共享或称为资源复用,指的是系统中的资源可供内存中多个并发执行的进程共同使用。 -------在宏观上既限定了时间(进程在内存期间),也限定了地点(内存)。
  • 实现资源共享的两种方式
    • 互斥共享方式:系统中的某些资源:如打印机、磁带机等,虽然可以提供给多个进程(线程)使用,但是应规定在一段时间内,只允许一个进程访问该资源。
      -------“临界资源”:一段时间内只允许一个进程访问的资源
    • 同时访问方式:系统中还有另外一些资源,允许在一段时间内由多个进程“同时”对它们进行访问。 ------这里的“同时”也就是前面讲的在微观下交替进行的。
      典型的例子:磁盘设备!

**并发和共享是OS的两个最基本的特征!没有并发和共享就谈不上虚拟和异步; **

虚拟(Virtual)

虚拟和异步是依赖于并发特性的。

所谓虚拟(Virtual)是指通过某种技术把一个物理实体变成为若干个逻辑上的对应物。
物理实体是实际存在的东西,逻辑实体是虚的,它并不存在,但是用户却感觉它存在。
用于实现虚拟的技术称为虚拟技术,在操作系统中利用了两种方式实现虚拟技术:时分复用技术和空分复用技术。

时分复用技术

时分复用技术概念:将资源在不同的时间片内分配给各进程以使该资源被重复利用,从而提高资源的利用率。如采用时分复用的虚拟处理机,能够在不同的时间片内处理多个用户的请求,从而使得用户感觉自己独占主机,而处理机在这期间也被充分的利用。

  1. 虚拟处理机技术:一台处理机,通过时分复用的方法,能实现同时(宏观上)为多个用户服务,亦即,利用多道程序设计技术,可将一台物理上的处理机虚拟为多台逻辑上的处理机 ---- 虚拟处理机。
  2. 虚拟设备技术:通过时分复用的方法,将一台物理I/O设备虚拟为多台逻辑上的I/O设备,并允许每个用户占用一台逻辑上的I/O设备。
  • 这样可使原来仅允许在一段时间内由一个用户访问的设备(即临界资源),变为允许多个用户“同时”访问的共享设备

  • 当一种资源在时间上复用时,不同的程序或用户轮流使用它。时分复用技术通过利用处理及的空闲时间运行其他程序,提高了处理机的利用率

空用复用技术

  • 实质上就是每次只把用户程序的一部分调入内存运行,运行完成后将该部分换出,再换入另一部分到内存中执行,通过这样的置换功能,实现了用户程序的各个部分分时地进入内存运行。

  • 让同一个频段在不同的空间内得到重复利用。空分复用技术利用存储器的空闲空间分区域分存放和运行其他多道程序,以此来提高内存的利用率。

注意:采用分时复用技术,每台虚拟设备的平均速度必然等于或低于物理设备速度的1/N,同理,采用空分复用技术,一台虚拟设备平均占用的空间必然等于或低于物理设备所拥有空间的1/N

异步

  • 由于资源等因素的限制,使进程的执行通常都不可能“一气呵成”,而是以“走走停停”的方式运行。

    对于内存中的每个进程,在何时能获得处理机执行,何时又因提出某种资源请求而暂停,以及进程以怎样的速度向前推进,每道程序总共需要多少时间才能完成等等,都是不可预知的

    进程是以人们不可预知的速度向前推进的,此即进程的异步性

操作系统的主要功能

处理机管理功能

操作系统关于进程方面管理任务有如下几种:进程控制进程同步进程通信调度

进程控制

  • 为作业创建进程、撤销(终止)已结束的进程,控制进程在运行过程中的状态转换。

进程同步

  • 为了使多进程同时运行时协调,有两种方式

    • 进程互斥方式:进程在对临界资源进行访问时,应采用互斥方式。(临界资源加锁实现,关锁时禁止访问;锁开时允许访问。
    • 进程同步方式:相互合作去完成共同任务的进程间,由同步机构对他们的执行次序加以协调。(信号量机制

进程通信

  • 实现相互合作进程之间的信息交换。

调度

  1. 作业调度:从后备队列中按照一定算法选择出若干个作业,为他们分配运行所需资源,讲作业调入内存后,分别建立与之对应的进程,使它们成为可能获得处理机的就绪进程,并将他们插入就绪队列中。
  2. 进程调度:从进程就绪队列中按照一定算法选出一个进程,将处理机分配给他,并为他设置运行现场,使其投入执行。

存储器管理功能

内存管理主要功能:内存分配内存保护地址映射内存扩充

内存分配

  1. 作用:为每道程序分配内存空间;提高存储器利用率,尽量减少内存空间碎片。
  2. 两种内存分配方式
    1. 动态内存分配:每个作业所要求的基本内存空间也是在装入时确定的,但允许作业在运行过程中继续申请新的附加内存空间,以适应程序和数据的动态增长,也允许作业在内存中“移动”。
    2. 静态内存分配:每个作业的内存空间是在作业装入时确定的;在作业装入后的整个运行期间,不允许该作业再申请新的内存空间,也不允许作业在内存中“移动”。
  3. 内存分配机制应具有的结构和功能:内存分配数据结构、内存分配功能、内存回收功能。

内存保护

  1. 主要作用:确保每道用户程序都只在自己的内存空间内运行,彼此互不干扰;绝不允许用户程序访问操作系统的程序和数据;也不允许用户程序转移到非共享的其它用户程序中去执行。
  2. 内存保护机制:设置两个界限寄存器,分别用于存放正在执行程序的上界和下界。系统对每条指令所要访问的地址进行检查,如果发生越界,产生越界中断请求,停止该程序的执行。

地址映射

  • 程序的逻辑地址通常从0开始,而物理地址不从0开始,因此需要一个映射转换过程。主要功能即为:将地址空间的逻辑地址转换为内存空间与之对应的物理地址。

内存扩充

  1. 借助于虚拟存储技术,从逻辑上去扩充内存容量。
  2. 为了能在逻辑上扩充内存,系统必须具有内存扩充机制,用于实现下述各功能:
    1. 请求调入功能:允许在装入一部分用户程序和数据的情况下,便能启动该程序运行。在程序运行过程中,若发现要继续运行时所需的程序和数据尚未装入内存,可向 OS 发出请求,由 OS 从磁盘中将所需部分调入内存,以便继续运行。
    2. 置换功能:若发现在内存中已无足够的空间来装入需要调入的程序和数据时,系统应能将内存中的一部分暂时不用的程序和数据调至盘上,以腾出内存空间,然后再将所需调入的部分装入内存。

设备管理功能

  • 主要任务

    • 完成用户进程提出的I/O请求;为用户进程分配其所需的I/O设备;
    • 提高CPU和I/O设备的利用率;提高I/O速度;方便用户使用I/O设备。
  • 为此,设备管理应具有缓冲管理设备分配设备处理等功能。

缓冲管理

  • CPU运行的高速性和I/O低速性间的矛盾自计算机诞生时起便已存在。
    如果在I/O设备和CPU之间引入缓冲,则可有效地缓和CPU和I/O设备速度不匹配的矛盾,提高CPU的利用率,进而提高系统吞吐量。
    因此,在现代计算机系统中, 都毫无例外地在内存中设置了缓冲区,而且还可通过增加缓冲区容量的方法,来改善系统的性能

设备分配

  • 设备分配的基本任务,是根据用户进程的I/O请求、系统的现有资源情况以及按照某种设备分配策略,为之分配其所需的设备。如果在I/O设备和CPU之间,还存在着设备控制器和I/O通道时,还须为分配出去的设备分配相应的控制器和通道。

设备处理

  • 设备处理程序又称为设备驱动程序。基本任务是用于实现CPU和设备控制器之间的通信,即由CPU向设备控制器发出I/O命令,要求它完成指定的I/O操作;反之,由CPU接收从控制器发来的中断请求,并给予迅速的响应和相应的处理。
  • 处理过程是:设备处理程序首先检查I/O请求的合法性,了解设备状态是否是空闲的,了解有关的传递参数及设置设备的工作方式。然后向设备控制器发出I/O命令,启动I/O设备去完成指定的I/O操作。

文件管理功能

  • 文件管理的主要任务是对用户文件和系统文件进行管理以方便用户使用并保证文件的安全性。
    为此,文件管理应具有对文件存储空间的管理目录管理文件的读/写管理以及文件的共享与保护等功能。

文件存储空间的管理

  • 由文件系统对诸多文件及文件的存储空间,实施统一的管理。其主要任务是为每个文件分配必要的外存空间,提高外存的利用率,进而提高文件系统的存、取速度。
  • 为此,系统中应设置用于记录文件存储空间使用情况的数据结构,以供分配存储空间时参考,还应具备对存储空间进行分配和回收的功能

目录管理

  • 目录管理的主要任务,是为每个文件建立一个目录项,并对众多的目录项加以有效的组织,以实现方便的按名存取。
  • 通常由系统为每个文件建立一个目录项。目录项包括文件名、文件属性、文件在磁盘上的物理位置等。由若干个目录项又可构成一个目录文件。即用户只须提供文件名, 即可对该文件进行存取。

文件的读/写管理

  • 该功能是根据用户的请求,从外存中读取数据;或将数据写入外存。由于读和写操作不会同时进行,故可合用一个读/写指针。

文件的共享与保护

  • 防止未经核准的用户存取文件;防止冒名顶替存取文件;防止以不正确的方式使用文件。

操作系统与用户之间的接口

  • 引题:

  • 包含关系:

用户接口

  • 为了方便用户直接/间接控制自己的作业,操作系统提供了命令接口,该接口又分为联机用户接口脱机用户接口图形用户接口3种
联机用户接口
  • **用户说一句,系统做一句。**用户可通过先后键入不同命令的方式,来实现对作业的控制,直至作业完成。
脱机用户接口
  • **用户说一堆,系统做一堆。**该接口是为批处理
图形用户接口
  • 采用图形化操作界面。

程序接口

  • 由一组系统调用命令(简称系统调用,也称广义指令)组成。
  • 系统调用的目的:请求系统服务
  • 系统调用/程序接口/程序接口 是操作系统提供给编程人员的接口 选C
  • 系统调用≠库函数 选A

注意事项:

操作系统的结构

传统OS结构

无结构OS

  • OS的各部分是以最基本的过程存在,每个过程都可随意地调用其他过程

模块化结构OS

基本概念
  • 对OS的各部分经过了划分,行程若干个具有一定独立性和大小的模块

模块独立性
  • 模块划分过小→降低本身复杂性,但会引起模块之间联系过多,造成系统混乱
  • 模块划分过大→增加模块内部复杂性,使内部联系增加
  • 内聚性:指模块内部各部分间联系的亲密程度。内聚性高→模块独立性强
  • 耦合度:指模块间相互联系和相互影响的程度。耦合度低→模块独立性强。
模块接口法的优缺点
  • 优点

    • 提高OS设计的正确性、可理解性和可维护性
    • 增强OS的可适应性
    • 加速OS的开发过程
  • 不足/存在的问题
    • 接口规定困难
    • 存在无序性

分层式结构OS

分层式结构概念
  • 目标系统An和裸机系统A0之间有许多层次软件:A1,A2,A3…An-1,使An通过这几层最终能在A0上运行。

分层结构的优缺点
  • 优点

    • 易保证系统的正确性:自下而上的设计方式使所有设计的决定都是有序的或者说是建立在较为可靠的基础上的,这样比较容易保证整个系统的正确性。
    • 具有易扩充和易维护性:在系统中增加、修改或替换一个层次中模块或整个层次时,只要不改变相应层次间接口,就不会影响其他层次,这就使得维护和扩充变得easy。
  • 缺点
    • 系统效率降低:由于层次结构是分层单项依赖的,必须在每层间都建立层次间的通信机制,OS执行一个功能就得由上到下穿越多层次,就会增加系统通信开销,从而导致系统效率降低。

客户端/服务端

客户/服务器模式由来、组成和类型

  • 客户机:每台客户机都是一个自主计算机,具有一定处理能力,客户进程在其上运行,平时处理一些本地业务,也可以发送一个消息给服务器用以请求某项服务
  • 服务器:通常是一台规模较大的机器,含有网络文件系统或数据库系统,应能为网上所有用户提供一种或多种服务。
  • 网络系统:用于连接所有客户机和服务器,实现他们之间通信和网络资源共享的系统

客户/服务器之间交互

一次完整的交互过程可以分成以下四步:

  1. 客户发送请求消息
  2. 服务器接收消息
  3. 服务器回送信息
  4. 客户机接收消息

客户/服务器模式优点

  1. 数据的分布处理和存储
  2. 便于集中管理
  3. 灵活性和可扩充性
  4. 便于改编应用软件

ps:经常会问到 在微内核OS中,为什么要采用客户/服务器模式?我们答客户/服务器模式的优点即可

微内核

微内核OS的基本概念

微内核技术——把操作系统中更多的成分和功能放到更高的层次(用户模式)中去运行,而留下一个尽量小的内核,用它来完成操作系统最基本的核心功能。

  1. 足够小的内核:微内核≠一个完整的OS,含有:

    1. 与硬件处理紧密相关的部分
    2. 一些较基本的功能
    3. 客户和服务器之间的通信
  2. 基于【客户/服务器模式】
  3. 应用"机制和策略分离"原理
    1. 机制:是指在实现某一功能时的具体规定或说原则
    2. 策略:在机制的基础上,借助某些参数和算法,用以实现该功能的优化,或达到不同的目标
  4. 采用面向对象技术

微内核的基本功能

基于【机制与策略分离】的原理,将机制部分以及与硬件紧密相关的部分放入微内核中。由此微内核通常具有如下几个方面功能:

  1. 进程(线程)管理
  2. 低级存储器管理
  3. 中断和陷入处理

微内核操作系统的优点

  1. 提高了系统的可扩展性
  2. 增强了系统的可靠性
  3. 可移植性强
  4. 提供了对分布式系统的支持
  5. 新技术——融入了面向对象技术

微内核操作系统存在的问题

  1. 缺点:微内核操作系统的运行效率相较早期操作系统有所降低
  2. 改进方法:可以把一些常用的操作系统基本功能由服务器移入微内核中

进程和线程

前趋图和程序执行

前趋图

  • 前趋图(Precedence Graph)是指一个有向无循环图,可记为DAG,用于描述进程之间执行的先后顺序

程序执行

程序顺序执行

  • 特征:

    • 顺序性:所谓顺序性是指,处理机严格的按照程序所规定的顺序执行,一个操作的开始必须在其前一个操作结束之后。
    • 封闭性:所谓封闭性是指,程序在执行的时候独占全机的资源
    • 可再现性:所谓可再现性是指,只要初始条件运行环境系统相同,其运行结果一定是一样的。

程序并发执行

  • 事实上,只有不存在前趋关系的程序之间才有可能并发执行,否则无法并发执行

  • 特征:

    • 间断性:所谓间断性指的是,由于多个程序对资源的要求产生的制约性,而导致的某一个程序在运行时等待资源的情况。 比如有两个程序都需要使用打印机这个资源,如果其中的一个程序已经占用,而另一个必须等待。这样后者表现出来的就是程序运行时的间断性。
    • 失去封闭性:所谓失去封闭性指的是,由于多个程序并发执行会共享资源,从而导致各个程序运行环境会失去封闭性。
    • 不可再现性:所谓不可再现性是指相同的输入,由于资源的共享,导致最后的输出结果不同。

进程的描述

进程的定义和特征

定义

  • 程序:是静态的,是一个存放在磁盘里的可执行文件,是一系列的指令集合

  • 进程:是动态的,是进程实体的运行过程,是系统进行资源分配和调度的一个独立单位。

    其中,进程实体包含三部分:程序段相关的数据段PCB

  • 程序进程关系:一个程序多次执行会对应多个进程,比如说QQ可以登录三个账号。那么既然都是QQ,那OS是怎么区分不同进程的呢?当进程被创建的时候,OS会为该进程分配一个唯一的,不重复身份证——PID(Process ID,进程ID)

  • 进程控制块(PCB):OS需要记录进程PID,分配了哪些资源,运行情况,那么这些信息需要记录到哪里呢?答案就是进程控制块——PCB。

    • 作用:PCB的作用是使一个在多道程序环境下不能独立运行的程序(含数据)成为一个能独立运行的基本单位,一个能与其他进程并发执行的进程。下面是具体作用:

      • 作为独立运行基本单位的标志:在进程的整个生命周期中,系统总是通过PCB对进程进行控制的,亦即系统是根据 进程的PCB感知该进程的存在的,所以,PCB是进程存在的唯一标志。
      • 能实现间断性运行方式
      • 提供进程管理所需要的信息
      • 提供进程调度所需要的信息
      • 实现与其他进程的同步与通信

特征

  • 主要由:动态性,并发性,独立性,异步性,结构性组成。

进程的基本状态及转换

进程的状态

进程状态有五种:创建状态,就绪状态,执行状态,阻塞状态,终止状态;其中就绪、执行、阻塞是三种基本状态。

创建态、就绪态

  • 创建状态:对于处于创建态的进程,当其获得了所需的资源以及对其PCB的初始化工作完成后,便可由创建态转入就绪态
  • 就绪状态:进程已经处于准备好运行的状态了,只需要CPU临门一脚便可以立即执行。

运行态

  • 运行态:指进程已经获CPU,其程序正在执行的状态。在单处理机系统中,只有一个进程处于执行状态,而在多处理机系统中,有多个进程处于执行状态。

阻塞态

  • 阻塞态:指正在执行的进程由于发生某事件(I/O请求,申请缓冲区失败等)暂时无法继续执行时的状态,亦即进程的执行收到阻塞。

终止态

  • 终止态:即一个进程达到了自然结束点,或是出现了无法克服的错误,或是被操作系统所终结,或是被其他有终止权的进程所终结,它将进入终止状态。

进程的转换

三态模型

  • 基本的三态模型:

五态模型

  • 未引入挂起的五态模型:

引起进程状态转换的具体原因如下:

  • NULL→新建态:执行一个程序,创建一个子进程。

  • 新建态→就绪态:当操作系统完成了进程创建的必要操作,并且当前系统的性能和虚拟内存的容量均允许。

  • 就绪态→运行态:进程被调度

  • 运行态→终止态:当一个进程到达了自然结束点,或是出现了无法克服的错误,或是被操作系统所终结,或是被其他有终止权的进程所终结。

  • 运行态→就绪态:运行时间片到;出现有更高优先权进程。

  • 运行态→阻塞态:等待使用资源;如等待外设传输;等待人工干预。

  • 阻塞态→就绪态:申请资源被分配,或等待的事件已经发生了

  • 就绪态→终止态:未在状态转换图中显示,但某些操作系统允许父进程终结子进程。

  • 阻塞态→终止态:未在状态转换图中显示,但某些操作系统允许父进程终结子进程。

  • 终止态→NULL:完成善后操作。

七态模型

接下来,我们引入了两个操作:挂起和激活

当挂起操作作用于某个进程时,该进程将被将被挂起,意味着此时该进程处于静止状态;

假如进程正在执行,他将暂停执行。

假如进程原本就处于就绪状态,则该进程此时暂不接受调度。

与挂起操作对应的就是激活啦。

  • 引入挂起后的进程状态转换五态模型:

  • 引入创建和终止——七态模型:

引起进程状态转换的具体原因如下:

  • 等待态—→挂起等待态:如果当前不存在就绪进程,那么至少有一个等待态进程将被对换出去成为挂起等待态;操作系统根据当前资源状况和性能要求,可以决定把等待态进程对换出去成为挂起等待态。
  • 挂起等待态—→挂起就绪态:引起进程等待的事件发生之后,相应的挂起等待态进程将转换为挂起就绪态。
  • 挂起就绪态—→就绪态:当内存中没有就绪态进程,或者挂起就绪态进程具有比就绪态进程更高的优先级,系统将把挂起就绪态进程转换成就绪态。
  • 就绪态—→挂起就绪态:操作系统根据当前资源状况和性能要求,也可以决定把就绪态进程对换出去成为挂起就绪态。
  • 挂起等待态—→等待态:当一个进程等待一个事件时,原则上不需要把它调入内存。但是在下面一种情况下,这一状态变化是可能的。当一个进程退出后,主存已经有了一大块自由空间,而某个挂起等待态进程具有较高的优先级并且操作系统已经得知导致它阻塞的事件即将结束,此时便发生了这一状态变化。
  • 运行态—→挂起就绪态:当一个具有较高优先级的挂起等待态进程的等待事件结束后,它需要抢占 CPU,,而此时主存空间不够,从而可能导致正在运行的进程转化为挂起就绪态。另外处于运行态的进程也可以自己挂起自己。
  • 新建态—→挂起就绪态:考虑到系统当前资源状况和性能要求,可以决定新建的进程将被对换出去成为挂起就绪态。

进程的组织

线性方式

  • 不论进程的状态如何,将所有的PCB连续地存放在内存的系统区。这种方式适用于系统中进程数目不多的情况。

链接方式

  • 系统按照进程的状态将进程的PCB组成队列,从而形成就绪队列、阻塞队列、运行队列等。

索引方式

  • 该方式是线性表方式的改进,系统按照进程的状态分别建立就绪索引表、阻塞索引表等。

进程控制

进程控制的主要功能是对系统中的所有进程实施有效的管理,它具有创建新进程,撤销已有进程,实现进程状态转换等功能;

简单来说,进程控制→实现进程状态转换。

  • 简单的流程图:

OS内核

与硬件紧密相关的模块、各种常用的设备的驱动程序以及运行频率较高的模块,安排在紧靠硬件的软件层次中,将它们常驻内存,通常被称为OS内核也就是OS内核其实本质上是各个模块

原语

  • 我们知道原语的执行具有原子性,它执行过程只能一气呵成,不允许被打断,那么为什么要一气呵成呢?下图即为解释**,假如不一气呵成,就会导致操作系统某些关键的数据结构信息不统一,就会影响操作系统进行别的管理工作**。
  • 如何实现原语的原子性呢?其实是利用了“关中断指令"和”开中断指令";
  • CPU执行了关中断指令后,不会再check中断信号,直到遇到了开中断指令才会再去check;
  • 因此关——开中断之间的指令是连续的,不可中断的,这就实现了原子性;

进程的创建

创建原语:

  1. 申请空白PCB
  2. 为新进程分配所需的资源
  3. 初始化PCB
  4. 将PCB插入就绪队列

哪里会用到进程创建呢?

  1. 用户登录
  2. 作业调度
  3. 提供服务
  4. 应用请求

进程的终止

终止/撤销原语:

  1. 从PCB集合中找到终止进程的PCB
  2. 若该进程正在运行,立刻剥夺CPU,将CPU分配给其他进程
  3. 终止其所有子进程
  4. 将该进程所拥有的所有资源还给父进程或操作系统
  5. 删除PCB

哪里会用到进程终止呢?

  1. 正常结束:进程自己请求终止
  2. 异常结束:非法使用特权指令
  3. 外界干预:用户自己选择杀某进程

进程的阻塞和唤醒

被什么事件所阻塞就会被该事件所唤醒

进程的切换

切换原语:

  1. 运行环境信息存入PCB
  2. PCB移入相应队列
  3. 选择另一个进程执行,并更新其PCB
  4. 根据PCB恢复进程所需的运行环境

这里有一个地方:进程所需的运行环境是什么东西呢?

首先回顾一下我们程序是如何运行的:是通过把我们的代码保存到硬盘然后转入内存,然后CPU去从内存中读取一条条指令,然后我们的程序就跑起来了。那么CPU中有个东西 叫做寄存器。它们有不同的功能,比如可以存放下一条指令地址,也可以存放正在执行的指令,也可以保存暂时运算得到的结果;然而我们知道寄存器并不是很专一的,他有可能会随时被其他的进程使用,那我们之前运算的结果啊,什么保存的指令啊,岂不是都不见了吗?这个时候就会用到我们的PCB了,它能够保持关键的一些信息,也就是运行环境,这样等我们的寄存器又空闲下来了的时候,我们就可以继续我们未完成的指令啦。

进程同步

进程同步与互斥

首先,我们回顾一下我们之前学的进程异步问题,由于并发执行的进程以各自独立,不可预知速度向前推进,我们所得到的结果往往也会不一样,而我们往往有时候需要控制一个事件的发生在一个事件前,那么就需要进程同步

同步也称为直接制约关系,是指为了完成某种任务而建立的两个或多个进程,这些进程因为需要在某些位置上协调他们的工作次序而产生的制约关系

有进程同步就有进程互斥,那么进程互斥又是什么呢?

要明白互斥,就引入了一个概念:临界资源

临界资源,即一个时间段内只允许一个进程使用的资源。对于该资源我们必须互斥的访问,也就是我访问,你不能访问,反之亦然;因此,进程互斥,即指当一个进程访问某临界资源时,另一个想访问此临界资源的进程必须要等待,只有当我正在访问该资源的进程访问结束了,释放掉了,下一个进程才能去访问该资源。

那么我们一般是如何进行对临界资源的访问的呢?

  • 进入区:负责检查是否可以进入临界区(上锁)
  • 临界区:访问临界资源的那段代码
  • 退出区:用于将临界区正被访问的标志恢复为未被访问的标志
  • 剩余区:做其他处理

ps:临界区是进程中访问临界资源的代码段;进入区和退出区是负责实现互斥的代码段;临界区也被称为”临界段"

同步机制遵循的规则

  • 空闲让进:多中选一
  • 忙则等待:互斥访问
  • 有限等待:避免死等
  • 让权等待:避免忙等

硬件同步机制

让硬件同步即为进程互斥的硬件实现方法:中断屏蔽方法,TestAndSet,Swap指令

中断屏蔽方法

  • 在进入锁测试之前关闭中断,直到完成锁测试并上锁之后才能打开中断。

TestAndSet

简称TS指令,也有称TestAndSetLock指令,或TSL指令

用TS指令管理临界区的时候,为每个临界资源设置一个布尔变量lock;

用lock的布尔值就可以判断资源是否空闲,进程能否访问。

  • 优点:实现简单;适用于多处理机的环境
  • 缺点:不满足让权等待

Swap指令

有的地方称为Exchange指令,或XCHG指令;Swap指令用硬件实现,不允许被中断

Swap指令与TS指令在逻辑上其实差不多,但Swap需要两个参数,不需要返回值,TS需要一个共享的变量实现互斥,因此在不同的地方就要用不同的方式去进行进程互斥。

信号量

信号量机制

由于之前的措施方案无法实现进程的“让权等待"问题,因此我们引出了信号量这个概念进行解决;

整型信号量

  • Dijkstra把整型信号量定义为一个用于表示资源数目的整形量S,它仅能通过P\V操作进行访问。(P\V 即 wait & signal)这两个操作是原子操作,是不可以在执行过程中被中断的。
  • 整形信号量存在的问题:不满足让权等待原则

记录型信号量

  • 对于每次的wait操作,意味着进程请求一个单位的该类资源,使系统中可供分配的该类资源数减少一个,因此描述为S→value–;当S→value < 0时,表 示该类资源已分配完毕,因此进程应调用block原语进行自我阻塞,放弃处理机,并插入到信号量链表S→list中
  • 对于每次的signal操作,意味着执行进程释放一个单位资源,使系统中可供分配的该类资源数增加一个,故S→value++操作表示资源数目+1。若+1后还是S→value <= 0,表示在该信号量链表中仍有等待该资源的进程被阻塞了,故应该调用wakeup原语,将S→list链表中的第一个等待进程唤醒。(被唤醒进程从阻塞态→就绪态)

ps:记录型信号量可实现进程互斥、进程同步;如果出现了P(S),V(S)的操作,除非默认说明,默认S为记录型信号量

信号量应用

实现进程互斥

  • 为使得多个进程互斥访问某临界资源【目的】,为该资源设置 互斥信号量【mutex】,初始值为1
  • 将该资源置于P、V操作之间;
  • 注意:利用信号量机制去实现进程互斥时必须保证我们的P\V操作是成对出现的
    • 缺少P会导致系统混乱,不能保证对临界资源互斥访问
    • 缺少V将会使临界资源永远不被释放,导致临界资源永远不被释放,从而使因等待该资源的阻塞的进程不能被唤醒
实现进程同步
  • 进程同步:要让各并发进程按要求有序地推进

现在P1,P2并发执行,由于存在异步性,二者交替推进次序无法控制,是不确定的;

假如我P2的代码4需要用到P1的代码1、2、3运行结果才能执行的话,我就得保证代码4是在代码3后执行;

此即进程同步问题:让本异步并发的进程互相配合,有序推进。

重点:在前操作之后执行V(S),在后操作之前执行P(S);

实现前趋关系

每一对的前趋关系都是一个进程同步的问题(为了保证一前一后的操作)

为了实现这个目标,我们需要做的事情

  1. 为每一对前趋关系各设置一个同步信号量
  2. 在【前操作】的之后相对应的同步信号量执行V
  3. 在【后操作】的之前相对应的同步信号量执行P

管程

引子

  • 为什么要引入管程?因为信号量机制存在一定的问题:编写程序困难,容易出错(废话,这么多PV操作,想不错都难)因此我们在1973年引入管程机制进行处理;

管程的定义

代表资源的数据结构以及由对该共享数据结构实施操作的一组过程所造成的资源管理程序共同构成了一个操作系统的资源管理模块——管程

管程是一种特殊的软件模块,由以下部分组成:

  1. 局部于管程的共享数据结构说明;
  2. 对该数据结构进行操作的一组过程(就是函数)
  3. 对局部于管程的共享数据设置初始值的语句【对数据结构初始化的语句】
  4. 管程有一个名字

发现了吗,管程和我们面向对象的有点相似——可以定义一些数据,可以定义一些对这些数据操作的函数,对属性初始化的语句。

管程的基本特征

  1. 局部于管程的数据只能被局部于管程的过程所访问
  2. 一个进程只有通过调用管程内的过程才能进入管程访问共享数据
  3. 每次仅允许一个进程在管程内执行某个内部过程

什么意思呢?通过1+2我们可以知道,假如我们要修改管程内的数据结构,我们只能够通过调用管程内封装好的方法(函数)去修改数据结构;而3则实现了进程互斥;

管程中的条件变量

我们使用管程实现进程同步需要用到同步工具,如wait,signal;

但仅仅有这两个是不够的,我们知道,每次仅能有一个进程进入管程,假如某进程在管程中被挂起或阻塞就得等很久了,那么解决这问题就引入了——条件变量 condition

怎么用呢?管程中对每个条件变量以说明**:condition x,y**;条件变量的操作是wait,signal;

每个条件变量保存一个链表,用以记录因为这条件变量所阻塞的所有进程,提供2个操作:

x.wait 和 x.signal

  • x.wait:正在调用管程的进程因为x条件需要被阻塞或者挂起,调用x.wait将自己插入到x条件的等待队列上,并释放管程,直到x条件变化。此时其他进程可以使用该管程——让出了入口。
  • x.signal:若正在调用管程的进程发现x条件发生了变化,则调用x.signal重新启动一个因x条件而阻塞或挂起的进程,如果存在多个这样的进程,则选择一个,如果没有,则继续执行原进程,而不产生任何结果。
    • 注意了 这与信号量机制的signal操作不能混为一谈,信号量中的signal需要s++操作,信号量改变了。

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

  1. 假如两个生产者进程并发执行,依次调用insert过程的话:

    1. 第一个生产者进程可以顺利执行完insert函数
    2. 假如在第一个生产者进程还未结束的时候,第二个生产者进程插入进来了,就会把第二个进程阻塞在insert函数后面,排队
  2. 假如两个消费者进程先执行,生产者进程后执行的话:
    1. 第一个进程进来,发现count = 0,那么就执行wait操作,就等待在empty变量相关的队列中
    2. 同样的,第二个就排在了empty变量相关的队列中
    3. 此时假如有个生产者进程进来了,进行生产,把生产品放在了缓冲区当中,假如发现自己的产品是第一个产品,那么此时有可能有别的消费者进程在等待产品,就执行一个唤醒操作,唤醒一个消费者进程。
    4. 然后count–,检查拿走前缓冲区是否是满的,假如是满的,说明有生产者进程需要被唤醒,就来一个signal(full)操作。

Java中类似管程的机制

Java中,如果用关键字【synchronized】描述一个函数,那么这个函数同一个时间段只能被一个线程调用。

经典的进程同步问题

生产者-消费者问题

问题描述

问题分析
  • 缓冲区未空(有产品)V→P消费者消费
  • 缓冲区未满P→V生产者生产

问题的解法步骤
  1. 关系分析,找出题目中描述的各个进程,分析他们之间的同步、互斥关系
  2. 根据各进程的操作流程去确定P、V的大致顺序
  3. 设置信号量,根据题目条件设信号量初值
    1. 互斥信号量初值一般为1
    2. 同步信号量的初值看对应的资源初值值(0/n…)
具体实现
  1. 设置好信号量

    1. 互斥信号量 = 1:实现对缓冲区的互斥访问
    2. 同步信号量empty = n:实现空闲缓冲区的数量
    3. 同步信号量full = 0:代表产品的数量,也即非空缓冲区的数量
  2. 分别在生产者,消费者中放置P、V操作(注意顺序,以及操作的哪个信号量)

思考
  • 想一想 能不能改变相邻P、V操作的顺序呢?

得出结论了:实现互斥的P操作必须放在实现同步的P操作之后;由于V操作并不会导致进程阻塞,因此两个V操作顺序可以交换;

多生产者-多消费者问题

问题描述

注意 这里的多生产者的多 代表的不是数量 而是多个类型

问题分析
  • 找出题目描述的各进程,分析它们之间同步、互斥关系;

    • 互斥关系:对缓冲区(盘子)的访问要互斥的进行
    • 同步关系:父亲放苹果,女儿才能取苹果;母亲放橘子,儿子才能取橘子;盘子为空时,父/母才能放水果,因此需要儿/女进行从盘子取水果;
  • 根据进程的操作流程确定P、V操作大致顺序
    • 实现互斥:在临界区前后分别P、V
    • 实现同步:前操作后V,后操作前P
  • 设置信号量
具体实现
  • 实现互斥访问盘子:mutex = 1
  • 多少个苹果 apple = 0
  • 多少个橘子 orange = 0
  • 盘子中还能放多少水果 plate = 1

以父亲和女儿举例:

父亲首先应该P盘子,查看是否为空,如果为空,则V苹果(苹果++);女儿首先P苹果,查看是否准备好了,有的话就取出,V盘子(盘子++);为了保证互斥,必须在P、V操作之中加入对互斥信号量mutex 的 P、V操作。

思考

可不可以不要互斥信号量mutex?

由于缓冲区大小为1,因此orange,apple,plate三者中最多只有一个是1;这样的话最多只有一个进程的P操作不会被阻塞,可以顺利进入临界区,因此可以顺利执行;

那么假如缓冲区(plate)数量为2呢?

那么父亲和母亲都能访问盘子,有可能出现不同进程写入缓冲区的数据相互覆盖的问题;

得出结论:如果缓冲区的大小大于1,那么就必须专门设置一个互斥信号量mutex来保证互斥的访问缓冲区

吸烟者问题

问题描述

问题分析
  • 找出题目描述的各进程,分析它们之间同步、互斥关系;

    • 同步关系:桌上有组合1→第一个抽烟者取走;组合2→第二个抽烟者取走;组合3→第三个抽烟者取走
    • 互斥关系:桌子只有一个,可以理解为容量为1的缓冲区需要互斥访问
  • 根据进程的操作流程确定P、V操作大致顺序
  • 设置信号量

具体实现
  • 桌上组合1、2、3分别为 offer1 offer2 offer3,他们的初值都是0
  • 设置一个信号量i,用于实现三个抽烟者轮流吸烟

以smoker1为例,首先P(offer1)检查是否有需要的组合1,有的话就拿走去抽,并且告诉供给者抽完了,然后供给者i++,p一下finish,并将下一个组合放在桌上然后v下一个组合;

读者-写者问题

问题描述

问题分析
  • 找出题目描述的各进程,分析它们之间同步、互斥关系;

    • 互斥关系写进程-写进程 写进程-读进程 这两者存在互斥关系。而读进程-读进程不存在互斥
  • 根据进程的操作流程确定P、V操作大致顺序
  • 设置信号量
    • 一个互斥信号量RW:写者访问文件的前后执行PV,读者访问前后执行PV
    • 整型变量count 记录当前有几个读进程在访问文件
    • 一个互斥信号量 mutex :保证对count变量的互斥访问
    • 一个互斥信号量 w :用于实现写优先

具体实现

哲学家进餐问题

问题描述

问题分析
  • 找出题目描述的各进程,分析它们之间同步、互斥关系;

    • 互斥关系:五位哲学家与左右邻居对其中间筷子的访问是互斥的
  • 根据进程的操作流程确定P、V操作大致顺序
  • 设置信号量
    • 一个互斥信号量组chopstick[5]:用于实现对五个筷子的互斥访问。其中哲学家编号**[0…4],各哲学家i的左边筷子编号为i**,右边筷子编号为**(i+1)%5**

然而我们发现 这样子会导致死锁的现象

具体分析

按照之前的方法会发生死锁现象,那我们怎么解决呢?

有三种方法:

  1. 最多允许四个哲学家同时进餐,这样可以保证至少有一个哲学家是可以拿到左右两只筷子的
  2. 要求奇数号哲学家先拿左边的筷子,然后再拿右边的筷子;偶数号哲学家相反。这种方法可以保证如果相邻的两个奇偶号哲学家都想吃饭的话,只有有其中一个可以拿起第一只筷子,而另一个因为拿不到第一个筷子就直接阻塞了。
  3. 仅当一个哲学家左右两只筷子都可用时才允许他抓筷子【这也是后面的破坏请求和保持条件 方法】

下面以第三种方法为例,进行解决

进程通信

进程通信概念

  • 进程通信是指进程之间的信息交换。
  • 进程所拥有的内存地址空间相互独立,换而言之,进程不能直接访问另一个进程的地址空间,然而有时候有需要信息交换,那该怎么办呢?OS提供了一些方法给我们。

共享存储

前提:两个进程对于共享空间的访问必须是互斥的。

  • 基于数据结构共享:各进程公用某些数据结构,实现各进程的信息交换:如生产者-消费者问题中的有界缓冲区。是低级通信方式。
  • 基于存储区共享:在内存中画一片共享存储区,数据形式和存放位置由进程控制而非OS。是高级通信方式。

管道通信

  • 【管道 pipe文件】是指用于连接读写进程的一个共享文件,本质是内存空间中开辟的一个固定大小的缓冲区
  • 管道采取半双工通信,一个时间段内只能实现单向传输;
  • 进程互斥访问管道
  • 管道写满时,写进程的系统调用被阻塞;管道为空时,读进程的系统调用被阻塞
  • 没写满不准读,没读空不准写。
  • 管道数据被读出后被抛弃,意味着读进程最多只能有一个,不然可能会读错数据。

消息传递

进程通过【格式化的消息】为单位,将通信的数据封装在消息中,通过OS提供的发送接收原语,在进程间进行消息传递,完成进程的数据交换。是一种高级通信方式。

  • 直接通信方式:发送进程利用OS提供的发送原语直接把消息发给接收进程/消息直接挂到接收方的消息队列中。

  • 间接通信方式/信箱通信方式:发送进程先发送到中间实体(信箱)中,接受进程再去接收,完成进程间的通信。

线程

线程的概念和特点

  • 什么是线程?为什么要引入线程?

下列是三个进程,他们所占用不同的空间内存和系统资源;假如我们要切换进程的时候,需要用保存/恢复运行环境,还需要切换内存地址空间(更新快表、更新缓存)开销非常大,因此,人们引入了线程机制

  • 引入了线程之后,线程是CPU调度基本单位。一个进程里面可以包含多个线程。线程之间可以并发进行。
  • 但是进程依旧是资源分配基本单位,从属一进程的各线程共享使用进程的资源。
  • 同一个进程内各个进程间并发,不需要切换进程运行环境和内存地址空间,省时省力。

引入线程后的变化

线程的属性

线程的特性与优点

  • 进程间并发,开销很大;线程间并发,开销很小

  • 引入线程机制之后,并发带来的系统开销降低,系统并发性提升

ps:从属于不同进程的线程间切换,也会导致进程间切换,开销也会很大。

  • 从属于同一进程的各个线程共享进程所拥有的资源。
  • 进程间通信必须请求操作系统服务(CPU需要切换到核心态),开销大;同进程下线程通信,无需OS干预,开销更小;

线程实现方式

用户级线程

  1. 线程的管理工作是由谁完成的?

    1. 答 线程的管理工作由应用程序通过线程库来完成的,不是通过OS完成的
  2. 线程切换是否需要CPU从用户态转换为内核态?
    1. 答 在用户态下,由应用程序通过线程库就可以进行线程切换了
  3. OS是否能意识到用户及线程的存在?
    1. 答 意识不到,只有用户能意识到有多个线程
  4. 用户级线程有什么优点和缺点?
    1. 答 优点:用户级线程的切换在用户态可以完成,不需要切换到核心态,系统开销小,效率高
    2. 缺点:假如其中某一个线程被阻塞了,其他线程都会被阻塞,并发度不高;

内核级线程

  1. 线程的管理工作由谁来完成?

    1. 答 由OS内核完成
  2. 线程切换是否需要CPU从用户态转换到内核态?
    1. 答 由于线程调度、切换工作由内核负责,因此在内核级线程的线程切换时需要从用户态转换到内核态的。
  3. OS能否意识到内核级线程的存在?
    1. 答 OS会为每个内核级线程建立对应TCB,然后通过TCB对线程进行管理,因此,OS能够意识到内核级线程的存在
  4. 内核级线程的实现方式的优缺点?
    1. 答 优点:内核级线程是处理机调度的基本单位,而进程只作为资源分配的基本单位;因此在多核CPU中,这几个线程可以被分配在多个不同cpu中并发执行,其中一个线程被阻塞了,其他的也能正常执行;
    2. 缺点:一个用户进程会占用多个内核级线程,线程切换由OS内核完成,由于用户态到核心态的转换需要开销,因此线程管理成本高,开销大。

多线程模型

  • 一对一模型

  • 多对一模型

  • 多对多模型

处理机调度与死锁

处理机调度

调度基本概念

  • 当有一堆任务需要处理,但由于资源有限,这些事情没法同时处理。这就需要确定某种规则来决定处理这些任务的顺序,这就是调度所研究的问题,简单来说就算:按照某种算法选择一个进程将处理机分配给他

处理机调度的层次

高级调度

  • 高级调度/长程调度/作业调度,调度对象为作业;

  • 高级调度主要用于多道批处理系统中,什么是多道批系统呢?虽然前面讲过了 再复习一遍吧:

  • 高级调度根据某种算法/一定的原则从处于后备队列的作业中挑选一个/多个作业,分配内存等资源,建立PCB,使他们获得竞争处理机的权利

  • 高级调度是外存与内存之间的调度,每个作业只调入一次,调出一次。调入时创建相应PCB,作业调出时又撤销PCB。由于调出的时机一定是作业运行结束的时候,因此高级调度主要是解决调入的问题

中级调度

  • 中级调度/内存调度,引入这个调度的目的是为了提高内存的利用率和系统的吞吐量

  • 引入虚拟存储技术后,将那些暂时不能运行的进程调至外存等待,此时进程的状态称为:就绪驻外存状态(挂起状态),等它们已具备运行条件且内存又稍有空闲时,由中级调度决定,重新调入内存并修改状态为就绪状态,挂在就绪队列上等待

    ps:PCB并不会一起被调到外存,而是常驻内存。OS通过内存中的PCB保持对各进程的监控和管理

低级调度

  • 低级调度/进程调度/短程调度:调度对象是进程(或内核级线程);
  • 主要功能是根据某种算法/方法/策略,决定就绪队列中哪个进程应该获得处理机,将处理机分配给它。
  • 这种调度方式是OS中最基本的一种调度,在多道批、实时和分时三种类型OS中必须配置这级调度;
  • 低级调度的频率很高,一般几十毫秒一次;

三种调度方式的联系、对比

进程调度

进程调度的时机

  • 这个地方有人会说:那么进程处于临界区时,不能进行处理机调度咯? 这个说法是错的 为什么呢?

    • 临界资源:一个时间段内只允许一个进程使用的资源,各进程需要互斥地访问临界资源。
    • 临界区:访问临界资源的那段代码
    • 内核程序临界区:一般是用来访问某种内核数据结构的(比如进程的就绪队列,由各就绪队列PCB租成)

进程调度的方式

非剥夺调度方式

在采用这种方式时,可能引起进程调度的因素归结为:

  1. 正在执行的进程运行完毕,或因发生某事件而使其无法再继续执行;
  2. 正在执行中的进程因提出I/O请求而暂停执行;
  3. 在进程通信/同步过程中,执行某原语操作;
剥夺调度方式

抢占并非是任意的,必须遵循一定原则。包括:

  1. **优先权原则:**指允许优先级高的新进程抢占当前进程的处理机;
  2. **短进程优先原则:**指允许新的短进程可以抢占当前长进程的处理机;
  3. **时间片原则:**即各进程按时间片轮转运行时,当正在执行的进程一个时间片用完时,便停止该进程的执行而重新进行调度;

进程调度的切换与过程

调度算法

调度算法的评价指标

CPU利用率

系统吞吐量

周转时间

  • 对于每个用户而言,都希望自己作业的周转时间短一点,然而对于OS,则向作业周转时间平均值小;那么引入了概念:带权周转时间,平均带权周转时间;

等待时间

响应时间

调度算法种类

FCFS-先来先服务算法

  • FCFS例题:

SJF-短作业优先算法

  • SJF例题:

    • 非抢占式:

      在0时刻,只有P1进来了,在他运行的时间7内,P2,P3,P4都进来了,在P1结束后选择运行时间最短的P3执行,后续同理;

    • 抢占式:

HRRN-高相应比优先算法

  • HRRN例题:

时间片轮转调度算法RR

  • RR例题

    • 时间片大小为2情况:

    最后的运行流程图:

    • 时间片大小为5情况:

补充:

  • 时间片太大的影响:如果时间片太大,使得每个进程都可以在一个时间片内完成,则时间片轮转调度算法会退化为先来先服务调度算法,并且会增大进程响应时间。因此时间片不能太大。
  • 时间片太小的影响:如果时间片太小,我们知道,进程调度、切换是有时间代价的(保存、恢复运行环境),因此这样的话会导致进程切换过于频繁,系统会花大量时间来处理进程切换,从而导致实际用于进程执行的时间比例减少。
优先级调度算法

  • 例题

    • 非抢占式的优先级调度算法:

    • 抢占式的优先级调度算法:

extra

  • 就绪队列未必是只有一个的,可以按照不同优先级来组织;另外也可以把优先级高的进程排在更靠近对头的位置
  • 根据优先级是否可以动态改变,可将优先级分为静态优先级动态优先级两种;
    • 静态优先级:创建进程时确定,之后一直不变
    • 动态优先级:创建进程时有一个初始值,之后根据情况动态地调整优先级
  • 如何合理设置各进程优先级呢?通常来说
    • 系统进程优先级高于用户进程
    • 前台进程优先级高于后台进程
    • I/0进程优先级高于计算型进程
  • 若采用动态优先级,什么时候该调整?
    • 如果某进程在就绪队列中等待了很久,适当提升优先级
    • 如果某进程占用处理机运行了很久,适当降低优先级
    • 如果发现一个进程频繁进行I/0操作,适当提升优先级
多级反馈队列调度算法

  • 例题:

死锁

死锁的概念

  • 简单来说,当一组进程发生死锁的情况下,这组死锁进程中的每个进程,都在等待另一个死锁进程所占有的资源,或者说每个进程所等待的事件是该组中其他进程释放所占有资源

死锁,饥饿,死循环

  • 死锁:各进程互相等待对方手里的资源,导致各进程都阻塞,无法向前推进的现象。
  • 饥饿:由于长期得不到想要的资源,某进程无法向前推进的现象。eg.SPF算法中的如果短进程一直进来,长进程得不到处理机就会饥饿。
  • 死循环:某进程执行过程中一直跳不出某个循环的现象。有时是因为程序bug,有时候是故意写出来的。(pv操作)

死锁产生的必要条件

  1. 互斥条件:在一段时间内,某资源只能被一个进程所占有,若其他进程请求该资源,那就只能等待,直到占有该资源的进程用完释放
  2. 不可抢占条件:进程已获得的资源未使用完之前不能被抢占只能在进程使用完时由自己释放
  3. 请求和保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源又被其他进程占有,此时请求进程被阻塞,但又对自己已有的资源保持不放
  4. 循环等待条件:在发生死锁时,必然存在一个【进程-资源】的循环链,即进程集合{P0,P1,P2…Pn}中:P0等待P1占用的资源,P1等待P2占用的资源…Pn等待P0占用的资源

  • 对不可剥夺资源的不合理分配,可能导致死锁

死锁的处理方法

预防死锁

整体思路:破坏死锁产生的四个必要条件中的一个或几个

破坏互斥条件
  • 互斥条件:只有对必须互斥使用的资源的争抢才会导致死锁
  • 破坏策略:把只能互斥使用的资源改造为允许共享使用,则系统不会进入死锁状态。
  • 缺点:并不是所有的资源都可以改造成共享使用的资源,并且为了系统安全,很多地方还必须保护这种互斥性。因此很多的时候是无法破坏互斥条件的。

破坏不可剥夺条件
  • 不可剥夺条件:进程所获得的资源在未使用完之前,不能由其他进程强行夺走,只能主动释放
  • 破坏策略
    • 当某个进程请求新的资源得不到满足的时候,必须立刻释放保持的所有资源,待以后需要时候再重新申请,即:即使某些资源尚未用完,也需要主动释放,从而破坏了不可剥夺条件。
    • 当某个进程需要的资源被其他进程所占有的时候,可以由操作系统协助,将想要的资源强行剥夺。这种方法需要考虑各进程的优先级。
  • 缺点
    • 实现起来复杂
    • 释放已获得的资源可能造成前一阶段的失效,因此这种方法一般只适用于容易保存和恢复状态的资源(CPU)
    • 反复申请和释放资源增加系统开销,降低系统吞吐量
    • 使用第一个方案,表示只要暂时得不到某资源,前面所获得资源全都得需要放弃,以后再申请。假如这种情况常发生,进程会饥饿。

破坏请求和保持条件
  • 请求和保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源又被其他进程占有,此时请求进程被阻塞,但又对自己已有的资源保持不放。
  • 破坏策略:采用静态分配方法,即进程在运行前一次申请完所需要的全部资源,在它资源尚未满足前,不让它投入运行。一旦投入运行,这些资源就一直归他所有,该进程就不会再请求别的任何资源。
  • 缺点:有些资源可能只需要用很短时间,因此如果进程的整个运行期间都一直保持着所有资源,就会造成严重的资源浪费,资源利用率极低。另外,该策略也可能导致某些进程饥饿。

正因为这种方法缺点太明显,因此衍生出了第二种方法:

它允许一个进程只获得初期所需的资源后,便开始运行。在运行过程中逐步释放已分配给自己的、且用毕的全部资源,然后再请求新的所需资源。

破坏循环等待条件
  • 循环等待条件:存在一种进程资源的循环等待链,链中每个进程已获得的资源同时被下一个进程所请求
  • 破坏策略:采用顺序资源分配法
    • 给系统中资源编号
    • 规定每个进程必须按照编号递增的顺序请求资源,同类资源(编号相同资源)一次申请完
    • 一个进程只有占有了小编号资源才有资格申请大编号资源
  • 缺点:
    • 不方便添加新设备,有可能要重新分配编号嘛
    • 进程实际使用资源顺序可能与编号递增顺序不同,会导致资源浪费
    • 必须按规定次序申请资源,用户编程麻烦(谢谢你啊 还替我考虑那么多

避免死锁

  • 用某种方法防止系统进入不安全状态,从而避免死锁(银行家算法)
安全序列

安全序列:是指如果系统按照这种序列分配资源,则每个进程都能顺利完成。只要能找出一个安全序列,系统就是安全状态。当然,安全序列可能会有多个

  • 不安全状态:如果分配了资源之后系统找不出任何一个安全序列,系统就进入了不安全状态。意味着之后可能所有进程都无法顺利执行下去了。为什么说是可能呢?因为假如存在进程提前归还了资源,那么系统也有可能重新回到安全状态
  • 安全状态、不安全状态、死锁
    • 系统处于安全状态一定不会发生死锁
    • 系统处于不安全状态有可能发生了死锁
    • 如果发生了死锁一定处于不安全状态
银行家算法
  • 核心思想:在资源分配之前预先判断这次分配是否会导致系统进入不安全状态,以此决定是否答应资源分配请求
  • 引例:

那么对于上述这个例子,OS是怎么做的呢?

  • 准备的数据结构:

    • 长度为m一维数组Available表示还有多少可用资源
    • n*m矩阵Max表示各进程对资源的最大需求数
    • n*m矩阵Allocation表示已经给各进程分配了多少资源
    • Max - Allocation = Need 矩阵表示各进程最多还需要多少资源
    • 长度为m一维数组Request表示进程此次申请的各种资源数
  • 银行家算法步骤:

    • 检查此次申请是否超过之前声明的最大需求数
    • 检查此次系统剩余的可用资源是否还能满足此次请求
    • 试探着分配,更改各数据结构
    • 用安全性算法检查此次分配是否会导致系统进入不安全状态
  • 安全性算法步骤:

    检查当前剩余的可用资源是否能够满足某个进程的最大需求,如果可以,就把该进程加入安全序列,并把该进程持有的资源全部回收

例题:

     Allocation   Max   AvailableABCD    ABCD  ABCDP1   0014    0656  1520 P2  1432    1942 P3  1354    1356P4  1000    1750
我们会看到一个资源分配表,要判断是否为安全状态,首先先找出它的Need,Need即Max(最多需要多少资源)减去Allocation(原本已经分配出去的资源),计算结果如下:NEEDABCD0642 051000020750
然后加一个全都为false的字段FINISHfalsefalsefalsefalse
接下来找出need比available小的(千万不能把它当成4位数 他是4个不同的数)NEED    AvailableABCD  ABCD0642  15200510<-00020750
P2的需求小于能用的,所以配置给他再回收NEED     AvailableABCD  ABCD0642  15200000 +14320002-------0750  2952
此时P2 FINISH的false要改成true(己完成)FINISHfalsetruefalsefalse
接下来继续往下找,发现P3的需求为0002,小于能用的2952,所以资源配置给他再回收NEED      AvailableABCD  A B C D0642  2 9 5 20000 +1 3 5 40000----------0750  3 12 10 6依此类推,做完P4→P1,当全部的FINISH都变成true时,就是安全状态。

检测和解除死锁

  • 允许死锁的发生,不过OS会负责检测出死锁的发生,然后采取某种措施解决死锁

检测死锁

  • 检测死锁的算法

简单来说就是依次消除与不阻塞进程相连的边直到无边可消;但你要是非说严谨的话就是下面几步

  1. 在资源分配图中,找出既不阻塞又不是孤点的进程Pi (不阻塞—— 所申请的资源数量必须小于等于系统中已有空闲资源数量;不是孤点——与该进程至少有一个边相连)。然后消去它的所有请求边,分配边,使之变为孤点
  2. 进程Pi释放的资源,可以唤醒某些因为等待这些资源而阻塞的进程——原来阻塞进程可能变为非阻塞进程
  3. 若剩下进程都能按照如上操作,消去图中所有的边,所有的进程结点都变成孤点,那称该图是 可完全简化 的;反之则称该图是 不可完全简化
  • 死锁定理:如果某时刻系统的资源分配图是 不可完全简化的,那么此时系统死锁
解除死锁

解除死锁的方法:

  1. 资源剥夺法:挂起某些死锁进程,并抢夺它的资源,将这些资源分配给其他的死锁进程
  2. 撤销进程法/终止进程法:强制撤销部分、甚至全部死锁进程,并剥夺这些进程的资源
  3. 进程回退法:让一个或多个死锁进程回退到足以避免死锁的地步

内存

内存基础知识

  • 什么是内存?内存是用于存放数据的硬件。程序执行前需要先放到内存中才能被CPU处理。

进程运行原理-指令

由 x = x + 1这个代码为例,它被编译后为三条指令;

第一条指令是让CPU进行数据传送,把内存单元为01001111的数据取出来取到地址为00000011的寄存器当中

第二条指令是让地址为00000011的寄存器上的数据+1

第三条指令是让CPU进行数据传送,把地址为00000011上的数据传送到地址为01001111的地方.

于是实现了 x = x + 1操作

ps:上述指令不是严谨的二进制指令,仅供参考。

单位换算

由于后续的学习,经常要进行内存之间的运算,而我们的单位往往需要统一,那么接下来就讲讲单位换算

  • 1B=8b=2^3b

  • 1KB=1024B=2^10B

  • 1MB=1024KB=210KB=220B

  • 1GB=1024MB=210MB=220KB=2^30B

  • 1TB=1024GB=210GB=220MB=230KB=240B

逻辑地址

上面的指令操作提到了逻辑地址,那么什么是逻辑地址呢?

eg. 0号同学入住的是房号为5(N=5)的房间,那么3(M=3)号同学入住的就是8(N+M=5+3=8)号房间;

写程序-运行程序

  • 编辑:程序员编辑代码
  • 编译:代码编译成若干个目标模块(把高级语言翻译为机器语言
  • 链接:由链接程序把 目标模块与所需库函数 连接在一起,形成一个完整的装入模块
  • 装入\装载:由装入程序装入模块装入内存运行

链接

静态链接
  • 装入前链接成一个完整装入模块
  • 在程序执行之前,先将目标模块以及它们所需的库函数连接成一个完整的可执行文件(装入模块),之后不再拆开

装入时动态链接
  • 运行前边装入边链接
  • 将各目标模块装入内存时,边装入边链接的链接方式

运行时动态链接
  • 运行时需要目标模块才装入并链接
  • 在程序执行中需要该目标模块时,才对它进行链接。其优点是便于修改和更新,便于实现对目标模块的共享

装入\装载

绝对装入
  • 编译时产生绝对地址
  • 地址由编译器产生,而不是OS

静态重定位
  • 装入时逻辑地址转换为物理地址/绝对地址
  • 转换过程由装入程序负责进行,装入程序是OS的一部分

动态重定位
  • 运行时将逻辑地址转换为物理地址,并设置重定位寄存器
  • 采用动态重定位方式装入的作业,其地址变换工作是在每执行一条指令时完成的
  • 执行中允许OS有条件地将其移动

内存管理概念

内存空间的分配与回收

  • OS需要负责内存空间的分配与回收

    • 记录哪些内存区域已经被分配出去了,哪些又属于空闲状态
    • 当进程运行结束之后,将进程占用的内存空间回收
    • 内存这么大,有很多位置可以放置内存,那么又该怎么分配内存空间?

连续分配管理方式

  • 连续分配:指为用户进程分配的必须是一个连续的内存空间

单一连续分配
  • 在这种分法中,内存被分为系统区和用户区
  • 系统区:用于存放OS相关数据
  • 用户区:存放用户进程相关数据
  • 内存中只能有一道用户程序,用户程序独占整个用户区空间
  • 优点:实现简单,无外部碎片;
  • 缺点:只能用于单用户、单任务的OS中,有内部碎片,存储区利用率极低

固定分区分配
  • 固定分区目的:为了能在内存中装入多道程序,且这些程序之间又不会相互打扰
  • 如何实现:将整个用户空间划分为若干个固定大小的分区,在每个分区中只装入一道作业
  • 固定分区分配有两种方法
    • 分区大小**相等:**缺乏灵活性,但适合用于用一台计算机控制多个相同对象的场合
    • 分区大小**不等:**增加灵活性,可以满足不同大小的进程需求。根据常在系统中运行的作业大小情况进行划分

那么固定分区分配又是如何实现的呢?

  • 操作系统需要建立一个数据结构 —— 分区说明表,实现各个分区的分配与回收。每个表象对应一个分区,通常按照分区大小排列。每个表包含对应分区的:大小、起始地址、状态

  • 当某用户程序要装入内存的时候,由OS内核程序根据用户程序大小检索该表,从中找到一个能满足大小、未分配的分区。将之分配给该程序,然后修改状态为“已分配”

  • 优点:实现简单,无外部碎片

  • 缺点

    • 当用户程序太大的时候,可能所有的分区都不能满足需求,此时不得不采用覆盖技术来解决,但这会降低性能
    • 会产生内部碎片,内存利用率低
动态分区分配
  • 动态分区分配又称为可变分区分配。

  • 这种分配方式不会预先划分内存分区,而是在进程装入内存的时候,根据进程的大小动态地建立分区,并使分区的大小正好适合进程的需要。

  • 系统分区的大小和数目是可变的

  • 缺点:会产生很多外部碎片,虽然可以用【紧凑】技术去处理,但紧凑的时间代价很高

下面思考三个问题:

  1. 系统要用什么样的数据结构记录内存的使用情况?

    通常使用 空闲分区表或者是空闲分区链

    空闲分区表:每个空闲分区对应一个表项

    空闲分区链:每个分区的起始部分和末尾部分分别设置前向指针和后向指针

  2. 当很多个空闲分区都能满足需求的时候,应该选择哪个分区进行分配呢?

    把一个新作业装入内存的时候,需要按照一定的动态分区分配算法,从空闲分区表(空闲分区链)中选出一个分区分配给该作业。

  3. 如何进行分区的分配与回收操作?

    分配:

    在上面的图示中,假如我们把一个4mb进程分配在了20MB处的话,我们只需要修改分区大小和起始地址即可

    假如我们分配在了4MB处的话,状态由空闲变为忙碌,则该行应该被删除,假如用空闲分区链的话,就把该节点删除

    回收

    假设一开始有个进程占据了14MB处(10+4),跑完了需要回收,回收区的后面/前面有一个相邻的空闲分区,那么就合并

    假如回收区的前、后各由一个相邻的空闲分区的话

    例:在20和10mb中间有一个4mb的进程需要进行回收

    假如回收区的前后都没有相邻的空闲分区的话

    假设有一个进程占满了14mb的地方,此进程需要被回收

  • 内部碎片,外部碎片

    • 内部碎片:位于一个操作系统分配的用于装载进程的内存区域或页面内部的空闲区域。
    • 外部碎片:位于任何两个操作系统分配的用于装载进程的内存区域或页面之间的空闲区域,可以用紧凑技术进行解决

首次适应算法
  • 算法思想:每次都从低地址开始查找,找到第一个能满足大小的空闲分区
  • 如何实现:空闲分区以地址递增的次序排列,每次分配内存的时候顺序查找空闲分区链(or空闲分区表),找到大小能满足要求的第一个空闲分区

最佳适应算法
  • 算法思想:动态分区分配是一种连续分配的管理方式,因此为各进程分配的空间必须也是连续的一整片区域。因此为了保证当大进程进来的时候有能连续的大片空间,可以尽可能多的留下大片空闲区,换句话说,就是大片的先不用,优先的把小空闲区给用了先。
  • 如何实现:空闲分区按容量递增次序链接,每次分配内存的时候顺序查找空闲分区链(or空闲分区表),找到大小能满足要求的第一个空闲分区
  • 缺点:会产生很多的外部碎片

最坏适应算法

为了解决上述出现的 产生很多外部碎片 这个问题,我们衍生出了新的与之相对的算法:最坏适应算法

  • **算法思想:**每次分配的时候优先使用最大的连续空闲区,使得分配后剩余的空闲区不会太小,方便使用;
  • 如何实现:空闲分区容量递减次序链接。每次分配内存时候按照顺序查找空闲分区链(or空闲分区表),找到大小能满足要求的第一个空闲分区。
  • 缺点:每次都选最大的分区进行分配,会导致较大的连续空闲区被迅速消耗掉,有大进程来了,就没内存空间可以用了。

临近适应算法

我们每次算法讲到最后一个都是综合类的算法,结合了前面算法的特点综合的算法

不例外,我们这次也一样:临近时应算法

  • **算法思想:**回想一下 ,首次适应算法是从链头开始找,低地址部分会出现小的空闲分区(外部碎片),而这些空闲分区部分我们往往用不上,因此会增加查找的开销。那么加入我们查找不从头开始,而是从上次查找结束的位置开始检索(因为后面是还没用到的部分,可以用的几率大点),就能减少查找开销。
  • 如何实现:空闲分区以递增递增的顺序排列(可排成一个循环链表),每次分配内存的时候从上次查找结束的位置开始查找空闲分区链(or空闲分区表),找到大小能满足要求的第一个空闲分区。

四种算法总结

非连续分配管理方式

基本分页存储管理

基本概念
  • 分页存储管理其实就是将一个进程的逻辑地址空间分成若干个大小相等的

    这些称为或者页面 从零开始编号

    相应的把内存空间分成与页面相同大小的若干个存储块 - 物理块/页框 同样 从0开始编号

如何实现地址的转换

回想我们之前所学的,进程在内存中连续存放的时候,OS是如何实现逻辑地址到物理地址的转换的呢?

是通过重定位寄存器去保存 装入模块存放的 起始位置 + 目标内存单元相对于起始位置的偏移量

我们就能得到该在物理地址(绝对地址)中,我们的目标存放的地址了,同理,我们把这种思想转移到了分页技术中地址的转换

以下面为例:

  • 页号:逻辑地址 / 页面长度 取整 (本题中:页号=80/50 = 1
  • 业内偏移量:逻辑地址 % 页面长度 (本题中:页内偏移量=80 % 50 = 30
  • 页面在内存中的起始位置:OS用某种数据结构去进行记录的 (本题中一号页在内存中存放的起始位置=450

为了方便计算页号、业内偏移量,页面大小一般取2的整数幂,那么接下来我们就来看看这种情况是什么样子的

红色部分前20位,表示了是第几页;(这的前值的是从左往右数,如果严格按照二进制来说应该是后20位

后面12位是偏移值(这的前值的是从左往右数,如果严格按照二进制来说应该是前12位

加入我们知道了N号页在内存中的起始地址(假设为X),那么我就知道我们已知的逻辑地址所对应的物理地址了

  • 结论:若每个页面大小为2^KB,用二进制数表达逻辑地址,那么末尾K位是页面偏移量。其余部分代表页号

逻辑地址结构
  • 分页存储管理的逻辑地质结构由:页内偏移量页号组成

  • 若由K位表示页内偏移量,说明系统中一个页面大小是2^K个内存单元

  • 若由M位表示页号,说明系统中,一个进程最多允许2^M个页面

页表

上述我们有个遗留问题,我们如何知道页号对应页面 在 内存中的起始地址呢?

其实是通过页表知道的,下面看看什么是页表

  1. 一个进程对应一张页表

  2. 进程的每一页对应一个页表项

  3. 每个页表项由【页号】和【块号】组成

  4. 页表记录进程页面和实际存放的内存块之间的对应关系

  5. 物理内存大小 / 页面大小 = 内存块 / 页表项(多少个 也就是能分成多少页号)

  6. 页表项 × 页表项长度 = 页表的大小(页表在内存中占用的大小)

  7. 页表的大小 / 页面大小 = 页表项个数(页框个数)

  8. 每个页表项的长度是相同的,页号是隐含的

    这句话是说明意思呢?意思是说 我们能够通过页表存放的起始地址页表项长度,就可以找到各页号所对应的页表存放的位置

是不是说的很绕呢?那么可以这么理解:

  • 页 - 一个数组

    页的大小 表现为 2^N 这N位叫做页内偏移量 = 业内地址用N位来表示

  • 页表项 - 数组中的元素 :存储的是物理地址的页框号

  • 页表项 × 页表项大小 = 页表大小 等价于

    数组个数 × 单个元素大小 = 整个数组大小

  • 物理地址 / 页表大小 = 页数 / 页框号 等价于页框号

    物理地址 / 数组大小 = 数组个数/ 最后一个数组下标 + 1

基本地址变换机构

  • 基本地址变换机构可以借助进程的页表将逻辑地址转换为物理地址
  • 通常会在系统中设置一个页表寄存器(PTR),存放页表在内存中的起始地址F页表长度M
  • 进程未执行的时候,页表的起始地址页表长度放在进程控制块PCB中,当进程被调度的时候OS内核会把他们放在页表寄存器

(页面大小为2的整数幂)

设页面大小为L,逻辑地址A到物理地址E的变换过程:

  1. 计算页号P页内偏移量W(以十进制为例:**页号P = 逻辑地址A / 页面大小L ;页内偏移量W = 逻辑地址A % 页面大小L ** 但计算机实际运行的时候,逻辑地址结构是固定不变的,因此计算机硬件可以更快得到二进制表示的页号和页内偏移量)

  2. 比较页号P页表长度M,如果P>=M,就产生越界中断,否则继续执行 (为什么等号也会算作越界中断呢?因为页号是从0开始的,而页表长度至少是1,因此P = M也会中断哦 类似于数组越界)

  3. 页表中页表P对应的页表项地址 = 页表起始地址F + 页号P * 页表项长度;取出该页表项内容b,即为内存块号

    页表长度:指的是这个页表中总共有几个页表项,就是有多少页

    页表项长度:指的是每个页表项占据了多大的存储空间

    页面大小:一个页面占多大的存储空间

  4. 计算物理地址E = 内存块号b * 页面大小L + 页内偏移量W ,用得到的物理地址E去访存

说了那么多,感觉是不是云里雾里的,知道了一堆名词,计算方式,但真正计算的时候仍然不会用?下面就来一道例题去实践一下

  • 页号P = 逻辑地址A / 页面大小L = 2500 / 1024 = 2 ; 页内偏移量W = 逻辑地址A % 页面大小L = 2500 % 1024 = 452
  • 判断越界:页号P对应内存块号为8 没越界
  • 物理地址 E = 内存块号 * 页面大小 + 页内偏移量 = 8 * 1024 + 452 = 8644 就是最后结果了
具有快表的地址变换机构

要去了解具有快表的地址变换机构前,首先要去理解一个概念 : 局部性原理

  • 局部性原理

    • 时间局部性:如果执行了程序中的某条指令,那么不久之后这条指令很有可能被再次执行;如果某个数据被 访问过,不久之后该数据很可能被再次访问。(程序中存在大量循环)
    • 空间局部性:一旦程序访问了某个存储单元,在不久之后,其附近的存储单元也很有可能被访问(许多数据在内存中是连续存放的)
    • 那么我们具有快表的地址变换机构与这局部性原理又有什么关系呢?我们上面介绍的基本地址变换机构中,每次要访问一个逻辑地址,都需要查询内存中的页表。而由于局部性原理,可能连续很多次查到的都是同一个页表项;当我们认识到了局部性原理之后,我们能否结合这个特性减少访问

在了解完局部性原理后,我们就可以正式开始学习快表

  • 快表,又称联想寄存器(TLB),是一种访问速度比内存块很多的高速缓冲寄存器(存放在更高速存储器中)

    用于存放当前访问的若干项页表项,以加速地址变换的过程

    与此对应,内存中的页表常被称为慢表(存放在内存中)

下面我们根据上方图,仔细理顺一下 引入快表之后,地址的变换过程

  1. CPU给出逻辑地址,由某硬件算出页号、页内偏移量,由地址变换机构自动地将页号P送入高速缓冲寄存器,并将此页号与高速缓冲寄存器/快表中的所有页号进行比较
  2. 若其中有与此相匹配的页号,说明要访问的页表项在快表中有副本,于是直接从快表中读出该页对应的内存块号/物理块号,再将内存块号与页内偏移量拼接成物理地址,然后访问该物理地址对应的内存单元。(一次)
  3. 若其中没有与此匹配的页号,则需要访问内存中的页表,(第一次访问内存)找到对应的页表项,得到页面存放的内存块号/物理块号,访问该物理对应的内存单元(第二次访问内存);(找到页表项后,应同时将其存入快表的一个寄存器单元中,假如联想寄存器已经满了,OS必须找到一个老的且已经被认为是不再需要的页表项,将他换出来)
  4. 发现了吗?如果快表命中了,则访问某个逻辑地址只需要一次访存。如果没命中,则需要两次访存

  • 有无快表的地址变换机构区别

两级页表

  • 我们为什么要引入新的存储管理方式呢?往往都是因为之前的多多少少存在一点问题,那么与两级页表对应的就是单级页表了,单机页表存在什么问题呢?

    首先:页表必须连续存放,因此当页表很大的时候,需要占用多个连续页框

    其次:没必要让整个页表常驻内存,因为进程在一段时间内可能只需要访问几个特定页面

  • 解决方案:将页表进行分组,使每个内存块刚好放入一个分组(比如,页面大小4KB,页表项大小4B,那么可以存1K个页表项,那么把连续的1K个页表项认定为一组),把各组离散存放到各内存块中

    然后为离散分配的页表再额外建立一张页表,我们称之为:页目录表/外层页表/顶层页表*

我们把中间这个大页表拆分成了左边这个小页表 1024个页表项为一组

为什么要这么分呢?我们知道一个分页存储管理是把一个进程分成若干个页面然后存储 对吧?那么就有可能出现页号特别特别多的情况,因此我们把这些页号又再分一次,形成了一个二级页表;

我们现在知道了 二级页表的来由了 那么二级页表又是如何实现地址转换的呢?

  1. 按照地址结构将逻辑地址拆成三个部分

    1. 一级页号
    2. 二级页号
    3. 页内偏移量
  2. 从PCB中读取页目录起始地址,根据一级页号查页目录表,找到**下一级页表(二级页表)**在内存中存放的位置
  3. 根据二级页号查表,找到最终想要访问的内存块号
  4. 结合页内偏移量得到物理地址

也就是需要3次的访存:第一次是访问内存中页目录表 第二次是访问内存中二级页表 第三次才是访问目标内存单元

有点套娃的意思;

下面来一道例题来看看一些细节问题

基本分段存储管理

分段
  • 进程的地址空间:按照程序自身的逻辑关系划分成了若干个段,每个段有一个段名

    编译程序会将段名转换为段号

    每段从 0 开始编制

  • 内存分配关泽:以段位单位进行分配,每个段在内存中占据连续空间,但各段之间可以不相邻

  • 分段细节

分段系统的逻辑地址结构段号和**段内地址(段内偏移量)**所组成

  • 段号的位数:决定了每个进程最多可以分几个段

  • 段内地址位数:决定了每个段的最大长度是多少

  • 以上图为例:段号与段内地址都是16位,因此每个进程最多有216=61K个段**,每个段的最大长度是**216=61KB

段表

上面我们学习了基本分页存储管理,有提到页表一概念,那么与之对应的,我们的基本分段存储管理是否也有段表一概念呢?答案是肯定的

  • 段表:程序分成多个段,直接离散地装入内存,为了保证程序可以正常运行,就必须能从物理内存中找到各逻辑段存放的位置,为此,需要为每个进程建立一张段映射表. 这个段映射表就是我们的段表
  • 每个在表中占有一个表项,记录了该段在内存中的起始位置(基址)和段的长度;各表项长度是相同的

地址变换

根据上方流程图我们来理一下地址变换的思路,其实是与分页存储管理的地址变换差不太多的

  1. 根据逻辑地址A得到段号S段内地址W
  2. 判断段号S是否越界,如果段号S≥段表长度M,就发生越界中断,否则继续
  3. 查询段表,找到对应的段表项,段表项的存放地址为段表起始地址F+段号S×段表项长度
  4. 检查段内地址W是否超过段号所对应的段长C,如果段内地址W≥段长C,则产生越界中断,否则继续
  5. 计算得到物理地址 = 段基址b + 段内地址w
  6. 访问目标内存单元
分段分页管理对比

  • 是信息的物理单位;分页的主要目的是为了实现离散分配,提高内存利用率;

    分页仅仅是系统管理上的需要,完全是系统行为,对于用户是不可见的

    分页的用户进程地址空间是一维的,程序员只需要给出一个记忆符即可表示一个地址;

    页的大小固定,且由系统决定。

  • 是信息的逻辑单位;分段的主要目的是更好地满足用户需求。

    一个段通常包含着一组属于一个逻辑模块的信息。

    分段对用户是可见的,用户编程的时候需要显示给出段名;

    分段的用户进程地址空间是二维的,程序员在标识一个地址时,既要给出段名,也要给出段名地址。

    段的长度不固定,决定于用户编写的程序。

  • 访问一个逻辑地址需要几次访问内存呢?

    • 分页(单级页表):第一次访问内存——查询内存中的页表,第二次访问内存——访问目标内存单元
    • 分段:第一次访问内存——查内存中段表,第二次访问内存——访问目标内存单元
信息共享

分段比分页更容易实现信息的共享和保护

首先引入一个概念叫做纯代码,纯代码/可重入代码 是 不能被修改的代码;这种类型的代码是可以共享的,而可修改的代码是不能进行共享的(比如一个代码段有很多变量,多进程并发地同时访问会导致数据地不一致)

那么如果进程是分段存储管理的 几句可以很好的进行信息地共享了,因为只需要改变进程地段表项,指向同一个段即可;

段页式存储管理

优点
  • 段页式存储,顾名思义,肯定是利用到了页式存储+段式存储,那么既然是两者结合,肯定取其精华,去其糟粕吧,那我们先复习一下段式存储和页式存储各自优缺点都有什么

概念
  • 那么我们的段页存储管理又是如何进行的呢?究竟是先分段还是先分页呢?
  • 我们的进程首先按照逻辑模块分段,再将分成的
  • 内存空间分为大小相同的内存块/页框/页帧/物理块
  • 进程将各页面分别装如个内存块中

逻辑地址结构

  • 段页式系统的逻辑地址结构由**段号、页号、页内地址(页内偏移量)**所组成
  • 段号:段号的位数决定了每个进程最多可以分几个段
  • 页号:页号的位数决定了每个段最大有多少页
  • 页内偏移量:页内偏移量决定了页面大小、内存块大小是多少
段表、页表

看下面这张图是否感觉有点眼熟?感觉与二级页表类似;

只不过把中间的那个页表替换成了一个段表,借此机会我们再来理顺一次思路:

一个进程对应一个段表 一个段表对应多个页表 换而言之,进程对应了多个页表

地址变换过程

像之前的两种存储管理的地址变换过程一样 ,我们也来理一次过程:

  1. 根据逻辑地址A得到段号S页号P页内偏移量W
  2. 判断段号S段表长度M关系,如果S≥M,发生越界中断,否则继续进行(第一次检查越界)
  3. 查询段表,进行运算:段表项 = 段表始址F + S × 段表项长度 → 找到对应的段表项(第一次访问内存)
  4. 判断页号P与页表长度的关系,如果页号P≥页表长度,发生越界中断,否则继续进行(第二次检查越界)
  5. 根据页表存放块号和页号P计算得到查询页表,找到对应的页表项(第二次访问内存)
  6. 页表项存储着该页所在的物理块号b,再利用物理块号b页内偏移量W构成最终的物理地址
  7. 最后访问目标内存单元(第三次访问内存)

内存空间的扩展

  • 操作系统需要提供某种技术从逻辑上对内存空间进行扩充
  • 通常有三种技术:
    • 覆盖技术
    • 交换技术
    • 虚拟存储技术

地址转换

  • OS需要提供地址转换功能,负责程序的逻辑地址与物理地址的转换
  • 绝对装入-编译时产生绝对地址
  • 可重定位装入-装入时将逻辑地址转换为物理地址
  • 动态运行时装入-运行时将逻辑地址转换为物理地址,需要设计重定位寄存器

内存保护

内存保护可采取两种办法

  • 方法一:在CPU种设置一对上、下限寄存器,寄存进程的上、下限地址。进程的指令要访问某个地址的时候,CPU检查是否越界

  • 方法二:采用重定位寄存器(基址寄存器)和界地址寄存器(限长寄存器)进行越界检查。其中,重定位寄存器中存放的是进程的起始物理地址界地址寄存器中存放的是进程的最大逻辑地址

覆盖与交换

覆盖技术

  • 覆盖是子啊同一个进程或程序中的

  • 思想:将程序分为多个段(多个模块),常用的段常驻内存,不常用的段在需要的时候才调入内存

    内存分为一个固定去和若干个覆盖区

    需要常驻内存的段放在固定区中,调入后就不再调出

    不常用的段放在覆盖区需要用的时候调入内存用不到的时候调出内存

  • 如下图所示,假如程序A执行需要走如下几个程序B、C…
  • B、C不能同时执行,我们就在物理内存中设置一个覆盖区 用于存放B或者C,其中其大小是B、C中较大内存决定
  • 同理D、E、F也不能同时执行,因此也设立一个覆盖区用于存储

交换技术

  • 交换是在不同进程(作业)之间的

  • 设计思想:内存空间紧张的时候,系统将内存中某些进程暂时换出外存,把外存中某些已具备运行条件的进程换入内存(进程在内存与磁盘间动态调度)

  • 这种技术其实在上面的处理机调度一节层次调度中的中级调度我们也有了解过了

  • 暂时换出外存等待的进程状态为挂起状态(挂起态)

  • 挂起态可以细分为就绪挂起和阻塞挂起两种状态 - 复习下7态模型吧

下面思考三个问题

  1. 应该在外存(磁盘)的什么位置保存被换出的进程?

    答:具有兑换功能的操作系统中,通常把磁盘空间分为文件区对换区两部分;

    文件区主要用于存放文件,主要追求存储空间的利用率,因此对文件区空间的管理采用离散分配方式

    对换区空间只占磁盘空间的小部分,被换出的进程数据就存放在对换区,由于对换的速度直接影响到系统的整体速度,因此对换区空间的管理主要追求换入换出速度,因此通常采用连续分配方式

    对换区的I/O速度比文件区的更快

  2. 什么时候应该交换?

    答:交换通常在许多进程运行且内存吃紧时进行,而系统负荷降低就暂停。例如:在发现许多进程运行时经常发生缺页,就说明内存紧张,此时可以换出一些进程;如果缺页率明显下降,就可以暂停换出。

  3. 应该换出哪些进程?

    1. 可优先换出阻塞进程
    2. 可换出优先级低的进程
    3. 为了放置优先级低的进程在被调入内存后很快又被换出,有的系统还会考虑进程在内存的驻留时间
    4. PCB会常驻内存,是不会被换出外存的

虚拟存储器

虚拟存储器概述

传统存储管理方式的特征

  • 一次性:是指作业必须一次性地全部装入内存后方能开始运行

    • 造成问题

      • 作业很大的时候,不能全部装入内存,导致大作业无法运行
      • 当大量作业要求运行的时候,由于内存无法容纳所有作业,因此只有少量作业能运行,导致多道程序并发度下降
  • 驻留性:是指作业被装入内存之后,整个作业都一直驻留在内存中,其中任何部分都不会被换出,直至作业运行结束。
    • 造成问题

      • 往往在一个时间段内,只需要访问作业的一小部分数据就可以正常运行,但偏偏会把其他不需要的部分也强行装入内存,这就导致了内存中会驻留大量、暂时用不到的数据,浪费了宝贵的内存资源。

局部性原理

而要解决传统存储管理的这些特征带来的问题,我们就引入了虚拟存储技术,而虚拟存储技术依靠【局部性原理】

关于局部性原理,在上述已经说过了,这里直接照搬上面的内容:

局部性原理

  • 时间局部性:如果执行了程序中的某条指令,那么不久之后这条指令很有可能被再次执行;如果某个数据被 访问过,不久之后该数据很可能被再次访问。(程序中存在大量循环)
  • 空间局部性:一旦程序访问了某个存储单元,在不久之后,其附近的存储单元也很有可能被访问(许多数据在内存中是连续存放的)

高速缓冲技术

高速缓冲技术其实就算应用了局部性原理

  • 高速缓冲技术的思想: 将近期会频繁访问到的数据放到更高速的存储器中,暂时用不到的数据放在更低速的存储器中;

虚拟内存的定义和特征

定义

虚拟内存/虚拟存储器,是指具有请求调入功能和置换功能,能从逻辑上(而不是物理上)对内存容量加以扩充的一种存储器系统。

那么虚拟内存是如何"增大"内存的呢?

基于局部性原理,在程序装入的时候,将程序中很快会用到的部分装入内存,暂时用不到的部分就留在外存,就可以让程序开始执行;

在程序执行的过程中,当所访问的信息不在内存时候,由OS负责将所需的信息从外存调入内存,然后继续执行程序;

假如内存空间不够,由OS负责将内存中暂时用不到的信息换出到外存;

你看 有用的在内存中,暂时用不到的在外存,是不是在用户看来是把整个程序塞入内存里了呢?

注意:

  • 虚拟内存的最大容量由计算机的地质结构(CPU的寻址范围)确定的

    比如某计算机地址结构为32位,按照字节编址,内存大小位512MB,外存大小为2GB;

    则虚拟内存的最大容量 232B= 4GB

  • 虚拟内存的实际容量 = min(内存和外村容量之和, CPU寻址范围)

    依旧是上述例子

    则虚拟内存的实际容量=min(232B, 512MB + 2GB)= 2GB +512MB;

特征

  • 多次性:是相对于传统存储管理方式的一次性而言的,是指一个作业中程序和数据无需在作业运行的时候一次性全部装入内存,而是允许被分成多次调入内存运行。多次性是虚拟存储器最重要的特征,是任何其他的存储管理方式所不具有的。因此,虚拟存储器其实是具有多次性特征的存储器管理系统。
  • 对换性:是相对于传统存储管理方式的常驻性而言的,是指一个作业中的程序和数据,无须在作业运行的时候一直常驻内存,而是允许在作业的运行过程中进行换入换出。
  • 虚拟性:虚拟性是指能够从逻辑上扩充内存容量,使得用户所看到的内存容量远远大于实际的内存容量。虚拟性是以多次性和对换性为基础的,正是由于系统允许将作业多次调入内存,并且能把内存中暂时不允许程序、数据、进程调至外存,才有可能实现了虚拟存储器;

虚拟内存的实现方法

我们知道,管理系统要实现虚拟性必须满足对换性和多次性,那么如何要满足对换性和多次性,又有什么要求呢?答案是必须建立在离散分配的内存管理方式基础上。那么现在有什么管理方式能实现虚拟存储器呢?

分页请求系统

  • 分页请求系统 = 分页系统的基础 + 请求调页功能 + 页面置换功能
  • 用户程序装入少数页面程序和数据就能运行,之后再通过上述两个功能把即将运行页面 → 内存,暂时不运行的页面 → 外存
  • 为了实现这两个功能,系统需要提供必要硬件支持和实现请求分页的软件
    • 硬件支持

      • 请求分页的页表机制
      • 缺页中断机构
      • 地址变换机构
    • 请求分页软件
      • 在硬件支持下,将程序正在运行时所需页面 → 内存,内存中不用的页面从内存 → 磁盘。

请求分段系统

  • 分页请求系统 = 分段系统的基础 + 请求调段功能 + 分段置换功能
  • 用户程序装入少数段的程序和数据就能运行,之后再通过上述两个功能把即将运行的→ 内存,暂时不运行的 → 外存
  • 为了实现这两个功能,系统需要提供必要硬件支持和实现请求分段的软件
    • 硬件支持

      • 请求分段的段表机制
      • 缺页中断机构
      • 地址变换机构
    • 软件支持
      • 在硬件支持下,将内存中暂时不用的段从内存 → 磁盘,程序运行时所需要的段 → 内存。

请求分页存储管理方式

请求分页中的硬件支持

请求页表机制

页号 物理块号 状态位P 访问字段A 修改位M 外存地址

状态位P:该页是否已调入内存 0 表示没有被调入 1表示被调入

访问字段A:本页在一段时间内被访问的次数,供页面置换算法使用

修改位M:标识该页在调入内存后是否被修改过

外存地址:该页在外存上的地址,供调入该页时参考

缺页中断机构

缺页中断机构的大致流程如下:

  • 缺页中断是因为当前指令想要访问的目标页面未调入内存而产生的,因此属于内中断

  • 一条指令在执行期间,可能产生多次缺页中断

    比如copy A to B,而A、B属于不同页面,那就有可能产生多次的中断:因为指令本身跨多个页面,A、B又是分别一个数据块;

  • 中断的分类如下

地址变换机构

由于逻辑地址到物理地址映射过程写过很多次了,这里不再赘述;

但这里需要注意的点是:

快表中有的页面一定是在内存中的,如果某个页面被换出外存,则快表中的相应表项也要删除,否则可能访问错误的页面;

找到对应的页表项之后,假如对应页面还没有调入内存,那就产生缺页中断,之后由OS的缺页中断处理程序进行处理

细节补充:

  • 只有写指令才需要修改【修改位】,而且一般来说只需要修改快表中的数据,只有要将快表项删除的时候才需要写回内存中的慢表;

    这样可以减少访存次数

  • 缺页中断处理依旧需要保持CPU现场

  • 换入/换出页面都需要启动慢速的I/O操作,可见,如果换入/换出太频繁,会有很大的开销

  • 页面调入内存中,需要修改慢表,同时也需要将表项复制到快表中

页面置换算法

请求分页管理存储与基本分页存储管理的主要区别:

程序执行的过程中,当所访问的信息不在内存的时候,由OS负责将所需要的信息从外存调入内存,然后继续执行

假如内存空间不够,就由OS负责将内存中暂时用不到的信息换出到外存

而由于我们的页面换入、换出需要磁盘I/O,有较大开销,因此好的页面置换算法的评价指标就是更少的缺页率

我们主要学习五种页面置换算法:最佳置换算法、先进先出置换算法、最近最久未使用置换算法、时钟置换算法、改进型的时钟置换算法

最佳置换算法(OPT)

  • 最佳置换算法:每次选择淘汰的页面将是以后永不使用,或者再最长时间内不再被访问的页面,这样可以保证最低的缺页率;
  • 缺页中断未必一定会发生页面置换,假如有空闲的内存块就不用进行页面置换
  • 缺页率的计算 :缺页率 = 缺页中断发生的次数 / 访问页面的总次数 × 100 %
  • 最佳置换算法是理想算法,因为只有在进程执行的过程中才能知道接下来会访问什么页面,OS无法提前预判页面访问序列,因此最佳置换算法是无法实现的。

先进先出置换算法(FIFO)

  • 先进先出置换算法(FIFO):每次选择淘汰的页面是最早进入内存的页面

  • 如何实现的呢?先进先出,学过数据结构的朋友们都清楚,应该使用队列这种数据结构。把调入内存的页面按调入的先后顺序排成一个队列,当新来的页面需要进行页面置换的时候,poll掉头页面即可;

    ps:队列的最长长度取决于系统为进程分配多少内存块

  • Belady(贝莱迪)异常: 一般来说,缓存越大,命中率越高,缺页率越低。但有一个计算机学者,名字叫Belady。在1969年研究FIFO算法时,发现了一个反例,使用4个页框时的缺页次数比3个页框时的缺页多,因此这种奇怪的情况称为Belady异常。

    这种异常的原因是对于FIFO算法来说,在同一时刻,使用4个页框时缓存中保存的页面并不完全包含使用3个页框时保存的页面,二者不是超集子集关系,造成都某些特殊的页面请求序列,4个页框命中率反而低。

    只有FIFO算法会产生这个异常,此外,FIFO实现简单,但是有可能先进入的进程也会被经常访问,因此算法性能差。

最近最久未使用置换算法(LRU)

  • 最近最久未使用置换算法(LRU):每次淘汰的页面是最近最久未使用的页面

  • 实现方法:赋予每个页面对应的页表项,用访问字段记录该页面自上次被访问以来所经历的时间t

    如果需要淘汰页面,就选择现有的页面中t值最大的,即最近最久没有使用的页面

  • 该算法需要专门的硬件支持(寄存器,栈),虽然算法性能好,但是实现也很困难,开销也很大

时钟置换算法(CLOCK)

简单的CLOCK算法

  • 时钟置换算法/最近未用算法:通过循环队列将未访问页面置换的算法
  • 简单的CLOCK实现方法:为每个页面设置一个访问位——用于标志最近是否访问过(如果该进程被访问了就从0被标志为1),然后将内存中各页面通过链接指针链接成一各循环队列;当访问新页面,需要置换页面的时候,通过检查页的访问位去置换:如果是0 → 换该页面,同时指针指向下一个页面;如果是1 → 置为0,给第二次留在内存的机会。

改进型的CLOCK算法

  • 改进型CLOCK置换算法:我们将一个页面换出的时候,假如它是修改过的,那么其修改后的数据需要重新写回外存中,如果没有被修改过,就不需要拷回外存。

    说人话就是:对于一个换出的页面,如果其被修改过,那么它换出的开销比没有被修改过的要大,那么换他出去就划不来了。

  • 那我们以什么为指标去评判一个页面该不该被置换呢?显然易见:有没有被访问过 + 有没有被修改过

    • (访问位A, 修饰位M)
    • (A = 0,M = 0)= 最近既没被访问 + 又没被修改过 ,被置换的优先级最高
    • (A = 0,M = 1)= 最近没被访问 + 已经被修改了,并不是很好的置换选项,优先级第二
    • (A = 1,M = 0)= 最近已经被访问了 + 但没被修改过 ,那么这个页面有可能还会被访问,优先级低三
    • (A = 1,M = 1)= 最近已经被访问 + 已经被修改,被置换的优先级最低(第四)
  • 那这个访问位,修饰位凭什么作为依据呢?这与我们的扫描过程有关

    • 第一轮去找(0,0),不改变标志位
    • 第一轮没找到,找第二轮,把扫描过的访问位置0,我们这轮找第一个(0,1)
    • 第二轮没找到,找第三轮,我们找(0,0),也不改变标志位
    • 第三轮没找到,我们找第四轮,找第一个(0,1)

    发现了吗,这与我们上面的优先级正好是吻合的,优先级低的相当于有多一次的免死金牌,多在内存里苟活一段时间;

页面分配策略

页面分配、置换策略

  • 驻留集:指请求分页存储管理中给进程分配的物理块的集合,在采用了虚拟存储技术的系统中,驻留集大小一般小于进程的总大小

由上面的概念我们可以发现:

  • 如果我们的驻留集太小了,会导致缺页非常的频繁,而这会导致系统要花大量时间处理缺页,开销就很大,而且用于进程推进的时间就很少了;
  • 如果我们的驻留集太大了,又会导致多道程序的并发度下降,资源的利用率就降低了

因此,不难发现,选择一个合适的驻留集大小就是非常必要的了,因此我们有了两种选择驻留集大小的分配方式

  • 固定分配:OS为每一个进程分配一组固定数目的物理块,在进程运行期间不再改变;即,驻留集大小不变
  • 可变分配:先为每个进程分配一定数目的物理块,在进程运行期间,可以根据情况做适当的增加或减少;即,驻留集大小可变

置换策略:

  • 局部置换:发生缺页的时候只能选择进程自己的物理块进行置换
  • 全局置换:可以将OS保留的空闲物理块分配给缺页进程,也可以将别的进程持有的物理块置换到外存,再分配给缺页进程

我们下面对✔的三种分配置换组合进行讲解:

  1. 固定分配局部置换:系统为每个进程分配一定数量物理块,在整个运行期间都不改变;若进程在运行过程中发生缺页,只能从该进程在内存中的页面选出一页换出,然后再调入需要的页面;

    缺点:很难在刚开始就确定应该为每个进程分配多少个物理块才合理(采用这种策略的系统可以根据进程大小、优先级、或是根据程序员给出的参数来确定为一个进程分配的内存块数)

  2. 可变分配全局置换:刚开始会给每个进程分配一定数量物理块。OS会保持一个空闲物理块队列,当某进程发生缺页的时候,从空闲物理块取出一块分配给该进程;

    若没有空闲物理块,就选择一个未锁定(系统会锁定一些页面,这些页面中的内容不能被置换出外存 比如,重要的内核数据就可以设置为**“锁定”)的页面换出外存,再将该物理块分配给缺页的进程。使用这种策略的话,只要某个进程发生了缺页,都将获得新的物理块,仅当空闲物理块用完的时候,系统才选择一个未锁定的页面调出。被选择调出的页可能是系统中任何一个进程的页,因此这个被选中的进程所拥有的物理块**会减少,缺页率也会增加。

  3. 可变分配全局置换:刚开始会给每个进程分配一定数量的物理块。当某进程发生缺页的时候,只允许从该进程自己的物理块选出一个换出外存。

    缺点:如果进程在运行中频繁地缺页,系统会给该进程多分配几个物理块,直至该进程缺页率趋势适当程度;反之,如果进程在运行中缺页率特别低,则可以适当减少分配给该进程的物理块 也就是说 这点体现出了可变分配的概念

不难发现:

  • 可变分配全局置换:只要缺页就分配新物理块
  • 可变分配局部置换:要根据发生缺页的频率来动态地增加或减少进程的物理块

何时调入页面

  1. 预调页策略:根据局部性原理(空间局部性),一次调入若干个相邻地页面可能比一次调入一个页面更高效。但如果提前调入的页面中大多数都没被访问过,则又是低效的。因此可以预测不久之后可能访问到的页面,将他们预先调入内存,但目前预调页的成功率仅仅只有50%;

    故这种策略主要用于进程的首次调入(运行前调入),由程序员指出应该先调入哪些部分;

    1. 请求调页策略:进程在运行期间发现缺页的时候才将所缺页面调入内存(运行时调入),由这种策略调入的页面一定会被访问到,但由于每次只能调入一页,每次调页都需要磁盘的I/O操作,因此I/O开销大。

何处调入页面

  1. 系统拥有足够对换区空间:页面的调入、调出都是在内存与对换区之间进行,这样可以保证页面的调入、调出的速度很快

    进程运行前,需要将进程相关的数据从文件区复制到对换区

  2. 系统缺少足够对换区空间:凡是不会被修改的数据都直接从文件区调入,由于这些页面不会被修改,因此换出的时候是不必被写回磁盘的,下次需要的时候再从文件区调入即可。

    对于可能被修改的部分,换出的时候需要写回磁盘对换区,下次需要的时候再从对换区调入。

  3. UNIX方式:运行之前进程有关的数据全部放在文件区,故未使用过的页面,都可以从文件区调入。

    若被使用过的页面需要换出,则写回对换区,下次需要的时候从对换区调入;

抖动(颠簸)现象

刚刚换出的页面马上又要换入内存,刚刚换入的页面马上又要换出内存,这种频繁的页面调度行为称为抖动,或颠簸

产生抖动主要原因是进程频繁访问的页面数量高于可用的物理块数

上面我们提到了,分配的物理块太少和太多带来的影响,因此为了研究每个进程分配多少个物理块这个问题,引入新概念工作集

  • 驻留集:指的是请求分页存储管理中给进程分配的内存块的集合;
  • 驻留集:zhi的是某段时间间隔内,进程实际访问页面的集合;

OS根据窗口尺寸来算出工作集

文件系统

策略

  • 驻留集:指请求分页存储管理中给进程分配的物理块的集合,在采用了虚拟存储技术的系统中,驻留集大小一般小于进程的总大小

由上面的概念我们可以发现:

  • 如果我们的驻留集太小了,会导致缺页非常的频繁,而这会导致系统要花大量时间处理缺页,开销就很大,而且用于进程推进的时间就很少了;
  • 如果我们的驻留集太大了,又会导致多道程序的并发度下降,资源的利用率就降低了

因此,不难发现,选择一个合适的驻留集大小就是非常必要的了,因此我们有了两种选择驻留集大小的分配方式

  • 固定分配:OS为每一个进程分配一组固定数目的物理块,在进程运行期间不再改变;即,驻留集大小不变
  • 可变分配:先为每个进程分配一定数目的物理块,在进程运行期间,可以根据情况做适当的增加或减少;即,驻留集大小可变

置换策略:

  • 局部置换:发生缺页的时候只能选择进程自己的物理块进行置换
  • 全局置换:可以将OS保留的空闲物理块分配给缺页进程,也可以将别的进程持有的物理块置换到外存,再分配给缺页进程

我们下面对✔的三种分配置换组合进行讲解:

  1. 固定分配局部置换:系统为每个进程分配一定数量物理块,在整个运行期间都不改变;若进程在运行过程中发生缺页,只能从该进程在内存中的页面选出一页换出,然后再调入需要的页面;

    缺点:很难在刚开始就确定应该为每个进程分配多少个物理块才合理(采用这种策略的系统可以根据进程大小、优先级、或是根据程序员给出的参数来确定为一个进程分配的内存块数)

  2. 可变分配全局置换:刚开始会给每个进程分配一定数量物理块。OS会保持一个空闲物理块队列,当某进程发生缺页的时候,从空闲物理块取出一块分配给该进程;

    若没有空闲物理块,就选择一个未锁定(系统会锁定一些页面,这些页面中的内容不能被置换出外存 比如,重要的内核数据就可以设置为**“锁定”)的页面换出外存,再将该物理块分配给缺页的进程。使用这种策略的话,只要某个进程发生了缺页,都将获得新的物理块,仅当空闲物理块用完的时候,系统才选择一个未锁定的页面调出。被选择调出的页可能是系统中任何一个进程的页,因此这个被选中的进程所拥有的物理块**会减少,缺页率也会增加。

  1. 可变分配全局置换:刚开始会给每个进程分配一定数量的物理块。当某进程发生缺页的时候,只允许从该进程自己的物理块选出一个换出外存。

    缺点:如果进程在运行中频繁地缺页,系统会给该进程多分配几个物理块,直至该进程缺页率趋势适当程度;反之,如果进程在运行中缺页率特别低,则可以适当减少分配给该进程的物理块 也就是说 这点体现出了可变分配的概念

不难发现:

  • 可变分配全局置换:只要缺页就分配新物理块
  • 可变分配局部置换:要根据发生缺页的频率来动态地增加或减少进程的物理块

何时调入页面

  1. 预调页策略:根据局部性原理(空间局部性),一次调入若干个相邻地页面可能比一次调入一个页面更高效。但如果提前调入的页面中大多数都没被访问过,则又是低效的。因此可以预测不久之后可能访问到的页面,将他们预先调入内存,但目前预调页的成功率仅仅只有50%;

    故这种策略主要用于进程的首次调入(运行前调入),由程序员指出应该先调入哪些部分;

    1. 请求调页策略:进程在运行期间发现缺页的时候才将所缺页面调入内存(运行时调入),由这种策略调入的页面一定会被访问到,但由于每次只能调入一页,每次调页都需要磁盘的I/O操作,因此I/O开销大。

何处调入页面

  1. 系统拥有足够对换区空间:页面的调入、调出都是在内存与对换区之间进行,这样可以保证页面的调入、调出的速度很快

    进程运行前,需要将进程相关的数据从文件区复制到对换区

  1. 系统缺少足够对换区空间:凡是不会被修改的数据都直接从文件区调入,由于这些页面不会被修改,因此换出的时候是不必被写回磁盘的,下次需要的时候再从文件区调入即可。

    对于可能被修改的部分,换出的时候需要写回磁盘对换区,下次需要的时候再从对换区调入。

  2. UNIX方式:运行之前进程有关的数据全部放在文件区,故未使用过的页面,都可以从文件区调入。

    若被使用过的页面需要换出,则写回对换区,下次需要的时候从对换区调入;

抖动(颠簸)现象

刚刚换出的页面马上又要换入内存,刚刚换入的页面马上又要换出内存,这种频繁的页面调度行为称为抖动,或颠簸

产生抖动主要原因是进程频繁访问的页面数量高于可用的物理块数

上面我们提到了,分配的物理块太少和太多带来的影响,因此为了研究每个进程分配多少个物理块这个问题,引入新概念工作集

  • 驻留集:指的是请求分页存储管理中给进程分配的内存块的集合;
  • 驻留集:zhi的是某段时间间隔内,进程实际访问页面的集合;

OS根据窗口尺寸来算出工作集

操作系统(考研,面试,期末复习)- 持续更新相关推荐

  1. 最全操作系统 考研、期末复习(上)(例题+讲解+知识点)务必收藏慢慢看

    操作系统概述与处理器管理 第一章 操作系统概述 1.操作系统的作用(目标) 2.操作系统的发展过程 第二章 进程的描述与控制 1.进程的定义和特征 2.进程和程序的比较 3.进程的三种基本状态及转换( ...

  2. 二元函数对xy同时求导_更新丨10分钟掌握高等数学上册函数极限求解问题(考研、期末复习均可以用)...

    学过高数的都知道,极限在高数的应用频率是非常高的,而且是很多高数知识的基础,求导.变限积分求极限.多重积分求极限等等均会用到 虽然是基础,但是很多人在刚学习的时候就会直接被理论弄懵圈,因此就无法继续再 ...

  3. Android面试总结(持续更新修改)

    ###Android面试总结(持续更新修改) 1.Android 的四大组件是哪些,它们的作用? ①Activity是Android程序与用户交互的窗口,是Android构造块中最基本的一种,它需要为 ...

  4. 【Python】Python学到什么程度可以面试工作?------持续更新 ...

    前言: 从事python学习,有爬虫.web后台.深度学习相关经验, 坐标北京欢迎骚扰. 本答案力求简洁和直击重点,代码部分使用Python3,更详细的解释请Google,回答有误请务必提醒答主,我将 ...

  5. 计算机考研资料库!近五十所高校计算机考研资料分享!持续更新中!

    由于文章内无法添加百度云链接,所以领取方式可以看这里 计算机考研资料库目录!(1) 计算机考研资料库目录!(2) 以下是考研资料库内已有资料,更多资料持续更新中,如果你需要哪个学校的计算机相关专业真题 ...

  6. CSAPP期末复习(更新ing)

    CSAPP期末快速复习(更新ing) 本人有关CSAPP的博客链接: 私人博客 CSDN 内容基本上差不多 主要内容 概论 信息的表示 机器级的表示 链接 I/O 概论 上下文:上下文是一个状态,包含 ...

  7. flex布局练习题,面试必备,持续更新建议收藏~

    先言:  现在网页布局大多都是flex布局,像浮动这些用得比较少,在面试中flex也是被经常问到的.而有些同学学完flex后,又不懂怎么练习巩固,所以,所以,所以,我汇聚了一些常见的flex的练习题案 ...

  8. 数学模型复习资料---自用款--大家可以借鉴来复习--持续更新

    数学模型复习资料 第一章:建立数学模型 学习建模的意义: 对于解决现实问题,根据问题内在特点,做出必要假设,合理利用数学方法和工具,得到一个恰当的数学表述. 在一般工程技术领域,数学建模仍然大有用武之 ...

  9. 前端面试计网、HTTP协议,操作系统(自留,持续更新)

    参考: https://juejin.cn/post/6844903590058786824 https://space.bilibili.com/327247876 https://blog.csd ...

  10. PHP 面试总结(持续更新) --小丑

    Session与cookie的区别? cookie数据存放在客户的浏览器上,session数据放在服务器上,以文件的形式存放,可以从php.ini里面的session.save_path找到存放位置 ...

最新文章

  1. 当前不会命中断点 源代码与原始版本不一致
  2. python采用函数式编程模式吗_Python函数与函数式编程
  3. SpringMVC_3.请求映射与静态资源处理
  4. SwiftUI优秀文章经典案例制作简易的新闻列表Demo
  5. OpenStack Weekly Rank 2015.08.24
  6. 当我们谈AI时,到底该谈什么?
  7. 针对xml文件做提取与写入的操作
  8. html5 在线留言,html5实现手机弹窗留言对话框
  9. Zabbix5系列-监控惠普服务器iLO管理口 (六)
  10. 人脸识别+AI技术如何实现智慧工地履约考勤?
  11. css图片九宫格布局
  12. 微信 获取signature签名
  13. PreScan 使用点云雷达代替激光雷达操作步骤:
  14. java加载顺序_类加载过程中几个重点执行顺序整理
  15. 笔记本 服务器 性能,笔记本CPU性能天梯图2021最新6月
  16. erp系统服务器性能指标,ERP系统体检的三大关键指标
  17. java的几个生命周期(部分简单总结)
  18. 使用Python脚本下载yandex云盘链接中的内容
  19. 如何看待2019年初多闪、马桶MT和聊天宝这三款社交APP同时发布
  20. Unix/Linux操作系统分析实验四 设备驱动: Linux系统下的字符设备驱动程序编程

热门文章

  1. 分布式应用的讲解(知乎讲解分布式较好的一篇文章)
  2. matlab批量保存图像
  3. 摹客3月重点功能更新
  4. JS实现类似网页的测试考卷
  5. 组合模式--设计模式解析与实战(关爱名 何红辉)笔记
  6. Windows10系统电脑怎样设置默认的视频播放器【图文教程】
  7. linux getenv路径目录,getenv的返回值是存放在哪里的,该怎么处理
  8. 服务状态巡检命令指引
  9. python asyncio理解_深入理解Python中的asyncio
  10. unity简单的登录注册界面