一小时通关计算机操作系统,冲冲冲!!!(含面试题解析)

一小时通过计算机操作系统

第一篇幅:操作系统引论

前言:计算机系统分软件与硬件之分,操作系统(OS) 是配置在计算机硬件上的第一层软件,是对硬件系统的
首次扩充,程序与常用软件没有操作系统的支持服务起来是非常困难的。

1.1 操作系统的目标与作用

一般来说,影响操作系统的目标与 计算机系统的规模操作系统的应用环境有关。

1.1.1 操作系统的目标

不同类型的OS,其目标各有其侧重,一般来说,在计算机硬件上配置的 OS,目标主要是:

  1. 有效性

早期的计算机系统特别的昂贵,正因如此,有效性是操作系统最重要的目标。

一般情况下操作系统的有效性包含如下两方面的含义:

(1)提高系统资源的利用率

  • 在未配置OS 的计算机系统中,CPU 与 I/O 设备等各种资源会因为它们经常处于空闲状态而得不到充分利用;内存及外存中所存放的数据太少或者无序而浪费了大量的存储空间。
  • 配置了OS 的计算机系统,可使得 CPU 和 I/O 设备能保持忙碌状态而得到有效的利用,且可使内存和外存中存放的数据因为有序节省了存储空间

在这里插入图片描述

(2) 提高系统的吞吐量

操作系统还可以通过合理地组织计算机的工作流程,而进一步改善资源的利用率,加速程序的运行,缩短程序的运行周期,从而提高系统的吞吐量。

  1. 方便性

配置 OS 后可使计算机系统更容易使用。一个未配置 OS 的计算机系统是极难使用的,因为计算机硬件只能识别 0 和 1 这样的机器代码,用户如果想使用计算机硬件运行自己编写的程序,学习成本是非常高的。

  • 用户 可通过 OS 所提供的各种命令来使用计算机系统。比如,用编译命令可方便地把用户用高级语言书写的程序翻译成机器代码,大大地方便了用户,从而使计算机变得易学易用。

在这里插入图片描述

  1. 可扩充性

计算机的新技术的迅速发展,计算机硬件和体系结构得到了飞速发展,它们对 OS 提出了更高的功能和性能要求,因此OS必须具有良好的可扩充性,方能适应其新技术的性能要求

  1. 开放性

为使来自不同厂家的计算机和设备能通过网络加以集成化,并能正确、有效地协同工作,实现应用的可移植性 和 互操作性,要求操作系统必须提供统一的开放环境。

开放性是指系统能遵循世界标准规范,特别是遵循开放系统互连(OSI)国际标准。凡遵循国际标准所开发的硬件和软件,均能彼此兼容,可方便地实现互连

1.1.2 操作系统的作用

从不同角度观察 OS 的作用:

  • 一般用户的角度: OS 是用户与计算机硬件系统之间的软件接口

在这里插入图片描述

OS 处于计算机用户与计算机系统之间,用户通过 OS 去更加简易地操纵计算机硬件和运行自己的程序 。

用户可通过以下3中方式来使用计算机: (1)命令方式 (2) 系统调用 (3) 图形,窗户方式

  • 资源管理的角度:OS 是作为计算机系统资源的管理者(非常重要的概念)

OS 的主要功能是对这四种资源(处理器,存储器,I/O设备 以及信息(数据和程序))进行有效的管理

  1. 处理机资源:用于分配和控制处理机
  2. 存储器管理:主要负责内存的分配和回收
  3. I/O 设备管理:负责I/O设备的分配与操纵
  4. 文件管理: 负责文件的存取,共享与保护
  • OS 实现了对计算机资源的抽象
    在这里插入图片描述

在裸机上覆盖的软件隐藏了对其设备资源操作的具体细节,向上提供了一组抽象的设备资源管理(利用其抽象模型提供的接口使用其设备资源,而无序了解物理接口实现的细节)。

1.3 推动操作系统发展的主要动力

1. 不断提高计算机资源的利用率
2. 方便用户
3. 器件的不断更新换代
4. 计算机体系结构的不断发展

1.2 操作系统的发展过程

单道处理系统多道批处理系统再到分时系统再到实时系统再到最终的微机操作系统,历史一直在告诉我们操作系统一直是极大地去 改善系统资源的利用率以及合理组织其工作流程(吞吐量)和方便用户的使用。(借鉴1.1.1 操作系统的目标)
加粗样式在这里插入图片描述在这里插入图片描述
在这里插入图片描述

1.3 操作系统的基本特征 (重要)

历史中的操作系统都具有并发,共享,虚拟和异步这四个特征,其中并发是操作系统中最重要的特征,并发与共享是操作系统的两个最基本的特征其他三个都是以并发特征为前提的。

1.3.1 并发性

  1. 并行与并发

并行与并发(Concurrence)是既相似又有区别的两个概念。

  • 并行性是指两个或多个事件同一时刻发生
  • 并发性指的是在一段时间间隔内有两个或多个事件发生
    在这里插入图片描述

在多道程序环境下,并发性是指在一段时间内宏观上有多个程序在同时运行,但在单处理机系统中,每一时刻却仅能有一道程序执行,故微观上这些程序只能是分时地交替执行。

若在计算机系统中有多个处理机,则这些可以并发执行的程序便可被分配到多个处理机上,实现并行执行,即利用每个处理机来处理一个可并发执行的程序,这样,多个程序便可同时执行。

  1. 引入进程 (第二篇幅我们仔细对其进行研究)

通常的程序是静态实体(Passive Entity),在多道程序系统中,它们是不能独 立运行的,更不能和其它程序并发执行。

在操作系统中引入进程的目的,就是为了使多个程序能并发执行

这里我们举一个例子:

在未引入进程的系统中,属于同一个应用程序的计算机程序和IO程序之间,两者之只能顺序进行,计算机程序执行完毕后,IO 程序才能使用当I/O 程序执行时,计算机程序也不能执行。这就会导致资源的利用率降低,系统吞吐量也低。

如果我们在系统中引入了进程,分别为计算机程序和I/O 程序各建立一个进程,则两个进程便可并发执行,这样I/O程序与计算机程序同时运行,实现并行工作从而提高了资源的利用率和吞吐量。

当用户运行程序时,系统分别为每个程序建立进程(Process),从这看出进程并不是指程序本身,而是在操作系统中能独立运行并作为资源分配的基本单位。
它是由一组机器指令,数据和堆栈等组成的,是一个能独立运行的活动实体。

  1. 引入线程 (第二篇幅我们对其仔细研究)

长期以来,进程都是操作系统中可以拥有资源并作为独立运行的基本单位。当一个进程因故不能继续运行时,操作系统便调度另一进程运行。由于进程拥有自己的资源,故使 调度付出的开销较大。直到 20 世纪 80 年代中期,人们才又提出了比进程更小的单位——线程(Threads)

通常在一个进程中可以包含若干个线程,它们可以利用进程所拥有的资源。
在引入线程的 OS 中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位。由于线程比进程更小,基本上不拥有系统资源,故对它的调度所付出的开销 就会小得多,能更高效地提高系统内多个程序间并发执行的程度。(加粗)

1.3.2 共享性

在操作系统环境下,所谓共享(Sharing),是指系统中的资源可供内存中多个并发执行 的进程(线程)共同使用,相应地,把这种资源共同使用称为资源共享,或称为资源复用。

由于各种资源的属性不同,进程堆资源复用的方式也不同,目前主要实现资源共享的方式有如下两种

  1. 互斥共享方式

资源虽可以提高给多个线程(进程)使用,但必须保证其结果是正确的,因此应保证在一段时间内只允许一个进程(线程访问该资源)。

当一个进程 A 要访 问某资源时,必须先提出请求。如果此时该资源空闲,系统便可将之分配给请求进程 A 使
用。此后若再有其它进程也要访问该资源时(只要 A 未用完),则必须等待。仅当 A 进程访 问完并释放该资源后,才允许另一进程对该资源进行访问。我们把这种资源共享方式称为 互斥式共享。

而把在一段时间内只允许一个进程访问的资源称为临界资源或独占资源。

  1. 同时访问方式

系统中还有另一类资源,允许在一段时间内由多个进程“同时”对它们进行访问。

在这里插入图片描述

1.3.3 虚拟技术

操作系统中的所谓“虚拟”(Virtual),是指通过某种技术把一个物理实体变为若干个 逻辑上的对应物
用于实现虚拟的技术称为虚拟技术。在操作系统中利用了两种方式实现虚拟技术,即时分复用技术空分复用技术

在这里插入图片描述

  1. 时分复用技术(分时使用方式,提高资源的利用)

在计算机领域中,广泛利用时分复用技术来实现虚拟处理机、虚拟设备等,以提高资源的利用率。

  • 虚拟处理器技术

在虚拟处理机技术中,利用多道程序设计技术,为每道程序建立一个进程,让多道程 序并发地执行,以此来分时使用一台处理机。
这一台实际处理器被虚拟为多台逻辑上的虚拟处理器为每个进程提供专门的服务。

  • 虚拟设备技术

我们还可以通过虚拟设备技术,将一台物理 I/O 设备虚拟为多台逻辑上的 I/O 设备,并允许每个用户占用一台虚拟 I/O 设备,
这样便可使原来仅允许在一段时间内由一个用户 访问的设备(即临界资源),变为在一段时间内允许多个用户同时访问的共享设备

  1. 空分复用技术 (提高空间的利用率)

在计算机中使用了空分复用技 术来提高存储空间的利用率

  • 虚拟磁盘技术

我们可以通过虚拟磁盘技术将一台硬盘虚拟为多台虚拟磁盘,这样使用起来既方便又安全

例如:我们将磁盘分为 1,2,3,4 四个卷,再通过安装程序将它们分别安装在 C,D,E,F四个逻辑驱动器上,这样机器便有了四个虚拟磁盘
访问D盘,即会访问到卷 2 种的内容

  • 虚拟存储器技术

虚拟存储技术在本质上就是使内存分时复用。它可以使一道程序通过时分复用方式,在远小于它的内存空间中运行。
例如:一个100MB 的应用程序可以运行在 20MB 的内存空间。

注意点:

如果虚拟的实现是通过时分复用的方法来实现的,即对某一物理设备进行分时使用,设 N 是某物理设备所对应的虚拟的逻辑设备数,则每台虚拟设备的平均速 度必然等于或低于物理设备速度的 1/N。
类似地,如果是利用空分复用方法来实现虚拟,此时一台虚拟设备平均占用的空间必然也等于或低于物理设备所拥有空间的 1/N。

1.3.4 异步性

在多道程序环境下允许多个进程并发执行,但只有进程在获得所需的资源后方能执行。 在单处理机环境下,由于系统中只有一台处理机,因而每次只允许一个进程执行,其余进程只能等待。
可见由于资源等因素的限制,使进程的执行通常都不是“一气呵成”,而是以“停停走走”的方式运行

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

这样,很可能是先进入内存的作业后完成,而后进入内存的作业先完成。或者说,进程是以人们不可预知的速度向前推进,此即进程的异步性(Asynchronism)

在这里插入图片描述

1.4 操作系统的主要功能 (重要)

操作系统的主要任务:是为多道程序的运行提供良好的运行环境,以保证多道程序能 有条不紊地、高效地运行,并能最大程度地提高系统中各种资源的利用率和方便用户的使用

操作系统应具有这样几方面的功能:处理机管理,存储器管理,设 备管理和文件管理。为了方便用户使用操作系统,还须向用户提供方便的用户接口,方便计算机联网提供了面向网络的服务功能。

在这里插入图片描述

1.4.1 处理机的管理

处理机管理的主要功能是创建和撤消进程(进程(线程)控制),对诸进程(线程)的运行进行协调(进程(线程)同步),实现进程(线程) 之间的信息交换(进程(线程)间通信),以及按照一定的算法把处理机分配给进程(线程) (调度)。

1. 进程控制

进程控制的主要功能是为 作业创建进程撤销已经结束的进程(及时回收进程所占用的资源),以及控制进程在运行过程种的状态转换,再者还应具有为一个进程创建若干个线程的功能和撤销已经完成任务的线程的功能

2. 进程同步

进程是以异步的方式进行的,为使多个进程能有条不紊地运行,系统中必须设置进程同步机制

进程同步的主要任务是为多个进程(线程)的运行进行协调。包括如下两种方式:

(1) 进程互斥方式 :进程(线程)在对临界资源进行访问时,应采用互斥方式
(2) 进程同步方式 :这是指在相互合作去完成共同任务的诸进程(线程)间,由同步机制 对它们的执行次序加以协调

为了实现进程同步,系统中必须设置进程同步机制。
最简单的用于实现进程互斥的机制是为每一个临界资源配置一把锁 W,当锁打开时,进程(线程)可以对该临界资源进行访问; 而当锁关上时,则禁止进程(线程)访问该临界资源。
实现进程同步的最常用的机制则是信号量机制

3. 进程通信

在多个进程(线程)共同合作去完成一个任务时,这些进程(线程)之间需要交换信息。

进程通信的任务就是用来实现在相互合作的进程之间的信息交换

实现:当相互合作的进程(线程)处于同一计算机系统时,通常在它们之间是采用消息缓冲队列通信方式
即由源进程利用发送命令直接将消息(Message)挂到目标进程的消息队列上,以后由目标进程利用接收命令从其消息队列中取出消息。

4. 调度

在后备队列上等待的每个作业都需经过调度才能执行,在传统的操作系统中,包括 作业调度进程调度 两步。

(1)作业调度

作业调度的基本任务是从后备队列中按照一定的算法,选择出若干个 作业,为它们分配运行所需的资源(首先是分配内存)。
在将它们调入内存后,便分别为它 们建立进程,使它们都成为可能获得处理机的就绪进程,并按照一定的算法将它们插入就 绪队列。

算法选择作业-》分配资源调入内存-》建立进程-》插入就绪队列

(2)进程调度

任务: 从进程的就绪队列中,按照一定的算法选出一个进程, 把处理机分配给它,并为它设置运行现场,使进程投入执行。

进程就绪队列根据算法选择进程-》分配其处理器-》运行执行

值得注意的是:在多线程OS中,通常是把线程作为独立运行和分配处理器的基本单位,为此把就绪线程排成一个队列,每次调度时,就从就绪队列种选出一个线程,把处理器分配给它。

进程就绪队列根据算法选择进程 -》 其线程就绪队列根据算法选出线程 -》处理器分配 -》运行执行

1.4.2 存储器管理功能

存储器管理的主要任务是 为多道程序的运行提供良好的环境, 方便用户使用存储器,提供存储器的利用率以及能从逻辑上扩充内存。

存储器具备:内存分配内存保护地址映射内存扩充功能

1. 内存分配

主要任务:为每道程序分配内存空间,提高存储器的利用率允许运行的程序申请附加的内存空间以适应程序和数据动态增长的需要。

OS实现内存分配采用静态动态两种方式。

  • 静态内存分配方式: 每个作业的内存空间是作业装入时确定的;在作业装入后的整个运行期间,不允许该作业再申请新的内存空间和移动
  • 动态内存分配方式:每个作业所需要的基本内存空间也是在装入时确定的,但允许作业运行过程种继续申请新的附加内存空间和移动,以适应程序和数据的动态增长。

实现内存分配的机制具备的结构功能

(1) 内存分配数据结构: 该结构用于记录内存空间的使用情况,作为内存分配的依据。
(2) 内存分配功能: 系统按照一定的内存分配算法为用户程序分配内存空间
(3) 内存回收功能:对用户不需要的内存通过用户的释放请求去完成系统的回收功能

2. 内存保护

任务: 确保每道用户程序都只在自己的内存空间内运行,彼此互不干扰,不允许用户程序访问操作系统的程序与数据和转移非共享的其他程序种执行

内存保护机制:设置两个界限寄存器,分别用于存放正在执行程序的上下界,越界检查由硬件实现,对越界后的处理,需要配合软件完成。

3. 地址映射

一个应用程序(源程序)经编译后,通常会形成若干个目标程序;这些目标程序再经过链 接便形成了可装入程序。
这些程序的地址都是从“0”开始的,程序中的其它地址都是相对于起始地址计算的。

  • 程序的这些地址所形成的地址范围称为 “地址空间”,程序其中的地址称为 “逻辑地址” 或 “相对地址”。
  • 内存中的一系列单元所限定的地址范围称为“内存空间”,其中的地址称为 “物理地址”。

在多道程序环境下,每道程序不可能都从“0”地址开始装入(内存),这就致使地址空间内的逻辑地址和内存空间中的物理地址不相一致。为了程序正确进行,存储器管理必须提供地址映射功能。

地址映射任务:将地址空间的逻辑地址转换为 内存空间中与之对应的物理地址。(硬件支持)

  1. 内存扩充

任务:借助于虚拟存储技术,从逻辑上扩充内存容量,以便更多的用户程序并发运行。
好处: 既满足用户需要,也改善系统的性能。

内存扩充机制所实现的功能:

(1) 请求调入功能

用户程序与数据装入一部分便可以启动程序运行,若发现程序需要未装入的内存,可向OS发送请求,由OS从磁盘中将所需部分调入内存,继续运行。

(2) 置换功能

若发现内存中无足够的空间来装入调入的程序和数据时,系统将内存中一部分暂时不用的程序和数据调至盘上,腾出内存空间,然后将所需调入的部分装入内存

1.4.3 设备管理功能

设备管理任务:完成用户进程提出的 I/O 请求,为进程分配的 I/O 设备;提高 CPU 和 I/O 设备的利用率;提高 I/O 速度;方便用户使用 I/O 设备。

应具备的功能:缓冲管理设备分配设备处理以及虚拟设备等功能。

1. 缓冲管理

CPU 运行的高速性I/O 低速性间的矛盾自计算机诞生时起便已存在了,这严重导致了CPU 的利用率

缓冲功能:则可有效地缓和 CPU 和 I/O设备速度不匹配的矛盾,提高CPU 的利用率,进而提高系统吞吐量。

2. 设备分配

任务:根据用户进程的 I/O 请求系统的现有资源情况以及按照某种设备的分配策略,为之分配其所需的设备

3. 设备处理(设备驱动程序)

任务:用于实现CPU和设备控制器之间的通信CPU的命令设备应当迅速给出其响应处理

处理过程是:设备处理程序首先检查 I/O 请求的合法性,了解设备状态是否是空闲的,了解有关的传递参数设置设备的工作方式。然后,便向设备控制器发出 I/O 命令启动 I/O设备去完成指定的 I/O 操作

设备驱动程序还应能及时响应由控制器发来的中断请求,并根 据该中断请求的类型,调用相应的中断处理程序进行处理。

1.4.4 文件管理功能

现代计算机管理中,总是把程序和数据以文件的形式存储在磁盘和磁带上,供所有的或指定的用户使用,为此,在操作系统中必须配置文件管理机构。

主要任务: 对用户文件系统文件进行管理,方便用户使用,并保证文件的安全性

具备功能: 文件存储空间管理,目录管理,文件的读/些管理, 以及文件的共享与保护

  1. 文件存储空间的管理

主要任务:为每个文件分配必要的外存空间,提高外存的利用率,并能有助于提高文件系统的存、取速度。

  1. 目录管理

主要任务是为每个文件建立其目录项,并对众多的目录 项加以有效的组织,以实现方便的按名存取.

  1. 文件的读/写管理和保护

(1)读/写: 系统先根据用户给出的文件名去检索文件目录,从中获得文件 在外存中的位置。
然后,利用文件读(写)指针,对文件进行读(写)。一旦读(写)完成,便修 改读(写)指针,为下一次读(写)做好准备。

(2)文件保护

文件系统提供有效的存取控制功能来保护文件。

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

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

为了方便用户使用操作系统,OS 又向用户提供了“用户与操作系统的接口”。该接口
通常可分为两大类:

(1) 用户接口。它是提供给用户使用的接口,用户可通过该接口取得操作系统的服务;
(2) 程序接口。它是提供给程序员在编程时使用的接口,是用户程序取得操作系统服务的唯一途径

  1. 用户接口

(1) 联机用户接口。这是为联机用户提供的,它由一组键盘操作命令及命令解释程序所组成,
当用户在终端或控制台上每键入一条命令后,系统便立即转入命令解释程序,对该命令加以解释并执行该命令。

(2) 脱机用户接口。该接口是为批处理作业的用户提供的,故也称为批处理用户接口。 该接口由一组作业控制语言(JCL)组成。
批处理作业的用户不能直接与自己的作业交互作用,只能委托系统代替用户对作业进行控制和干预。

(3)图形用户接口

。。。我们现在window 就是,点击图形即可响应接口。

  1. 程序接口

该接口是为用户程序在执行中访问系统资源而设置的,是用户程序取得操作系统服务的惟一途径。
它是由一组系统调用组成,每一个系统调用都是一个能完成特定功能的子程 序,每当应用程序要求 OS 提供某种服务(功能)时,便调用具有相应功能的系统调用。

1.5 OS 结构设计

软件工程的目标是十分明确的,所开发出的软件产品应具有良好的软件质量和合理的 费用,整个费用应能为用户所接受。
软件质量可用这样几个指标来评价:功能性,有效性,可靠性,易使用性,可维护性和易移植性。
为此,先后产生了多种操作系统的开发方法,如模块化方法、结构化方法和面向对象的方法等。利用不同的开发方法所开发出的操作系统将具有不同的操作系统结构。

1.5.1 传统的操作系统结构

软件开发技术的不断发展,促进了 OS 结构的更新换代。这里,我们把早期的无结构OS(第一代)、模块化结构的 OS(第二代)和分层式结构的 OS(第三代),都统称为传统结构的OS,而把微内核结构的 OS 称为现代结构的 OS。

  1. 无结构操作系统

OS是为数众多的一组过程的集合,每个过程可以任意地相互调用其他过程,使操作系统内部复杂混乱,这种OS是无结构的。

2.模块化结构OS

  • 模块化程序设计技术的基本概念

该技术基于 “分解” 和 “模块化” 原则来控制大型软件的复杂度,将OS按其功能划分为若干大小且 独立的模块,模块具有某方面的功能,
“进程管理模块,存储器管理模块,I/O设备管理模块”,通过其中接口各模块之间可以实现交互,当模块较大时,在进行划分未子模块,
这种设计方法构成了模块化结构的操作系统。

在这里插入图片描述

  • 模块的独立性

衡量模块的独立性有两个指标:

(1) 内聚性,指模块内部各部分间联系的紧密程度,内聚性越高,模块独立性越强

(2) 耦合度:指模块间相互联系和相互影响的程度,显然,耦合度越低,模块的独立性越好。

  • 模块接口法的优缺点

优点:

  • 提高 OS 设计的正确性,可理解性和可维护性
  • 增强 OS 的适应性
  • 加速 OS 的开发过程

存在的问题:

  • 在OS设计时,对各模块之间的接口规定很难满足在模块完成后对接口的实际需求。
  • 各模块的设计无法寻找到一个可靠的决定顺序,无序性造成其设计决定的困难。
  1. 分层式结构OS

为了将模块―接口法中“决定顺序”的无序性变为有序性,引入了有序分层法。

优点: 易保证系统正确性与扩充和易维护性。
缺点:系统效率降低,相邻间要建立其层次间通信机制,一个功能要自上而下地去传递多个层次,增加了系统的通信开销。

1.5.2 客户/服务器模式(C/S)

  1. C/S模式的组成

主要由客户机,服务器和网络系统三个部分组成

  1. C/S之间的交互

在客户机和服务器之间需要进行交互,必须利用消息机制在这两者之间进行多次通信。一次完整的交互过程可分成以下四步:

(1) 客户发送信息

客户机上用户请求服务器进行应用处理,要输入其相应命令和有关参数,并被发送进程装配成请求消息发送服务器,
客户机上的接受进程则等待接收从服务器发回来的响应消息。

(2)服务器接收消息

服务器的接收进程平时处于等待状态,一旦客户机发来请求,接收进程被激活,根据请求信息的内容将其提供给服务器相应软件进行处理。

(3) 服务器回送消息

服务器端软件根据请求进行处理,完成指定处理后,把处理结果装配成一个响应消息,由服务器中发送进程发往客户机。

(4) 客户机接收消息

客户机中的接收进程将收到的响应消息转交给客户机软件,再由后者做出适当处理后提交给发送该请求的客户。

  1. C/S模式的优点

C/S模式之所以能称为当前分布式系统和网络环境下软件的主要工作模式,是由于该模式具有传统集中模式中无法比拟的一系列优点。

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

基本客户/服务器模式的不足之处是存在着不可靠性和瓶颈问题。
在系统仅有一个服务器时,一旦服务器故障,将导致整个网络瘫痪。当服务器在重负荷下工作时,会因忙不过 来而显著地延长对用户请求的响应时间。
如果在网络中配置多个服务器,并采取相应的安 全措施,则可加以改善。

1.5.3 面向对象的程序设计

  1. 面向对象技术的基本概念

原理: 基于“抽象” 和 “隐蔽” 原则来控制大型软件的复杂度的。

对象:指的是现实世界具有相同属性,服从相同规则的一系列事物的抽象,而把具体实例称为 对象的实例。

  • 对象

在面向对象的技术中,是利用被封装的数据结构(变量)和一组对它进行操作的过程(方 法),来表示系统中的某个对象的,

  • 对象类

类是在对象上的抽象,对象则是类的实例。
对象类的概念非常有用,因为它极大地提高了创建多个相似对象的效率

在这里插入图片描述

  • 继承

继承是父类和子类之间共享变量和方法的机制,该机制规定,子类自动继承父类中定义的变量和方法,并允许子类再增加新的内容。

在这里插入图片描述

  1. 面向对象技术的优点

(1) 通过“重用”提高产品质量和生产率。

(2) 使系统具有更好的易修改性****和易扩展性.

(3) 更易于保证系统的“正确性”和“可靠性

1.5.4 微内核OS结构

1. 微内核的操作系统的基本概念

  • 足够小的内核

在微内核操作系统中,内核是指精心设计的、能实现现代 OS 最基本的核心功能的部分。
微内核并非是一个完整的 OS,而只是操作系统中最基本的部分。

@1. 实现与硬件紧密相关的处理
@2. 实现一些较基本的功能
@3. 负责客户和服务器之间的通信,

  • 基于 C/S模式

操作系统最基本的部分在内核中,把操作系统的绝大部分功能放在微内核外面的一组服务器(进程)实现,

例如: 提供对进程(线程)管理的进程(线程)服务器。

在这里插入图片描述

  • 应用 “机制与策略分离” 原理

机制:是指实现某一功能的具体执行机构
策略:在机制的基础上,借助于某些参数和算法来实现该功能的优化,或达到不同的功能目标。

在微内核操作系统中将机制放在 OS 的微内核中,策略放于微内核之外的管理服务器等等去实现。

  • 采用面向对象技术

操作系统是一个极其复杂的大型软件系统,我们不仅可以通过结构设计来分解操作系统的复杂度,还可以基于面向对象技术中的“抽象”和“隐蔽”原则控制系统的复杂性,
再进一步利用“对象”、“封装”和“继承”等概念来确保操作系统的“正确性”、“可靠性”、 “易修改性”、“易扩展性”等,并提高操作系统的设计速度。

2. 微内核的基本功能

  • 进程(线程)管理

由于进程(线程)之间的通信功能是微内核 OS 最基本的功能,被频繁使用,因此几乎所有的微内核 OS 都是将进程(线程)之间的通信功能放入微内核中。
此外,还将进程的切换、线 程的调度,以及多处理机之间的同步等功能也放入微内核中。

  • 低级存储器管理

通常在微内核中,只配置最基本的低级存储器管理机制。
例如: 用于实现将用户空间的逻 辑地址变换为内存空间的物理地址的页表机制和地址变换机制。

  • 中断和陷入处理

3. 微内核操作系统的优点

提高了系统可扩展,可靠性,可移植性,对分布式系统的支持,融入面向对象技术

4. 微内核系统存在的问题

C/S之间的 上下文切换为微内核带来了效率问题

在这里插入图片描述

第二篇幅:进程管理

2.1 进程的基本概念

在未配备OS的系统中,程序的执行是顺序执行的,即一个程序执行完才能允许另一个程序执行,这没有充分利于CPU

由于程序并发执行的特征导致了操作系统引入了进程的概念。

2.1.1 程序的顺序执行及其特征

1. 程序的顺序执行

通常可以把一个应用程序分成若干个程序段,在各程序段之间,必须按照某种先后次
序顺序执行
,仅当前一操作(程序段)执行完后,才能执行后继操作。

在这里插入图片描述

2. 程序顺序执行的特征

(1) 顺序性处理机的操作严格按照程序所规定的顺序执行,即每一个操作必须在上一个操作结束后开始。

(2) 封闭性:程序是在封闭的环境下执行的,即程序运行独占全机资源。

(3) 可再现性: 只要程序执行时的环境和初始条件相同,程序重复执行时,都会获得相同的结果

2.1.2 前趋图

前趋图是一个有向无循环图,DAG(Directed Acyclic Graph),用于描述进程之间执行的前后关系
注意:前趋图必须不存在循环

Pi->Pj :结点间的有向边则用于表示两个结点之间存在的偏序(Partical Order,前趋关系)

在这里插入图片描述

2.1.3 程序的并发执行及其特征

1. 程序的并发执行

例如:在输入程序,计算机程序,打印程序三者间,存在 Ii -> Ci -> Pi 的前趋关系,以至于对于一个作业的输入,计算,打印三个
操作需要顺序执行,但并不存在 Pi -> Ii+1 的关系。

但对一批程序进行处理时,可以使得其 并发执行。例如:输入程序在输入第一个程序后,在计算机程序计算时,输入程序可以输入第二个
程序,这使得第一个程序与第二个程序能并发执行。

s1: a = x+2
s2: b = y+4
s3: c = a+b
s4: d = c+b

s1
s3 - s4
s2 /

可以看出:s1 和 s2 是可以并发执行的,但是 s3只能等待 s1,s2执行完毕才能执行

2. 程序并发执行时的特征

程序的并发执行,虽提高了系统吞吐量,但也会产生下述一些与程序顺序执行时不同的特征。

- 间断性

程序在并发执行时,由于它们共享系统资源,以及为完成同一项任务而相互合作,致使这些并发执行的程序之间,形成了相互制约的关系。

相互制约导致并发程序具有 “执行-暂停-执行”间断性的活动规律

- 失去封闭性

程序在并发执行时是多个程序共享系统中的各种资源,因而这些资源将由多个程序来改变(同一个程序执行,必然受到其他程序的影响),导致程序运行失去了封闭性。

- 不可再现性

程序在并发执行时,由于失去了数据资源的封闭性,其计算结果与并发程序的执行速度有关。

2.1.4 进程的特征与状态

1. 进程的特征与定义

并发执行的程序失去了密闭性且具有间断与不可再现的特征,这决定了通常的程序是不能参与并发执行的,因为程序执行的结果是不可再现的。
这样的程序运行失去了意义,为了使得程序能并发执行且对并发执行的程序加以描述和控制,人们引入了 “进程” 的概念。

进程包括如下特征:

  • 结构特征

上面提到通常的程序是不能并发执行的,为了使得程序(数据)能独立运行,应为之配置一个进程控制块PCB(Process COntrol Block)

由程序段,相关的数据和PCB 构成了进程的实体(通常情况下所说的进程)。

所谓创建进程,实质上是创建进程实体的PCB,而撤销进程,实际上撤销进程的PCB

  • 动态性

进程的实质是进程实体的一次执行过程,动态性是进程最基本的特征

  • 并发性

多个进程实体同存在内存中,且在一段时间内同时运行,引入进程的目的也正是为了使得进程实体能和其他进程实体并发执行
而程序(没有建立PCB)是不能并发执行的。

  • 独立性

进程实体是一个能独立运行,独立分配资源且独立接收调度的基本单位。

  • 异步性

指进程按各自独立的,不可预知的速度向前推进,或者说进程实体按照异步方式运行。

进程的定义:进程是进程实体(由程序执行时系统分配的PCB与程序本身的数据和程序段构成)的运行过程,是系统进行资源分配和调度的一个独立单位。

2. 进程的三个基本状态 (重点)

进程的执行时的间断性决定了进程可能具有多种状态,我们列出以下三种基本状态:

  • 就绪(Ready)状态

当进程已经分配除CPU之外的所有必要资源后,会进行排队等待CPU 的分配,这时的状态叫就绪状态

  • 执行状态

进程已经获得 CPU,其程序正在执行。

  • 阻塞状态

正在执行的进程由于发生某事件而暂停无法继续执行时,便放弃处理机而处于暂停状态,即进程的执行受到阻塞,把这种状态叫阻塞状态。
处于阻塞状态的进程也会排成一个队列进行等待。

导致进程阻塞的典型事件有:请求I/O,申请缓冲空间。

总结:

在这里插入图片描述

处于就绪状态的进程,在调度程序为之分配了处理机之后就从就绪状态转换为执行状态,如果因分配给当前进程的时间片已经完毕而被
暂停执行时,该进程便由执行状态又回复道就绪状态;如果发生某事情而使进程的执行受阻(进程访问其临界资源,该资源被其他进程访问)
使之无法运行,该进程就会从执行状态转换为阻塞状态。

3. 挂起状态 (不做重点)

  • 引起挂起状态的原因

(1) 终端用户的请求,当终端用户在自己的程序运行期间发现有可疑问题时,希望暂停使自己的程序静止下来,使自己程序的状态调整到静止状态,这样的状态我们称为挂起状态。

(2) 父进程请求
(3)负荷调节的需要

实时系统的工作负荷已经影响到其对实时任务的控制时,由系统把一些不重要的进程挂起,保证系统正常运行

(4) 操作系统的需要

  • 进程状态的切换

在引入挂起状态后,增加了从挂起状态到非挂起状态的切换。

(1) 就绪 -> 静止就绪。 处于静止就绪的进程 不再被调度执行
(2) 阻塞 -> 静止阻塞。
(3) 静止就绪 -> 就绪
(4) 静止阻塞-> 阻塞

4. 创建状态和终止状态

为了管理的需要,实际的系统还存在着两种常见的进程状态,创建状态与终止状态

  • 创建状态

进程的创建需要两个步骤:为新进程创建 PCB,并填写必要的管理信息;其次把该进程转入就绪状态并插入就绪队列之中

概念:当系统为进程分配了 PCB,但进程自身还未进入主存,即创建工作尚未完成还不能被调度运行,其所处的状态就是创建状态。

作用:为了保证进程的调度必须在创建工作完成后之后进行,确保对进程控制块操作的完整性。

  • 终止状态

进程的终止需要两个步骤:首先等待操作系统进行善后处理,然后将其PCB清零,并将PCB空间返回系统

进程自然结束或是出现错误,或被操作系统终结都将进入终止状态,进入终止状态的进程不能再次执行,但在操作系统会保存其记录(状态码和计时统计数据,供其他进程收集),一旦其他进程完成了对终止状态进程的信息提取后,操作系统将删除该进程。

进程的5种状态转换图:

在这里插入图片描述

增加了创建和终止且具有挂起状态的进程状态及转换图:

在这里插入图片描述

在引入创建和终止以及具有挂起状态的进程状态转换,需增加以下情况:

(1) NULL- >创建 :一个新进程产生时,该进程处于创建状态

(2) 创建-> 活动就绪:在当前系统的性能和内存的容量均允许的情况下,完成对进程的创建,其进程的状态转换为就绪状态

(3) 创建 -> 静止就绪:考虑到系统当前资源状况和性能要求,并不分配给新建进程所需资源,主要是主存资源,响应的系统进程将
进程状态转换为静止就绪状态,对换到外存,不再参与调度,进程创建尚未完成。

2.1.5 进程控制块(重点)

进程控制块是由系统为进程定义的数据结构,用于描述和控制进程的运行

进程控制块是进程实体的一部分,是操作系统种最重要的记录型数据结构。

PCB 中记录了操作系统所需的,用于描述进程的当前情况以及控制进程运行的全部信息。

1. 进程控制块的作用

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

OS是通过从进程的PCB来对并发执行的进程控制和管理的,PCB 是进程存在的唯一标志

PCB的存储位置:PCB经常被系统访问,因此PCB应该常驻内存,系统将所有的PCB组织成若干各链表(队列)存放在操作系统中专门开辟的PCB区内。

2. 进程控制块中的信息

在这里插入图片描述

进程控制块包括以下四方面的信息。

  • 进程标识符

进程标识符用于唯一地标识一个进程,一个进程通常有两种标识符。

(1)内部标识符: 操作系统提高给进程唯一一个数字标识符(序号),为了方便系统使用。
(2)外部标识符:由创建创建者提供,字母数字组成,由用户在访问该进程使用。

  • 处理机状态

处理机状态信息主要是由处理机的各种寄存器中的内容组成的。

处理器在运行时的信息都放在寄存器中,当其中断时,这些信息保存到PCB中,以便进程重新执行时,从断点继续执行。

寄存器包括:

(1) 通用寄存器,用户程序可访问,用于暂存信息
(2) 指令计数器:其中存放了要访问的下一条指令的地址
(3) 程序状态字 PSW: 包含状态信息,(条件码,执行方式,中断屏蔽标志)
(4) 用户栈指针:指每个用户进程都有一个或若干个与之相关的系统栈,用于存放过程和系统调用参数及调用地址,栈指针指向栈顶

  • 进程调度信息

PCB还存放了与进程调度和切换有关的信息

@1. 进程状态 :指明进程的当前状态,作为进程调度和对换的依据
@2. 进程的优先级 :用于描述进程使用处理机的优先级别的整数,优先级高的进程优先获得处理机
@3. 进程调度所需的其他信息 : 与所采用的进程调度算法有关(进程等待CPU的时间和,进程执行的时间总和)
@4. 事件:指进程由执行状态转变为阻塞状态所等待发生的事件。 阻塞原因。

  • 进程控制信息 (重要)

@1. 程序和数据的地址,指进程的程序和数据所在的内存或外存地(首)址,以便再调度到该进程执行时,能从 PCB 中找到其程序和数据;
@2. 进程同步和通信 机制,指实现进程同步和进程通信时必需的机制,如消息队列指针、信号量等,它们可能 全部或部分地放在 PCB 中
@3. 资源清单:列出了除CPU以外的,进程所需的全部资源及已经分配到该进程的资源的清单
@4. 链接指针:它给出了进程(PCB)所在队列中的下一个进程的PCB 的首地址。

3. 进程控制块的组织方式

在一个系统中,PCB数量成百数千,为了对它们进行有效的管理,应该用适当的方式对其加以管理并组织,目前组织方式有两种:

  • 链接方式

把具有统一状态的PCB,用其链接字链接成一个队列,这样就可以形成就绪队列,若干个阻塞队列和空白队列等。

在这里插入图片描述

  • 索引方式

系统根据所有进程的状态建立几张索引表。例如,就绪索引表,阻塞索引表等,并把各索引表在内存的首地址记录在内存的一些专用单元中。

在这里插入图片描述

2.2 进程控制

进程控制是进程管理中最基本的功能。它用于创建一个新进程,终止一个已完成的进程,或终止一个因出现某事件而使其无法运行下去的进程,
还可负责进程运行中的状态转 换

进程通信与控制是由 OS 内核中原语来实现的

原语:由若干指令组成,用于完成一定功能的一个过程,其过程是 “原子操作:要么全做,要么全不做”。

2.2.1 进程的创建 (重要)

1. 进程图(Process Graph)

进程图是用于描述一个进程的家族关系的有向 树,书的节点代表进程。

在这里插入图片描述

了解进程间的关系是非常重要的。因为子进程会继承父进程所拥有的资源,如继承父进程所分配到的缓冲区,子进程被撤销,应将从父进程那获得的资源归还给父进程,在PCB中设置了家族关系表项,标明自己的父进程及所有的字进程。

2. 引起创建进程的事件

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

3. 进程的创建 (重要)

OS发现了要求创建新进程的事件会**调用进程创建原语Creat()**按下列步骤创建一个新进程。

(1) 申请空白PCB:为新进程申请获得唯一的数字标识符,并从PCB集合中索取一个空白PCB

(2) 为新进程分配资源:为新进程的程序和数据以及用户栈分配必要的内存空间

(3) 初始化进程控制块:PCB的初始化包括

@1. 初始化标识信息,将系统分配的标识符和父进程标识符填入新 PCB 中;
@2. 初始化处理机状态信息,使程序计数器指向程序的入口地址,使栈指针指向栈顶;
@3. 初始化处理机控制信息,将进程的状态设置为就绪状态或 静止就绪状态,对于优先级,通常是将它设置为最低优先级,除非用户以显式方式提出高优先级要求

(4) 将新进程插入就绪队列

2.2.2 进程的终止 (重要)

1. 引起进程终止的事件

  • 正常结束: 在计算机系统中,都有一个用于标识进程已经运行完成的指示。
  • 异常结束

(1)越界错误:程序所访问的存储区已经越出该进程的区域
(2)保护错误:指进程试图去访问一个不允许访问的资源或文件
(3)非法指令: 程序试图去执行一条不存在的指令。
(4)特权指令错误:这时用户进程试图执行一条只允许 OS 执行的指令
(5)运行超时:这是指进程的执行时间已经超过了指定的最大值
(6)等待超时:进程等待某事件的时间超过了规定的最大值
(7) 算术运算错误:进程试图去执行一个被禁止的运算,被 0 除
(8) I/O 故障:这是指I/O过程中发生了错误。

  • 外界干预

进程应外界的请求而终止运行。

(1) 操作员或者操作系统干预,例如:发生了死锁,由操作员或操作系统终止该进程
(2) 父进程请求:父进程有终止自己的任何子孙的权利,当父进程提出请求,系统会终止该进程
(3) 父进程终止:当父进程终止时,OS也将它的所有子孙进程终止。

2. 进程的终止过程 (重要)

OS 调用原语来终止进程

(1) 根据被终止进程的标识符,从 PCB 集合中检索出该进程的 PCB,从中读出该进程的状态
(2) 若被终止进程正处于执行状态,应立即终止该进程的执行,并置调度标志为真,用于指示该进程被终止后应重新进行调度。
(3) 若该进程还有子孙进程,还应将其所有子孙进程予以终止,以防它们成为不可控的进程。
(4) 将被终止进程所拥有的全部资源,或者归还给其父进程,或者归还给系统
(5) 将被终止进程(PCB)从所在队列(或链表)中移出,等待其他程序来搜集信息。

2.2.3 进程的阻塞与唤醒 (重要)

1. 引起进程阻塞和唤醒的事件

唤醒:

  • 请求系统服务
  • 启动某种操作

阻塞:

  • 新数据尚未到达
  • 无新工作可做

2. 进程阻塞过程 (重要)

正在执行的过程,发生上述事件后,进程通过调用阻塞原语Block把自身阻塞。阻塞是进程自身的主动行为

(1)停止执行进程,将PCB现行状态改为 阻塞,并将PCB插入阻塞队列,本进程插入到具有相同事件的阻塞队列
(2)转调度程序进行重新调度将处理机分配给另一就绪进程并进行切换。
(3)保留被阻塞进程的处理机状态(PCB中),按新进程的PCB中的处理机设置CPU 的环境

3. 进程唤醒状态

当被阻塞进程期待的事件出现时,则由有关进程调用唤醒原语 wakeup()

执行过程:

被阻塞的进程从等待该事件的阻塞队列中移出,将其PCB中的现行状态由阻塞改为就绪,然后将该PCB插入到就绪队列中

2.2.4 进程的挂起与激活

1. 进程的挂起

用户进程请求将自己挂起,或父进程请求将 自己的某个子进程挂起,系统将利用挂起原语 suspend( )将指定进程或处于阻塞状态的进程挂起。

挂起原语的执行过程是:首先检查被挂起进程的状态,若处于活动就绪状态,便将其改为静止就绪;
对于活动阻塞状态的进程,则将之改为静止阻塞。为了方便用户或父进程考查该进程的运行情况而把该进程的 PCB 复制到某指定的内存区域。
最后,若被挂起的进程正在执行,则转向调度程序重新调度。

2. 进程的激活过程

当发生激活进程的事件时,例如,父进程或用户进程请求激活指定进程,若该进程驻 留在外存而内存中已有足够的空间时,则可将在外存上处于静止就绪状态的该进程换入内 存。这时,系统将利用激活原语 active( )将指定进程激活。激活原语先将进程从外存调入内 存,检查该进程的现行状态,若是静止就绪,便将之改为活动就绪;若为静止阻塞,便将
之改为活动阻塞。假如采用的是抢占调度策略,则每当有新进程进入就绪队列时,应检查 是否要进行重新调度,即由调度程序将被激活进程与当前进程进行优先级的比较,如果被
激活进程的优先级更低,就不必重新调度;否则,立即剥夺当前进程的运行,把处理机分 配给刚被激活的进程。

2.3 进程同步 (非常重要)

在 OS 中引入进程后,虽然提高了资源的利用率和系统的吞吐量,但由于进程的异步性, 也会给系统造成混乱,尤其是在他们争用临界资源时。

主要任务:对多个相关进程在执行次序上进行协调,以使并发执行的诸程序之间能有效地共享资源和相互合作,从而使程序执行具有再现性

2.3.1 进程同步的基本概念

1. 两种形式的制约关系

在多道程序环境下,当程序并发执行时,由于资源共享和进程合作,使同处于一个系统中的诸进程之间存在着两种形式的制约关系。

  • 间接相互制约关系: 同处于一个系统中的进程,通常共享着某种系统资源,如共享CPU,共享I/O设备。对于可能出现的共享资源的竞争导致了间接相互制约关系

  • 直接相互制约关系: 制约源自进程间的合作,多个进程间有直接的关系.

例如:有一输入进程A通过单缓冲向进程B提供数据。缓冲为空时,计算进程因不能获得所需数据而阻塞,而当进程A把数据输入缓冲区后,B进程被唤醒,反之,缓冲区满时,A不能写入而阻塞,等B把缓冲数据取走便可唤醒。

2. 临界资源

对于临界资源(同一时间仅一个进程访问)诸进程采用互斥方式,实现对其资源的共享

生产者-消费者进程同步问题:

producer: repeat

produce an item in nextp;
while counter = n do no-op;
 buffer[in] = nextp;
 in = in +1 mod n;
 ++counter;
until false;

consume: repeat
 while counter = 0 do no-op;
 nextc = buffer[out];
 out = (out + 1) mod n;
 --counter;
 consumer the item in nextc;
until false;

我们不难看出这上面的生产者程序和消费者程序是非常正确的,在顺序执行的程序结果也是非常正确的,但在并发
执行时就会出现差错,问题在于这两个进程共享变量 counter

一般情况下改变变量:将变量的值写到寄存器,在寄存器中改变其值,返回改变后的值

在并发执行中,counter = 5 分别在两个进程中修改,会导致最终的值不可估测,也许是 6 或 4

3. 临界区

人们把在每个进程中访问临界资源的那段代码称为临界区(critical section)。

互斥访问原理:

每个进程进入临界区之前,对欲访问的临界资源进行检查,看它是否正在被访问。未被访问则进程可进入临界区对资源进行访问,并设置它正被访问的标志,如果此刻临界资源被其他进程访问,则本进程不能进入临界区。

repeat
    【entry section】
     critical section;
     【exit section】
     remainder section
until false;

4. 同步机制应遵循的规则

为实现进程互斥进入临界区,更多的方法是在系统中设置专门的同步机制来协调各进程间的运行,其同步机制应遵循以下四条准则:

(1)空闲让进 :无进程处于临界区(空闲),应允许一个请求进入临界区的进程立即进入,有效地利用临界资源。
(2)忙则等待 : 有进程在临界区(在访问),其他试图进入的进程必须等待,以保证对临界资源的互斥访问。
(3)有限等待 : 对要求访问临界资源的进程,保证在有限时间内进入自己的临界区,以免陷入 “死等“ 状态。
(4)让权等待 : 当进程不能进入自己的临界区,应立即释放处理机,以免线程陷入”忙等“状态。

2.3.2 信号量机制

信号量(Semaphores)机制是一种卓有成效的进程同步工具。

1. 整型信号量

整型信号量:用于表示资源数目的整型量 S,仅通过两个标准的原子操作(Atomic Operator)wait(S)和signal(S)来访问

wait(S): while S<=0 do no-op;
             S = S-1;
signal(S):  S = S+1;

wait(S) 和 signal(S) 是两个原子操作,在执行时不可中断,当一个进程修改某信号量时,没有其他进程可同时对该信号量进行修改

2. 记录型信号量

整型信号量机制中的 wait 操作,在信号量S<=0 时,就会不断地测试,是未遵循 “让权等待”的准则,而是使进程处于 “忙等” 的状态。

记录型信号量机制除了表示资源数目的整型变量外,还增加了一个进程链表指针L,用于链接上述的所有等待进程

type semaphore = record
         value : interger
         L : list of process;
         end
       
wait(S) 和 signal(S) 操作可描述为:

procedure wait(S)
   var S: semaphore;
   begin
      S.value = S.value -1;
      if S.value<0 then block(S.L);
    end
    
procedure signal(S)
   var S: semaphore;
   begin
     S.value = S.value + 1;
     if S.value <= 0  then wakeup(S.L);
   end
 
 

wait 操作: 意味着进程请求一个单位的该类资源,使系统中可供分配的该类资源数减少一个,因此描述为 S.value:=S.value-1;当 S.value<0 时,表示该类资源已分配完毕,因此进程应调用 block 原语,进行自我阻塞,放弃处理机,并插入到信号量链表 S.L 中。

signal操作:释放一个单位资源,使系统中可供分配的该类资源数增加一个,故 S.value:=S.value+1 操作表示资源数目加 1。若
加 1 后仍是 S.value≤0,则表示在该信号量链表中,仍有等待该资源的进程被阻塞,故还应 调用 wakeup 原语,将 S.L 链表中的第一个等待进程唤醒。

3. AND型信号量

解决一个进程如果需要先获得两个或者多个共享资源后方能执行其任务的问题

例如:

现有进程A和B,它们都想访问共享数据 D,E,当然,共享数据都作为临界资源。这时,这两个数据都设置用于互斥的信号量
Dmutex 和 Emutex,并令其初值都是 1,相应地,这两个进程都要包哦含对 Dmutex 和 Emutex 的操作。

process A: wait(Dmutex), wait(Emutex)
process B: wait(Emutex), wait(Dmutex)

若进程 A 和 B 按照下述次序交替执行 wait 操作。

process A: wait(Dmutex); 于是 Dmutex=0
process B: wait(Emutex); 于是 Emutex=0
process A: wait(Emutex); 于是 Emutex=-1 A 阻塞
process B: wait(Dmutex); 于是 Dmutex=-1 B 阻塞

这时进程 A和 B都处于僵持状态,这时的进程 A和B都进入死锁状态,进程要求的共享资源越多,发生进程死锁的可能性越大。

AND同步机制对若干个临界资源的分配,采取原子操作方式:要么把它所请求的资源全部分配到进程,要么一个也不分配,这样可以避免死锁情况的发生。

AND同步机制在wait操作中,增加了 “AND” 条件,成为同时wait操作,即Swait(Simultaneous wait)定义如下:

Swait(S1, S2, S3, S4.....Sn)
    if Si>=1 and ... and  Sn >=1 then
      for i =1  to n do
        Si = Si -1;
       endfor
    else
     将进程转向 阻塞状态,等指定的(Si分配)继续重新调度
     
 Ssignal(S1, S2, S3, ....Sn)
 for i = 1 to n do
     Si = Si + 1;
 将所有的进程阻塞队列全部移动到进程就绪队列中 
 endfor;

4. 信号量集

需求与问题:

在记录型信号量机制中,wait(S)或 signal(S)操作仅能对信号量施以加 1 或减 1 操作,意 味着每次只能获得或释放一个单位的临界资源。而当一次需要 N 个某类临界资源时,便要进行 N 次 wait(S)操作,显然这是低效的

此外,在有些情况下,当资源数量低于某一下限值时,便不予以分配。因而,在每次分配之前,都必须测试该资源的数量,看其是否大于 其下限值。

基于上述两点,可以对 AND 信号量机制加以扩充,形成一般化的“信号量集” 机制。Swait 操作可描述如下,其中 S 为信号量,d 为需求值,而 t 为下限值

 
 Swait(S1,t1,d1,…,Sn,tn,dn) 
  if Si>=t1 andand Sn>=tn then 
  for i:=1 to n do 
  Si:=Si-di;
  endfor 
  else 
  Place the executing process in the waiting queue of the first Si with Si<ti and set its program counter 
 to the beginning of the Swait Operation. 
  endif 
  
 Ssignal(S1,d1,…,Sn,dn) 
  for i:=1 to n do 
  Si:=Si+di;
  Remove all the process waiting in the queue associated with Si
  into the ready queue 
  endfor;
  

下面我们讨论一般“信号量集”的几种特殊情况:
(1) Swait(S,d,d)。此时在信号量集中只有一个信号量 S,但允许它每次申请 d 个资 源,当现有资源数少于 d 时,不予分配。
(2) Swait(S,1,1)。此时的信号量集已蜕化为一般的记录型信号量(S>1 时)或互斥信号 量(S=1 时)。
(3) Swait(S,1,0)。这是一种很特殊且很有用的信号量操作。当 S≥1 时,允许多个进程进入某特定区;当 S 变为 0 后,将阻止任何进程进入特定区。 换言之,它相当于一个可控开关。

2.3.3 信号量的应用

1. 利用信号量实现进程互斥

var mutex: semaphore = 1;
成对出现 wait(mutex) 和 signal(mutex)在临界区上下界,可实现进程互斥

2.3.4 管程机制

信号量机制会使得每个访问临界资源的进程必须自备同步操作 wait(S) 和 signal(S)。这就使得大量的同步操作分散在各个进程中,不仅给系统的管理带来了麻烦,也可能导致死锁,为了避免上述问题,产生了 新的进程同步工具—管城(Monitors)

1. 管程的定义

一个管城定义了一个数据结构和能为并发进程所执行(在该数据结构上)的一组操作,这组操作能同步进程和改变管程中的数据。

管城由 4部分组成:

@1: 管程的名称
@2: 局部于管程内部的共享数据结构说明
@3: 对该数据结构进行操作的一组过程
@4: 对局部于管程内部的共享数据设置初始值的语句

管程相当于围墙,它把共享变量和对它进行操作的若干过程围了起来, 所有进程要访问临界资源时,都必须经过管程(相当于通过围墙的门)才能进入,而管程每次 只准许一个进程进入管程,从而实现了进程互斥。

在这里插入图片描述

管程的特征:

(1)模块化:是基本程序单位,可以单独编译
(2)抽象数据类型:管程中不仅有数据,还有对数据的操作
(3)信息掩蔽: 数据结构封装

管程于线程不同:

(1)数据结构不同,进程定义的是私有数据结构PCB,管程定义的是公共数据结构(消息队列)
(2)数据结构上的操作不同:进程是由书匈奴程序执行有关的操作,而管程主要是进行同步和初始化操作。
(3)设置进程的目的是在于实现系统的并发性,管程的设置则是解决共享资源的互斥使用问题。
(4)进程通过调用管程中的过程对共享数据结构实行操作,管城为被动工作方式,进程为主动
(5)进程能并发执行,管程不能与调用者并发
(6)进程具有多态性,“创建诞生”,”撤销“ 消亡,管程则是OS中一个资源管理模块,供进程调用。

2. 条件变量(condition_variable)

一种情况:当一个进程调用了管程,在管程中时被阻塞或挂起,直到阻塞或挂起的原因解除,而在此期间,如果该进程不释放管程,则其它进程无法进入管程,被迫长时间地等待。

为了解决这个问题,引入了条件变量condition

条件变量是抽象数据类型,每个条件变量保存了一个链表,用于记录因该条件变量而阻塞的所有进程,同时提供了两个操作
x.wait 和 x.signal 含义是:

@1: x.wait :正在调用管城的进程因 x 条件需要被阻塞或挂起,调用 x.wait 将自己插入到 x 条件的等待队列上,并释放管程,直到
x条件变化,此时其他进程可以使用该管程。

@2. x.signal: 正在调用管城的进程因为 x 条件发生了变化,则调用 x.signal,重新启动一个因 x 条件而阻塞或挂起的进程
如果存在多个这样的进程,则选择其中的一个,如果没有,继续执行原进程,而不产生任何结果,与信号量机制的 signal 操作不同

2.4 经典进程的同步问题

进程同步问题十分重要,也很有趣,因此学者们的深入研究产生了一系列经典的进程同步问题。这些可以帮我们更加深入理解进程同步的概念
及实现方法。

2.4.1 生产者 - 消费者问题

前面我们提到生产者-消费者问题未考虑进程的互斥和同步问题,造成了数据的不定性。

生产者-消费者问题是相互合作的进程关系的一种抽象,例如:输入时,进程是生产者,计算进程是消费者,在输出时,计算进程是生产者而打印进程是消费者。该问题具有很大的代表性和实用价值。

1. 利用记录型信号量-解决生产者消费者问题

Var mutex,empty,full:semaphore  = 1,n,0;
   buffer: array[0, 。。。。n-1] of item
   in, out: integer = 0,0
   begin
     parbegin
        proceducer: begin
                     repeat
                       proudcer an item nextp;
                       wait(empty); //等待其消费者消费完毕
                       wait(mutex); // 等待进入临界区访问临界资源
                       buffer[in]  = nextp
                       in = (in +1 ) mod n; //生产下一个
                       signal(mutex)//释放临界资源,供消费者使用
                       signal(full);  // 提醒消费者当前食物是饱和,可继续消费
                       unitil false;
                      end
        consumer: begin
                  repeat 
                    wait(full);         //等待消费者生产完毕
                    wait(mutex);        // 等待进入临界区
                    nextc = buffer(out);   
                    out = (out+1) mod n;   //消费事物
                    signal(mutex);           //释放临界资源,供生产者生产就绪
                    signal(empty);           //提醒消费者当前食物可继续生产
                    consumer the item in nextc; 
                   until false
                  end
                   
          

注意点:信号量应成对出现

2. 利用 AND 信号量解决生产者-消费者问题

将多信号量一起wait,一起signal

Var mutex,empty,full:semaphore  = 1,n,0;
   buffer: array[0, 。。。。n-1] of item
   in, out: integer = 0,0
   begin
     parbegin
        proceducer: begin
                     repeat
                       proudcer an item nextp;
                       Swait(empty, mutex); //等待其消费者消费完毕,等待进入临界区访问临界资源
                       buffer[in]  = nextp
                       in = (in +1 ) mod n; //生产下一个
                       signal(mutex,full)//释放临界资源,供消费者使用,提醒消费者当前食物是饱和,可继续消费
                       unitil false;
                      end
        consumer: begin
                  repeat 
                    wait(full, mutex);         //等待消费者生产完毕,等待进入临界区
                    nextc = buffer(out);   
                    out = (out+1) mod n;   //消费事物
                    signal(mutex ,empty);           //释放临界资源,供生产者生产就绪,提醒消费者当前食物可继续生产
                    consumer the item in nextc; 
                   until false
                  end

3. 利用管程来解决生产者-消费者问题

  • 建立管程,命名ProclucerConsumer
  • put(item)过程。生产者利用该过程将自己生产的产品放到缓冲区中,并用整型变量count来表示在缓冲区中已经有的产品数目,
    当count >= n ,表示缓冲池已经满,生产者必须等待。
  • get(item) 过程,消费者利用该过程从缓冲区中取走一个产品,当count<=0,表示缓冲池中已经没有可取走的产品,
    消费者应该等待。

定义管程:

type producer-consumer = monitor
   Var in,out,count: integer
    buffer: array[0,.....n-1] of item;
    nofull, noempty:condition;
    procedure entry put(item)
        begin
          if count>=n  then  notfull.wait; //等待缓冲区不满
             buffer(in)  = nextp;
             in  = (in + 1) mod n;
             count = count +1;
             if notempty.queue then notempty.signal; //使等待count 数量不为0的进程队列唤醒。
         end
    procedure entry get(item)
       begin
         if count <=0 then notempty.wait; //等待缓冲池不为空
         nextc = buffer(out);
         out = (out+1) mod n;
         count = count -1;
         if notfull.queue then notfull.signal;//使等待 缓冲区不满的进程唤醒
       end

在利用管程解决生产者-消费者问题,其描述为:

producer: begin
              repeat
                produce an item in nexp;
                PC.put(item);
               until false;
           end
consumer: begin
               repeat
                 PC.get(item);
                 consume the item in nextc;
                until false;
          end

2.4.2 哲学家进餐问题

Dijkstra 提出并解决的哲学家进餐问题是典型的同步问题(进程资源共享):五个哲学家共用一张圆桌,分别坐在周围五张桌子上
桌子上有五个碗与五只筷子,它们的生活方式是交替地进行思考与进餐,平时,一位哲学家进行思考,饥饿时便试图取用其左右最靠近他的筷子
只有他拿到两个筷子才能进餐,进餐完毕,放下筷子进行思考。

1. 利用记录型信号量解决哲学家进餐问题

筷子是临界资源,在一段时间内只允许一位哲学家使用。利用 5 个信号量来表示筷子实现其互斥使用。

Var chopstick: array[0,....4] of semaphore;
对所有的信号量都初始化为1, 第i位哲学家的活动可描述为:

repeat
   wait(chopstick[i]);
   wait(chopstick[(i+1) mod 5]);
   
   eat;
   
   signal(chopstick[(i+1) mod 5])
   signal(chopstick[i+1]);
   
   think;
 until false;

这种解法在多个哲学家一起进餐的时候,很大几率造成死锁,(当五位哲学家同时饥饿各拿起左边的筷子时,五个信号量都是0,当他们都拿左边的筷子时,就会使得五个信号量 chopstick 为0 ).

这种情况下:利用 AND信号量机制就能解决哲学家进餐问题

2. 利用 AND信号量机制就能解决哲学家进餐问题

AND 信号量机制使得 获得两个临界资源是原子操作,要么共同获得,要么一个也不获得。

Var chopstick array of semaphore = (1,1,1,1,1);

processi
   repeat
      think;
      Swait(chopstick[(i+1) mod 5], chopstick[i]);
      eat;
      Ssignat(chopstick[(i+1)mod 5], chopstick[i]);
    until false;

2.4.3 读者-写者问题

一个数据文件或记录,可被多个进程共享,我们把只要求读该文件的进程称为”Reader进程“,其他进程称为 ”Writer进程“。

  • 允许多个进程同时读取一个共享对象,读操作不会使得数据文件混乱
  • 不允许(一个writer进程与 reader进程)或 Writer进程同时访问共享对象,因为这种访问会引起混乱。

所谓 ”读者-写者问题(Reader-Writer Problem)“ 是指保证一个Writer进程必须与其他进程互斥第访问共享对象的同步问题

1. 利用记录型信号量解决读者-写者问题

  • 实现Reader 与 Writer进程间在读或写时的互斥设置一个互斥信号量 Wmutex。
  • ReadCount表示正在读的进程数目,仅当 ReadCount = 0 ,Reader进程才能需要执行 Wait(Wmutex)操作,
    当Wait(Wmutex)操作成功,Reader进程可区读,做 ReadCount+1操作是(其他读进程无须等待Wmutex信号量,因为不会有进程执行写入操作,可以同步读取)。
  • 仅当Reader进程在执行 ReadCount 减 1 操作其值为0时,才能执行 signal(Wmutex) 操作,以便 Writer进程写。
  • 因为ReadCount 是一个可被多Reader进程访问的临界资源,因此应该为其设置 互斥信号量rmutex

读者-写者问题描述:

Var rmutex,wmutex: semaphore  = 1,1;
    ReadCount: integer = 0;
    begin
    parbegin
      Reader: begin
            repeat
                wait(rmutex);
                if(ReadCount == 0) then wait(wmutex);
                  ReadCount = ReadCount +1;
                signal(rmutex);
                perform read operation;
                wait(rmutex);
                readcount = readcount -1;
                if readcount = 0  then signal(wmutex);
                signal(rmutex);
             until false; 
              end
       
      writer:begin
         repeat 
            wait(wmutex);
            perform write operation;
            signal(wmutex);
          until false;
         end
     parend
  end

2. 利用信号量集机制解决读者-写者问题

在记录型信号量加入了 最多只允许RN个读者同时来读的限制。

  • 引入了信号量L,赋予其初始值 RN,执行wait(L,1,1)操作来限制读者的数目,当有一个读者进入就要执行 wait(L,1,1)操作,使得
    L 的值减1 ,当RN+1 个读者进入,就会因为wait(L,1,1)操作失败而堵塞:
var RN integer;
 L ,mx:semaphore  = RN,1;
begin
 parbegin
   reader: begin
       repeat
         Swait(L,1,1);
         Swait(wmutex,1,0);  //当前不能有写进程执行,否则将阻塞
         perform read operation;
         Ssignal(L,1);
        until false;
      end
  
    writer:begin
          repeat:
           Swait(wmutex, 1, 1; L,RN,0); //当前无写进程且无读进程,否则阻塞
           perform write operation;
           Ssignal(mx,1);
          until false
         end
        parend
      end

2.5 进程通信 (非常重要)

概念:进程间的信息交换,其所交换的信息量少者是一个状态或数值,多者则是成千上万各个字节

低级通信与高级通信:

进程之间的互斥与同步由于其交换的信息量少而被归为低级通信。

上节提到的信号量机制作为同步工具是卓有成效的,但作为通信工具,不够理想:

  • 效率低,生产者每次只能向缓冲池投放一个信息。。,消费者每次只能从缓冲区宏获取一个消息。
  • 通信对用户不透明

下面我们介绍一个高级进程通信,指用户可以利用操作系统所提供的一组通信命令高效传送大量数据的一种通信方式。
且OS隐藏了进程通信的实现细节,通信过程对用户是透明的,这样大大减少了通信程序编制上的复杂性。

2.5.1 进程通信的类型

高级通信机制可归结为三大类:共享存储器系统、消息传递系统以及管道通信系统。

1. 共享存储器系统

(Shared_Memory System)中,相互通信的进程共享某些数据结构或共享存储区,进程间能通过这些空间通信

(1) 基于共享数据结构的通信方式(低级通信)

要求诸进程公用某些数据结构, 借以实现诸进程间的信息交换,如生产者-消费者问题中的有界缓冲区。

公用数据结构的设置及对进程间同步的处理,都是程序员的职责,操作系统仅提供共享存储器,这种通信方式是低效的。

(2) 基于共享存储区的通信方式 (高级通信)

在存储器划出了一块共享存储区,诸多进程通过对共享存储区中数据的读或写来实现通信

具体实现: 进程在通信前,向系统申请获得共享存储区的一个分区,并指定该分区的关键字,若系统
已给其他进程分配了这样的分区,则将该分区的描述符返回给申请者,继之,由申请者把获得的共享存储分区连接到本进程上,之后便可以
读、写普通存储器一样地读取该公共存储分区。

2. 消息传递系统(最广泛的一种进程间的通信机制,高级通信)

在该进程间的数据交换是以格式化的消息(message)为单位的。

使用:利用OS 提供的一组通信命令(原语),可以实现其大量数据的传递,且隐藏通信的实现细节

3. 管道通信(高级通信)

管道:连接一个读进程与一个写进程以实现它们之间通信的一个共享文件(pipe文件)。
向管道(共享文件)提供输入的发送进程(即写进程),以字符流形式将大量的数据送入管道;而接受管道输出的接收进程(即读进程),则从管道中接收(读)数据,发送进程于接收进程都是利用管道进行通信的,故称为 管道通信

为了协调接收于发送进程,管道机制提供了三方面的协调能力:

  • 互斥: 当一个进程对pipe 进行读/写操作,其他进程必须等待。

  • 同步(相互合作): 指当输入进程把一定数量(4kb)的数据写入pipe,便进入阻塞状态,等读进程拿走数据后,再次唤醒。
    输出进程相同。

  • 确定对方是否存在:确定对方存在后,才能通信。

2.5.2 消息传递通信的实现方式

在进程通信时,源进程可以直接或间接地将目标传送给目标进程,由此将进程通信分为 直接通信 于 间接通信两种

1. 直接通信方式

这是指发送进程利用 OS 所提供的发送命令,直接把消息发送给目标进程。此时,要求 发送进程和接收进程都以显式方式提供对方的标识符。
通常,系统提供下述两条通信命令

(原语):
Send(Receiver,message); 发送一个消息给接收进程;
Receive(Sender,message); 接收 Sender 发来的消息;

注意:接收进程可与多个发送进程通信,因此接收进程不能事先指定发送线程:Receive(id, message);

生产者消费者的直接通信方式:

repeat
   produce an item in nextp;
   send(consumer, nextp);
 until false;
 
 repeat:
    receive(producer, nextc);
    consum the item in nextc;
 until false;
 

2. 间接通信方式

间接通信方式:指进程之间的通信需要通过作为共享数据结构的实体(信箱)。

信箱暂存发送进程发送给目标进程的消息接收进程则从中取出对方发送给自己的消息。可实现(实时通信与非实时通信)

系统为信箱提供了若干条原语,分别用于信箱的创建,撤销和消息的发送,接收。

(1)信箱的创建和撤销。

进程使用创建原语来建立信箱,创建者进程给出(信箱名字,信箱属性(公有,私用或共享); 共享信箱应该给出共享着的名字),
当进程不需要信箱时,使用信箱撤销原语将其撤销。

(2) 消息的发送和接收

进程间通信需要使用共享信箱,利用下面的原语通信:

  • Send(mailbox, message) 将一个消息发送到指定信箱。

  • Recieve(mailbox, message): 从指定信箱中接收一个消息。

信箱分为三类:

(1) 私用信箱:(用户创建,进程的一部分)

信箱的拥有者有权从信 箱中读取消息,其他用户则只能将自己构成的消息发送到该信箱中。
这种私用信箱可采用 单向通信链路的信箱来实现。当拥有该信箱的进程结束时,信箱也随之消失。

(2)公用信箱 (OS创建,提供给系统中的所有核准进程使用)

核准进程既可把消息发送给该信箱中,也可以从信箱读取发送自己的消息。
这种信箱采用双向通信链路的信箱实现,在系统运行期间始终存在。

(3) 共享信箱(进程创建,并指明其是可共享的,指出其共享进程)

信箱的拥有者和共享者都有权力从信箱中取走发送给自己的消息。

在利用信箱通信时,发送进程与接收进程之间存在以下四种关系:

(1) 一对一的关系:发送进程和接收进程建立一条两者专用的通信链路,使两者之间的交互不受其他进程的干扰。
(2) 多对一关系:允许提供服务的进程与多个用户进程之间进行交互,也称为客户/服 务器交互(client/server interaction)。
(3) 一对多关系:允许一个发送进程与多个接收进程进行交互,使发送进程可用广播方 式向接收者(多个)发送消息。
(4) 多对多关系:允许建立一个公用信箱,让多个进程都能向信箱中投递消息;也可从 信箱中取走属于自己的消息。

2.5.3 消息传递系统实现中的若干问题

高级进程通信广泛采用消息传递系统。故本小节将对这种通信中的几个主要问题做扼要的阐述。

1. 通信链路

为使在发送进程和接收进程之间能进行通信,必须在两者之间建立一条通信链路。

通信链路的连接方法,可把通信链路分为两类:

(1) 点—点连接通信链路,这时的一条链路只连接两个结点(进程);
(2) 多点连接链路,指用一条链路连接多个(n>2)结点(进程)。

根据通信方式的不同,则又可把链路分成两种:

(1) 单向通信链路,只允许发送进程向接收进程发送消息,或者相反;
(2) 双向链路,既允许由进程 A 向进程 B 发送消息,也允许进程 B 同时向进程 A 发送消息。

通信链路容量的不同而把链路分为两类:

(1) 一是无容量通信链路,没有缓冲区,不能暂存消息
(2) 二是有容量链路,设置了缓冲区,能暂存消息,缓冲区数目愈多,通信链路的容量愈大。

2. 消息的格式

  • 计算机网络中源和目标进程所处环境不同,信息的传输举例远,导致所用的消息格式比较复杂,通常是消息(消息头,消息正文)
  • OS:采用比较短的定长消息格式(减少对消息的处理和存储开销),和变长消息格式(在发送较长消息使用)

3. 进程同步方式

进程通信,需要有进程同步机制保证其进程间能协调通信。

发送进程或者接收进程完成消息的发送和接受都存在两种可能性:进程继续发送(接受)或阻塞。

(1) 发送进程阻塞,接受进程阻塞

用于进程之间紧密同步(tight synchronization),发送进程和接收进程之间无缓冲时。这两个进程平时都处于阻塞状态,直 到有消息传递时。这种同步方式称为汇合(rendezrous)。

(2)发送进程不阻塞,接受进程阻塞 (广泛的进程同步方式)

发送进程不阻塞,因而它可以尽快地把一个或多个消息发送给多个目标; 而接收进程平时则处 于阻塞状态,直到发送进程发来消息时才被唤醒。

(3)发送进程和接收进程均不阻塞。(较为常见的进程同步形式)

发送进程和接收进程都在忙于自己的事情,仅当发生某事件使它无法继续运行时,才把自己阻塞起来等待

2.5.4 消息缓冲队列通信机制(广泛应用于本地进程之间的通信)

在这种通信机制中,发送进程利用 Send 原语将消息直 接发送给接收进程;接收进程则利用 Receive 原语接收消息

1.消息缓冲队列通信机制中的数据结构

- 消息缓冲区

消息缓冲队列通信方式中,主要利用的数据结构是消息缓冲区。

type message buffer = record
                      sender :发送者进程标识符
                      size : 消息长度
                      text :消息正文
                      next : 下一个消息缓冲区的指针
                     end

- PCB 中有关通信的数据项

OS采用了消息缓冲队列通信机制时,除了需要为进程设置消息缓冲队列外,还应在进程PCB增加消息队列队首指针,用于对消息
队列进行操作,以及用于实现同步的互信号量 mutex和资源信号量sm

 type processcontrol block = record
                              mq:消息队列首指针
                              mutex:消息队列互斥信号量
                              sm :消息队列资源信号量
                              end

2. 发送原语

  • 发送进程在利用发送原语发送消息前,应在自己的内存空间设置一发送区a,将待发送的消息正文,发送进程标识符,消息长度
    填入其中,之后调用发送原语,将消息发送给目标进程
  • 发送原语首先根据发送区 a中所设置的消息长度 a.size,申请一缓冲区 i,之后把a复制到 i 中
  • 为了能够将i挂在接收进程的消息队列 mq 上,应现获取到接收进程PCB的内部标识符 j,然后将 i 挂在j.mq上,该队列属于临界资源在执行insert 的前后,都要执行 wait 和 signal 操作。

发送原语描述:

produre send(receiver, a )
  begin
    getbuf(a.size, i);    //根据 a.size 申请缓冲区
    i.sender = a.sender   //复制 a 的信息到消息缓冲区 i中
    i.size = a.size
    i.text = a.text
    i.next = 0;
    getid(PCB set,receiver.j);  //获取接收进程内部标识符
    wait(j.mutex)       
    insert(j.mq, i);   //将消息缓冲区插入消息队列中
    signal(j.mutex)
    signal(j.sm);
   end
   

加粗样式

3. 接收原语

接收进程调用接收原语 receive(b),从自己的消息缓冲队列 mq 中摘下第一个消息缓冲区 i,并将其中的数据复制到以 b 为首址的指定消息接收区内。

接收原语描述如下:

procedure receive(b)
  begin
     j = internal name;  //j 为接收进程内部的标识符。
     wait(j.sm);
     wait(j.mutex);
     remove(j.mq, i);    //将消息队列中第一个消息移出。
     signal(j.mutex)
     b.sender = i.sender; // 将消息缓冲区i中信息复制到接受区b
     b.size = i.size
     b.text = i.text
   end
   

2.6 线程 (非常重要)

线程的发展历史:

20世纪60年代人们提出了进程的概念,在OS中一直是以进程作为拥有资源和独立运行的基本单位的。
直到20世纪80年代中期,人们提出了比进程更小的能独立运行的基本单位–线程(Threads),试图使用其来提高系统内程序并发执行
的速度,从而进一步提高系统的吞吐量。到了20世纪90年代,多处理机系统得到发展,线程比进程更好地提高程序的并行执行速度,充分发挥
多处理机的优越性,因而在近代OS也都引进了线程改善OS的性能。

2.6.1 线程的概念

1. 线程的引入

在操作系统中引入进程的目的,是为了使多个程序能并发执行,以提高资源利用率和系统吞吐量。
在操作系统中再引入线程的目的,则是为了减少程序在并发执行时所付出的时空开销,使得OS具有更好的并发性

进程的两个基本属性:可用于资源的独立单位,可独立调度和分配的基本单位,这两点构成了进程并发执行的基础。

系统为了使得程序能并发执行,还应该进行以下的一系列操作:

(1) 创建进程 :创建一个进程,为其分配所必需的,处理机外的所有资源,内存空间,I/O设备和建立对应的PCB
(2) 撤销进程 :系统在撤销进程时,对其所占资源进行回收,然后撤销PCB
(3) 进程切换 : 保留当前进程 CPU 环境和设置新选中进程的CPU环境,要花费不少的处理时间。

进程是一个资源的拥有者,因而在创建,撤销,切换,系统为之付出较大的时空开销。因此系统中设置的进程,数目不宜多,
进程切换频率也不宜过高,这就限制了并发程度的提高。

即能让多个程序更好的并发执行又能尽量减少系统的开销,已经称为近年来设计操作系统时所追求的重要目标。这时,不少研究OS的学者们想到若将进程上述两个属性分开,由操作系统分开处理,(轻装上阵:对可调度和分配的基本单位,不作为拥有资源的单位) 和 (拥有资源的基本单位,又不对之进行频繁的切换),正是这种思想的指导下,形成了线程的概念(作为调度和分配的基本单位)。

2. 进程与线程的比较

线程有传统进程所具有的特征,所以又称为轻型进程,传统进程为重型进程(只有一个线程的任务)。

引入了线程的OS,一个进程有多个线程,也至少有一个线程。

我们将从调度性,并发性,系统开销和拥有资源方面对进程与线程做比较

(1) 调度

传统OS,进程是作为拥有资源的基本单位和独立调度,分派的基本单位
引入线程OS,线程是作为调度和分派的基本单位,进程作为资源拥有的基本单位。
这样线程不拥有资源,就能轻装前进,显著提高系统的并发程度,

在同一进程内,线程的切换不会引起进程的切换,但从一个进程的线程切换到另一个进程中的线程时,会引起进程的切换。

(2) 并发性

引入线程的OS,进程可并发执行, 且一个进程中多个线程之间也可并发执行,使得OS有更好的并发性,更加有效地提升系统资源的利用率和系统的吞吐量

(3) 拥有资源

OS 的进程可以拥有资源,是系统拥有资源的一个基本单位
线程自己不拥有资源(有一点点必不可少的资源),但它可以访问隶属于进程的资源(进程的代码段,数据段及所拥有的系统资源,
如打开文件,I/O设备等,都可供进程的所有线程共享)

4)系统开销

  • 创建与销毁进程时,系统为之创建和回收进程控制块PCB,分配或回收其资源(内存空间,I/O设备),OS 为之付出的开销明显大于线程创建或撤销时的开销。

  • 进程切换时,涉及到当前进程CPU环境的保存及被新调度运行进程的CPU环境的设置,而线程的切换仅保存和设置少量寄存器内容,不涉及存储器管理方面的操作,这样看来,切换代价方面进程也是远高于线程的。

  • 同步与通信方面,一个进程中的多个线程具有相同的地址空间,在同步和通信的实现方面线程也比进程容易(且大多OS线程的切换同步与通信都无须OS内核的干预)

3. 线程的属性

多线程OS中,一个进程包括多个线程,每个线程都是作为利用CPU的基本单位,是花费最小开销的实体,有如下属性

(1) 轻型实体

线程实体基本上不拥有系统资源,只有一点必不可少的,能保证其独立运行的资源。
每个线程都有一个用于控制线程运行的线程控制块TCB,用于指示被执行指令序列的程序计数器,保留局部变量,少数状态参数和返回地址等的一组寄存器和堆栈)。

(2)独立调度和分派的基本单位

在多线程 OS 中,线程是能独立运行的基本单位,因而也是独立调度和分派的基本单位
由于线程很“轻”,故线程的切换非常迅速且开销小

(3)可并发执行

在一个进程中的多个线程之间可以并发执行,甚至允许在一个进程中的所有线程都能并发执行;同样,不同进程中的线程也能并发执行。

(4) 共享进程资源

在同一进程中的各个线程都可以共享该进程所拥有的资源,这首先表现在所有线程都具有相同的地址空间(进程的地址空间)。
这意味着线程可以访问该地址空间中的每一个虚地址;此外,还可以访问进程所拥有的已打开文件、定时器、信号量机构 等。

4. 线程的状态

(1) 状态参数

在OS中每一个线程都可以利用线程标识符和一组状态参数进行描述。

状态参数:

  • 寄存器状态:包括程序计数器PC 和堆栈指针中的内容
  • 堆栈:堆栈中保存局部变量与返回地址
  • 线程运行状态:用于描述线程在处于何种状态
  • 优先级:描述线程执行的优先程度
  • 线程专有存储器:用于保存线程自己的局部变量拷贝
  • 信号屏蔽:即对某些信号加以屏蔽

(2) 线程运行状态

与传统进程相似,各线程之间也存在着共享资源和相互合作的制约关系,导致线程在运行时也具有间断性。

线程在运行期间具备以下三种基本状态:

  • 执行状态:表示线程正获得处理机并运行
  • 就绪状态:指线程已经具备了各种执行条件,一旦获得CPU便可执行的状态。
  • 阻塞状态:指线程在执行时因某事件 而受阻,处于暂停执行时的状态。

5. 线程的创建和终止

创建:

在多线程OS环境下,应用程序在启动时,通常只有一个线程执行,该线程被人们称为”初始化线程“,它可根据需要再去
创建若干个线程。

创建线程时:利用线程创建函数(或系统调用),提供相应参数(指向线程主程序的入口指针,堆栈的大小,以及用于调度的优先级),线程创建函数执行完后,将返回一个线程标识符供以后使用

终止线程:

线程终止的方式:

  • 线程完成了自己的工作后自愿退出;
  • 线程在运行中出现错误或由于某种原因而被其他线程强制终止。

一些线程(主要是系统线程),在它们一旦被建立起来,便一直运行下去而不再被终止。

注意:

(1)在大多数OS,线程中止后不会立即释放它所占用的资源,仅当进程中的其他线程执行了分离函数后,被终止的线程才与资源分离,这时的资源才能被其他线程利用。

(2)虽被终止但尚未释放资源的线程,仍可以被需要它的线程所调用(调用者线程使用 ”等待线程终止“的连接命令来与该线程进行连接),以使被终止线程重新恢复起来。

(3)若一个调用者线程调用命令试图与指定线程相连接时,若指定线程未被终止,则调用连接命令的线程会被阻塞,直至
指定线程被终止后才能实现与它调用者线程的连接并继续执行;若指定线程已经被终止,则调用者线程不会被阻塞而是继续执行。

6. 多线程OS中的进程

在多线程OS中,进程是作为拥有系统资源的基本单位,通常的进程包含多个线程并为它们提供资源,但此时的进程就不能作为一个执行的实体。

多线程OS的进程有如下属性:

(1)作为系统资源分配的单位。
在任一进程中所拥有的资源包括受到分别保护的用户地址空间、用于实现进程间和线程间同步和通信的机制、
已打开的文件和已申请到的 I/O 设备,
以及一张由核心进程维护 的地址映射表,该表用于实现用户程序的逻辑地址到其内存物理地址的映射。

(2) 包括多个线程

一个进程都含有多个相对独立的线程,其数目可多可少,但至少也要有一个线程,由进程为这些(个)线程提供资源及运行环境,使这些线程可并发执
行。在 OS 中的所有线程都只能属于某一个特定进程。

(3) 进程不是一个可执行的实体

在多线程 OS 中,是把线程作为独立运行的基本单位, 所以此时的进程已不再是一个可执行的实体。

虽然如此,进程仍具有与执行相关的状态。
例如,所谓进程处于“执行”状态,实际上是指该进程中的某线程正在执行。此外,对进程所施加的与进程状态有关的操作,也对其线程起作用。例如,在把某个进程挂起时,该
进程中的所有线程也都将被挂起;又如,在把某进程激活时,属于该进程的所有线程也都 将被激活。

2.6.2 线程间的同步和通信(非常重要)

为使系统中的多线程能有条不紊地运行,在系统中必须提供用于实现线程间同步和通信的机制。
为了支持不同频率的交互操作和不同程度的并行性,在多线程 OS 中通常提供多种同步机制,如互斥锁、条件变量、计数信号量以及多读、单写锁

1. 互斥锁(mutex)

互斥锁是一种比较简单的,时空开销低的、且用于实现线程间对资源互斥访问的机制(较使用高频度使用的关键共享数据和程序段)。

互斥锁状态: 开锁(unlock) 和 关锁(lock)状态。

互斥锁操作: 关锁lock 将 mutex 关上,开锁操作 unlock 则用于打开mutex

使用:

  • 当一个线程需要读/写一个共享数据段时,线程应该首先为该数据段所设置的mutex执行关锁命令
  • 命令会判别 mutex 的状态,如果处于关锁状态,则试图访问该数据段的线程将被阻塞;而如果 mutex 是处于开锁状态,则将
    mutex 关上后便去读/写该数据段
  • 在线程完成对数据的读/写后,必须再发出开锁命令将mutex打开,同时将阻塞在该互斥锁上的一个线程唤醒,其他的线程
    仍被阻塞在等待 mutex 打开的队列上

另外:为了减少线程被阻塞的机会,在有的系统提供了一种用于mutex上的操作命令 Trylock,当一个线程在利用 Trylock 命令去访问mutex时,若mutex 处于开锁状态,Trylock将返回成功的状态码,若mutex处于关锁状态,则Trylock并不会阻塞该线程,而只是返回一个指示操作失败的状态码

2. 条件变量

在许多情况下,只使用 mutex 实现互斥访问可能会引起死锁的问题:

有一个线程在对 mutex1 执行关锁操作成功后,便进入临界区 C,如果在临界区内该线程又须访问某个临界资源R,同样为R设置另一互斥锁 mutex2.假如资源R此时处于忙碌状态,则线程在对 mutex2 执行关锁操作时必将被阻塞,这样将使得 mutex1一直保持关锁状态;如果 保持了资源R的线程也要求进入临界区C,但由于mutex1一直保持关锁状态而无法进入临界区,这样便形成了死锁。

条件变量的引入就是为了解决上述问腿(线程间死锁)

一般情况下每一个条件变量通常与一个互斥锁一起使用,单独的互斥锁用于短期的锁定保证临界区的互斥访问,而
条件变量则用于线程的长期等待,直至所等待的资源称为可用的资源。

互斥锁与条件变量的搭配使用(对资源R的访问)

  • 线程对mutex 执行关锁操作后进入临界区,并查找R,若所需资源R处于忙碌装填,线程便转为等待(阻塞)状态,并对mutex开锁,并等待R资源释放,当R资源释放后,wait该函数将取消阻塞并调用lck.lock()

  • 资源处于空闲状态,表明线程可用使用该资源,于是再将该R资源设置为忙碌状态,再对 mutex执行开锁操作

下面我们描述以下资源的申请部分:

 Lock mutex
    check data structures;
    while(resources busy)
       wait(condition variable);       
    mark resources as busy;
  unlock mutex;

资源R的释放

  Lock mutex
    mark resources  as free;
  unlock mutex;
  wakeup(conditon variable);

占有资源R的线程在使用完该资源后,便按右边的描述释放资源,其中wakeup表示去唤醒在指定条件变量上等待的一个或
多个线程。

3. 信号量机制

实现进程同步的最常用工具——信号量机制,也可用于多线程 OS中,实现诸线程或进程之间的同步
为了提高效率,可为线程和进程分别设置相应的信 号量。

  • 私有信号量(private samephore:不安全)

当线程需利用信号量实现同一进程中各线程之间的同步时,可调用创建信号量的命令创建一私用信号量,其存放在应用程序的
地址空间中,属于特定进程所有,OS不知道其存在。

若私有信号量占有者异常结束或正常结束,但未释放器信号量占用的空间情况时,系统无法将它恢复为 0,也不能将其传递给下一个请求它的
线程。

  • 公有信号量(public samephore:安全的同步机制)

公用信号量是为实现不同进程间或不同进程中各线程之间的同步而设置的。由于它有着一个公开的名字供所有的进程使用,故而把它称为公用信号量。

其存放在受保护的系统存储区, OS为它分配空间并进行管理,称为系统信号量。

若信号量的占有者在结束时未释放该公用信号量,则 OS会自动将信号量空间回收,并通知下一进程。

2.6.3 线程的实现方式

线程的实现方式一般是:内核支持线程与用户级线程

1. 内核支持线程

进程与线程都是在OS内核的支持下运行的,与内核紧密相关。

内核支持线程(KST):内核空间为每一个内核支持线程设置了线程控制块,内核根据该控制块感知到某个线程的存在,并对其加以控制。

优点:

  • 在多处理器系统下,内核能够同时调度同一进程中多个线程并行执行
  • 如果进程中的一个线程阻塞了,内核可用调度该进程中其他线程占有处理器运行,也可以运行其他进程中的线程
  • 内核支持线程具有很小的数据结构和堆栈,线程切换比较块,开销小
  • 内核本身也采用多线程技术,提供系统的执行速度和效率

缺点:对于用户的线程切换而言,其切换开销大,需要从用户态转到内核态进行(用户进程的线程在用户态运行,线程调度和管理在内核实现,系统开销大)

2. 用户级线程

用户级线程ULT(User Level Threads)仅存在于用户空间中,无须系统调用来实现其功能,也无需内核的支持。

值得一提:设置用户级别线程的系统,调度是以进程为单位进行的。

在采用轮转调度算法中,各个进程轮流执行一个时间片,这对诸进程而言是公平的。

假如A进程包含了用户级线程,另一个进程B含有100个用户级线程,这样A进程中线程的运行事件是进程B中个线程运行事件的100倍,相应速度要快100倍。

系统中设置的是内核支持线程,则调度便是以线程为单位进行的,采用轮转法调度时,各个线程轮流执行一个时间片,同时假定进程A中只有一个内核支持线程,而进程B中有100个内核支持线程。此时进程B可用获得的CPU时间是进程A的100倍,进程B可使100个系统调用并发工作。

用户级线程优点:

(1) 线程切换不需要转换到内核空间:节省了模式切换开销,节省内核宝贵资源
(2) 调度算法可用是进程专用的。
(3) 用户级线程的实现与操作系统平台无关,因为对于线程管理的代码是在用户程序中的。

缺点:

(1)系统调用的阻塞:基于进程机制的OS,大多数系统调用将阻塞进程,,因此当线程执行一次系统调用时,该进程内的所有线程都会被阻塞。而内核支持线程方式,进程中的其他线程仍然可以运行

(2)在单纯的用户级线程实现方式中,多线程应用不能利用多处理机进行多重处理的优 点。
内核每次分配给一个进程的仅有一个 CPU,因此进程中仅有一个线程能执行,在该线 程放弃 CPU 之前,其它线程只能等待。

  1. 组合方式

组合方式线程系统中,内核支持多KST线程的建立,调度和管理,同时,也允许用户应用程序建立,调度和管理用户级线程。

组合方式多线程机制能结合KST和ULT的优点,并克服其各自不足,是优良的线程实现方式。

2.6.4 线程的实现

不论是进程还是线程,都必须直接或间接地取得内核的支持。
由于内核支持线程可以直接利用系统调用为它服务,故线程的控制相当简单;
而用户级线程必须借助于某种形式的中间系统的帮助方能取得内核的服务,故在对线程的控制上要稍复杂些。

  1. 内核支持线程的实现(KST)

系统在创建一个新进程时,便为其分配任务数据区PTDA(Per Task Data Area),包括若干个线程控制块TCB空间,在每一个
TCB中可保存线程标识符,优先级,线程运行的CPU状态等信息。这些信息与用户级别线程 TCB 中的信息相同,但现在却是被保存在内核空间中。

  • 进程创建线程时便为线程分配一个TCB,将有关信息填入该 TCB 中,并为之分配必要的资源
  • 当PTDA中的所有TCB空间已经被用完,而进程又要创建新的线程时,只需要其创建的线程数目未超过系统的允许值,系统为PTDA分配新的TCB空间。
  • 撤销线程时,回收线程的资源和TCB。有的系统为了减少创建和撤销一个进程时的开销,在撤销进程时,并不回收该线程的资源和TCB,当以后再创建一个新线程时便可利用已经被撤销但仍保持资源和TCB的线程作为新线程。

内核支持线程的调度和切换分抢占式和非抢占方式两种:

  • 线程的调度算法上采用 时间轮转法,优先权法
  • 当线程调度选中一个线程时,便将处理机分配给它。
  1. 用户级线程的实现

用户级线程是在用户空间实现的。所有的用户级线程具有相同的结构,它们运行在有两种方式实现的中间系统。

(1) 运行时系统(Runtime System)

用于管理和控制线程的函数的集合。

其包括用于创建和撤销线程的函数,线程同步和通信的函数,运行时系统的所有函数都驻留在用户控件,作为用户级线程与内核之间的接口

用户级线程的切换过程:

传统OS:先由用户态转为核心态,再由核心来执行切换任务。
用户级线程:在切换时无需转入核心态,而是由运行时系统中的线程切换过程来执行切换任务,大致过程为:

  • 将线程的CPU状态保存在线程的堆栈中
  • 按照一定的算法选择处于就绪状态的新线程运行,将新线程堆栈中的CPU状态转入CPU相应的寄存器中,一旦将栈指针和程序计数器切换后
    便开始了新线程的运行,由于用户级线程的切换无需进入内核,且切换操作简单,因而使得用户级线程的切换速度十分快。

在传统OS与多线程OS中,系统资源都是由内核管理的,用户级线程是不能 利用系统调用的,当线程需要系统资源时,将该要求传送到
运行时系统,后者通过相应的系统调用来获得系统资源。

(2)内核控制进程(轻型线程 Light Weight Process)

进程可拥有多个LWP。

LWP有自己的数据结构(TCB)其中包含(线程标识符,优先级,状态,还有栈和局部存储区)。
LWP通过系统调用来获得内核提供的服务。
当用户线程运行时将其连接到LWP上,它便具有了内核支持线程的所有属性,这种方式就是组合方式。

LWP缓冲池(线程池):一个系统中的用户级线程数量很大,为了节省系统开销,不可能设置太多LWP,因此将LWP做成了缓冲池。

用户进程的任一用户线程都可以连接到LWP池任意一个LWP上, 为了使用户线程能利用LWP与内核通信,使得多用户级线程多路复用
一个LWP,连接上LWP的线程才能与内核通信,其余进程或者阻塞,或则等待LWP。

每一个LWP都要连接到内核级线程上,这样通过LWP可将用户级线程与内核线程连接起来。

仅当用户线程需要与内核通信时才需要LWP。

内核级线程执行操作时,如果发送阻塞,则与之相连接的多个LWP也随之阻塞,进而其LWP上的用户级线程也阻塞。

  1. 用户级线程与内核控制线程的连接

实际在不同OS中,用户级线程与内核控制线程的连接有不同的模型:一对一,多对一,与多对多

  • 一对一模型

为每一个用户线程设置一个内核控制线程与之相连接,一个线程阻塞时,允许调度另一个线程运行。

该模型的并行能力较强,每创建一个用户线程相应地需要创建一个内核线程,开销较大,因此需要限制整个系统的线程数。

  • 多对一模型

该模型将多用户进程映射到一个内核控制线程。

为了管理方便,这些用户线程一般属于一个进程,运行与调度管理都在进程的用户空间,仅当用户现场访问内核时,才
映射到内核控制线程上,但每次仅允许一个线程进行映射。

优点: 线程管理开销小,效率高,
缺点:当一个内核控制线程访问内核发送阻塞,则整个进程都会阻塞。

  • 多对多模型

结合上述两种模型的优点,将多用户模型映射到多个内核控制线程,内核控制线程的数目可用根据进程和系统的不同而变化,可用比用户线程少也可与之相同。



进程面试题

进程与线程面试题(亲身经历)

第三篇幅:处理机调度与死锁

什么是死锁?死锁产生的条件?

1). 死锁的概念

所谓死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去,此时称系统处于死锁状态或系统产生了死锁。通俗的讲,就是两个或多个进程无限期的阻塞、相互等待的一种状态。

2). 死锁产生的四个必要条件

互斥条件:一个资源每次只能被一个进程使用;若其他进程申请使用该资源,必须等到该资源被释放为止;

请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放;

不可剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺;

循环等待条件:若干进程之间形成一种头尾相接的环形等待资源关系;

死锁的解除与预防

(1). 死锁预防
  
死锁预防的基本思想是 只要确保死锁发生的四个必要条件中至少有一个不成立,就能预防死锁的发生,具体方法包括:

注意:互斥条件无法破坏

  • 打破请求与保持条件:

可以实行资源预先分配策略(进程在运行前一次性向系统申请它所需要的全部资源,若所需全部资源得不到满足,则不分配任何资源,此进程暂不运行;只有当系统能满足当前进程所需的全部资源时,才一次性将所申请资源全部分配给该线程)或者只允许进程在没有占用资源时才可以申请资源(一个进程可申请一些资源并使用它们,但是在当前进程申请更多资源之前,它必须全部释放当前所占有的资源)。但是这种策略也存在一些缺点:在很多情况下,无法预知一个进程执行前所需的全部资源,因为进程是动态执行的,不可预知的;同时,会降低资源利用率,导致降低了进程的并发性。

  • 打破不可剥夺条件:

允许进程强剥夺使用其他进程占有的资源,从而破坏不可剥夺条件。也就是说,一个进程占有了一部分资源,在其申请新的资源且得不到满足时,它必须释放所有占有的资源以便让其它线程使用。这种预防死锁的方式实现起来困难,会降低系统性能。

  • 打破循环等待条件:

实行资源有序分配策略,破坏环路条件。对所有资源排序编号,所有进程对资源的请求必须严格按资源序号递增的顺序提出,即只有占用了小号资源才能申请大号资源,这样就不回产生环路,预防死锁的发生。

(2). 死锁避免的基本思想
 
死锁避免的基本思想是动态地检测资源分配状态,以确保循环等待条件不成立,从而确保系统处于安全状态

所谓安全状态是指:

如果系统能按某个顺序为每个进程分配资源(不超过其最大值),那么系统状态是安全的,换句话说就是,如果存在一个安全序列,那么系统处于安全状态。资源分配图算法和银行家算法是两种经典的死锁避免的算法,其可以确保系统始终处于安全状态。其中,资源分配图算法应用场景为每种资源类型只有一个实例(申请边,分配边,需求边,不形成环才允许分配),而银行家算法应用于每种资源类型可以有多个实例的场景。

银行家算法:

该算法需要检查申请者对资源的最大需求量,如果系统现存的各类资源可以满足申请者的请求,就满足申请者的请求。这样申请者就可以很快完成其计算,然后释放它占用的资源,从而保证了系统中所有进程都能完成,所以可避免死锁的发生。

第四篇幅:存储器管理

什么是缓冲区溢出?有什么危害?其原因是什么?

缓冲区溢出:当计算机向缓冲区内填充数据时超过了缓冲区本身的容量,溢出的数据覆盖在合法数据上

危害:在当前网络与分布式系统安全中,被广泛利用的50%以上都是缓冲区溢出,其中最著名的例子是1988年利用fingerd漏洞的蠕虫。而缓冲区溢出中,最为危险的是堆栈溢出,因为入侵者可以利用堆栈溢出,在函数返回时改变返回程序的地址,让其跳转到任意地址,带来的危害一种是程序崩溃导致拒绝服务,另外一种就是跳转并且执行一段恶意代码,比如得到shell,然后为所欲为。通过往程序的缓冲区写超出其长度的内容,造成缓冲区的溢出,从而破坏程序的堆栈,使程序转而执行其它指令,以达到攻击的目的。

造成缓冲区溢出的主原因是程序中没有仔细检查用户输入的参数

内存管理有哪几种方式

内存管理有块式管理,页式管理,段式和段页式管理。现在常用段页式管理

  • 块式管理:把主存分为一大块、一大块的,当所需的程序片断不在主存时就分配一块主存空间,把程序片断load入主存,就算所需的程序片度只有几个字节也只能把这一块分配给它。这样会造成很大的浪费,平均浪费了50%的内存空间,但是易于管理。

  • 页式管理:把主存分为一页一页的,每一页的空间要比一块一块的空间小很多,显然这种方法的空间利用率要比块式管理高很多。

  • 段式管理:把主存分为一段一段的,每一段的空间又要比一页一页的空间小很多,这种方法在空间利用率上又比页式管理高很多,但是也有另外一个缺点。一个程序片断可能会被分为几十段,这样很多时间就会被浪费在计算每一段的物理地址上。

  • 段页式管理:结合了段式管理和页式管理的优点。将程序分成若干段,每个段分成若干页。段页式管理每取一数据,要访问3次内存。

分页和分段有什么区别?

段式存储管理是一种符合用户视角的内存分配管理方案。在段式存储管理中,将程序的地址空间划分为若干段(segment),如代码段,数据段,堆栈段;这样每个进程有一个二维地址空间,相互独立,互不干扰。段式管理的优点是:没有内碎片(因为段大小可变,改变段大小来消除内碎片)。但段换入换出时,会产生外碎片(比如4k的段换5k的段,会产生1k的外碎片)

页式存储管理方案是一种用户视角内存与物理内存相分离的内存分配管理方案。在页式存储管理中,将程序的逻辑地址划分为固定大小的页(page),而物理内存划分为同样大小的帧,程序加载时,可以将任意一页放入内存中任意一个帧,这些帧不必连续,从而实现了离散分配。页式存储管理的优点是:没有外碎片(因为页的大小固定),但会产生内碎片(一个页可能填充不满)。

两者的不同点:

目的不同:分页是由于系统管理的需要而不是用户的需要,它是信息的物理单位;分段的目的是为了能更好地满足用户的需要,它是信息的逻辑单位,它含有一组其意义相对完整的信息;

大小不同:页的大小固定且由系统决定,而段的长度却不固定,由其所完成的功能决定;

地址空间不同: 段向用户提供二维地址空间;页向用户提供的是一维地址空间;

信息共享:段是信息的逻辑单位,便于存储保护和信息的共享,页的保护和共享受到限制;

内存碎片:页式存储管理的优点是没有外碎片(因为页的大小固定),但会产生内碎片(一个页可能填充不满);而段式管理的优点是没有内碎片(因为段大小可变,改变段大小来消除内碎片)。但段换入换出时,会产生外碎片(比如4k的段换5k的段,会产生1k的外碎片)。

页面置换算法

  • 最佳置换算法OPT:只具有理论意义的算法,用来评价其他页面置换算法。置换策略是将当前页面中在未来最长时间内不会被访问的页置换出去。

  • 先进先出置换算法FIFO:简单粗暴的一种置换算法,没有考虑页面访问频率信息。每次淘汰最早调入的页面。

  • 最近最久未使用算法LRU:算法赋予每个页面一个访问字段,用来记录上次页面被访问到现在所经历的时间t,每次置换的时候把t值最大的页面置换出去(实现方面可以采用寄存器或者栈的方式实现)。

  • 时钟算法clock(也被称为是最近未使用算法NRU):页面设置一个访问位,并将页面链接为一个环形队列,页面被访问的时候访问位设置为1。页面置换的时候,如果当前指针所指页面访问为为0,那么置换,否则将其置为0,循环直到遇到一个访问为位0的页面。

  • 改进型Clock算法:在Clock算法的基础上添加一个修改位,替换时根究访问位和修改位综合判断。优先替换访问位和修改位都是0的页面,其次是访问位为0修改位为1的页面。

  • LFU最少使用算法LFU:设置寄存器记录页面被访问次数,每次置换的时候置换当前访问次数最少的。

什么是虚拟内存?

1).内存的发展历程

没有内存抽象(单进程,除去操作系统所用的内存之外,全部给用户程序使用) —> 有内存抽象(多进程,进程独立的地址空间,交换技术(内存大小不可能容纳下所有并发执行的进程)
)—> 连续内存分配(固定大小分区(多道程序的程度受限),可变分区(首次适应,最佳适应,最差适应),碎片) —> 不连续内存分配(分段,分页,段页式,虚拟内存)

2).虚拟内存

虚拟内存允许执行进程不必完全在内存中。虚拟内存的基本思想是:每个进程拥有独立的地址空间,这个空间被分为大小相等的多个块,称为页(Page),每个页都是一段连续的地址。这些页被映射到物理内存,但并不是所有的页都必须在内存中才能运行程序。当程序引用到一部分在物理内存中的地址空间时,由硬件立刻进行必要的映射;当程序引用到一部分不在物理内存中的地址空间时,由操作系统负责将缺失的部分装入物理内存并重新执行失败的命令。这样,对于进程而言,逻辑上似乎有很大的内存空间,实际上其中一部分对应物理内存上的一块(称为帧,通常页和帧大小相等),还有一些没加载在内存中的对应在硬盘上,如图所示。
注意,请求分页系统、请求分段系统和请求段页式系统都是针对虚拟内存的,通过请求实现内存与外存的信息置换。

由图可以看出,虚拟内存实际上可以比物理内存大。当访问虚拟内存时,会访问MMU(内存管理单元)去匹配对应的物理地址(比如图5的0,1,2)。如果虚拟内存的页并不存在于物理内存中(如图5的3,4),会产生缺页中断,从磁盘中取得缺的页放入内存,如果内存已满,还会根据某种算法将磁盘中的页换出。

3). 虚拟内存的应用与优点

虚拟内存很适合在多道程序设计系统中使用,许多程序的片段同时保存在内存中。当一个程序等待它的一部分读入内存时,可以把CPU交给另一个进程使用。虚拟内存的使用可以带来以下好处:

在内存中可以保留多个进程,系统并发度提高

解除了用户与内存之间的紧密约束,进程可以比内存的全部空间还大

颠簸

颠簸本质上是指频繁的页调度行为,具体来讲,进程发生缺页中断,这时,必须置换某一页。然而,其他所有的页都在使用,它置换一个页,但又立刻再次需要这个页。因此,会不断产生缺页中断,导致整个系统的效率急剧下降,这种现象称为颠簸(抖动)。

内存颠簸的解决策略包括:

如果是因为页面替换策略失误,可以修改替换算法来解决这个问题;

如果是因为运行的程序太多,造成程序无法同时将所有频繁访问的页面调入内存,则要降低多道程序的数量;

否则,还剩下两个办法:终止该进程或增加物理内存容量。

局部性原理

(1). 时间上的局部性:最近被访问的页在不久的将来还会被访问;

(2). 空间上的局部性:内存中被访问的页周围的页也很可能被访问。

第五篇幅: 设备管理

第六篇幅: 文件管理

第七篇幅: 操作系统接口

第八篇幅: 网络操作系统

第九篇幅: 系统安全性

第十篇幅: UNIX系统内核结构

猜你喜欢

转载自blog.csdn.net/chongzi_daima/article/details/107349517