操作系统知识整理

第一章 操作系统概论

1.1 操作系统概观

(1)操作系统的定义和目标

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

计算机系统的层次结构:

硬件->操作系统->系统软件->应用软件

接口提供了一条合理的边界,使接口的两边可以很好的对话。接口可分成硬―硬、软硬、软软几类, OS和硬件之间是软硬接口

OS是一个协调者和交通警察

管理资源

    硬件资源(处理机,存储器,设备)

    信息资源(文件)

解决申请资源时产生的冲突

阻止错误的产生和对计算机不正当的使用

OS是一个便利店

提供良好的用户界面

标准的函数库

使得编程更加方便并且不容易出错

(2)操作系统的作用和功能

主要功能:

处理器管理

存储管理

设备管理

文件管理

网络与通信管理

用户接口

观察和研究操作系统的方法(windows)->Windows XP SP3

文件分析exescope解析exe

静态二进制分析winhex

静态逆向分析peid

动态运行状态观察ida

动态调试分析

动态注入分析

层次化分析

功能性分析

分析逻辑:BIOS->MBR->DBR->NTLDR->ntoskmd.exe->smss.exe

0X80000000划分内核和应用层

OS接管计算机的过程

打开电源

●CPU将控制权交给BIOS (基本输入输出系统,存放在CMOS中)

●BIOS运行一个程序:通电自测试程序

●BIOS确认所有外部设备:硬盘或扩充卡

●BIOS找到磁盘的引导区(通常在磁盘的最开始的部分),将接下来的512Bytes主引导程序装入内存。(主引导程序是一段代码,它可以将OS余下部分装入内存)

引导操作系统结束,操作系统接管计算机

(3)操作系统的主要特性

并发性

共享性

异步性

虚拟性

 

1.2 操作系统的形成和发展

(1)人工操作阶段

(2)管理程序阶段

(3)多道程序设计与操作系统的形成

多道程序设计是指允许多个程序同时进入一个计算机系统的主存储器并启动进行计算的方法。 

从宏观上看并行

从微观上看串行

引入多道程序设计技术的根本目的:提高CPU的利用率

实现该目标的硬件保证:处理器和外围设备具可以并行工作

优点:

提高了CPU的利用率,

提高了内存和I/O设备的利用率,

改进了系统的吞吐率,

充分发挥了系统的并行性。

缺点

作业周转时间延长。

三个关键促成了操作系统的形成

中断技术

   实现了多道程序的切换

通道技术

   实现了硬件之间的并行性

磁盘的问世

   高速高容量的存储器充当虚拟存储器

操作系统的分类

批处理操作系统

分时操作系统

实时操作系统

微机操作系统

并行操作系统

网络操作系统

分布式操作系统

嵌入式操作系统

1.3 操作系统提供的服务和用户接口

用户如何使用OS提供的服务?

操作系统提供了许多服务,底层服务通过系统调用来实现,可被用户程序直接使用。

高层的服务通过系统程序来实现,用户不必自己编写程序而是借助命令来请求执行完成各种功能

系统调用(广义指令)是由操作系统在机器指令(访管指令)基础上实现的,是能完成特定功能的过程或子程序。

程序接口又称应用编程接口API,允许运行程序调用操作系统的服务和功能。

程序接口由一组系统调用(System Call)组成,用户程序使用系统调用就可获得操作系统的底层服务,使用或访问系统的各种软硬件资源

1.4 操作系统的结构设计

整体式结构(简单结构)

操作系统的整体式结构又叫模块组合法,它按照功能划分模块,模块间可以互相调用,最后把所有的模块连接起来构成一个完整的系统。

优点:结构紧密,组合方便,执行效率高。

缺点:模块独立性差,可靠性低,系统功能增减十分困难

层次式结构

它把操作系统划分为内核和若干模块(或进程),这些模块(或进程)按功能的调用次序排列成若干层次,各层之间只能是单向依赖或单向调用关系,这样不但系统结构清晰,而且不构成循环。

部分功能无法很分清谁上谁下,如进程调度和存储管理。

该结构很适合平台和硬件的移植,此设计思想很值得借鉴。

虚拟机结构

客户/服务器及微内核结构

由两类进程和微内核组成

每个进程实现一类服务,称服务器进程(如文件服务、进程管理服务、存储管理服务、网络通信服务等)。它的任务是检查是否有客户提出服务请求,如果有请求则在满足客户的要求后返回结果,于是,客户进程与服务器进程形成了C/S关系。

与层次结构相似,但更加灵活

操作系统的运行模型

非进程内核模型

OS功能在用户进程内执行的模型

OS功能作为独立进程执行的模型

1.5 流行操作系统简介

第二章处理器管理

一个程序装载入内存后有四个部分:

代码

数据

(heap)malloc分配的空间就是从这里来的

(stack):系统使用的空间,保存函数的返回地址。

程序的执行过程

PC(程序计数器)所指地址取出

   指令,并且装载到IR(指令寄存器)

●CPU解码并指执行该指令

将执行结果写入到内存当中

●PC=下一条指令在内存中的地址

进程是一组数据结构的集合,程序只是进程的一个部分同一个程序可以对应多个进程(e.g.聊天软件)程序被载入内存开始执行»进程

CPU当前的PC值和寄存器堆值都是与当前正在执行的进程相关的,为了使得该进程在重新获得CPU后能够继续工作,在其被剥夺CPU时就应该把那些CPU状态信息保存到进程的数据结构当中去。

 我们把进程运行所需要的这些状态信息称为进程上下文(context),进程(虚拟CPU)之间的切换叫作上下文切换。

2.1 中央处理器 

(1)处理器的构成

寄存器是一组CPU内部的存储器,容量小但速度快,一般存放操作数、地址以及控制信息等。各种寄存器构成了一级储存

(2)特权指令与非特权指令

特权指令:只能由操作系统使用的指令。(如启动I/O设备、设置时钟、控制中断屏蔽位、清内存、建立存储键,加载PSW)

使用多道程序设计技术的计算机指令系统必须要区分为特权指令和非特权指令

(3)处理器状态

管理状态(特权状态、系统模式、特态或管态)

程序可以执行全部指令,使用所有资源,具有改变处理器状态的能力

用户状态(目标状态、用户模式、常态或目态)

程序只能执行非特权指令

(4)程序状态字寄存器

通常操作系统都引入程序状态字PSW来区别不同的处理器工作状态并且保留和指示与运行程序有关的各种信息。它主要内容包括: 

程序基本状态

程序计数器PC

条件码:反映指令执行后的结果特征

处理器状态

中断码:保存程序执行当时发生的中断事件

中断屏蔽码:指明程序执行中发生中断事件时,是否响应出现的中断事件

PSW保存了程序的CPU现场信息

每个程序都有一个与其执行相关的PSW,每个处理器都设置一个PSW寄存器。程序占有处理器执行,它的PSW将占有PSW寄存器。

各种不同的处理器的控制寄存器组织方式不同,所以大多数计算机的处理器现场都有一组控制与状态寄存器构成。

2.2 中断技术

(1)中断的概念

中断是指程序执行过程中

当发生某个事件时,中止CPU上现行程序的运行

引出该事件的处理程序执行

执行完毕返回原程序中断点继续执行

中断处理是操作系统的重要组成部分操作系统就是由中断驱动的中断是现代计算机系统中基本设施之一,它起着通讯联络作用,协调系统对各种外部事件的响应和处理中断是实现多道程序的必要条件

中断系统

中断源:引起中断的事件

中断装置:指发现中断,响应中断的硬件

中断处理程序:由软件来完成

中断系统 中断装置 中断处理程序

中断由软硬件协同处理

异常:发生在指令执行当中

中断:发生在指令执行之间

中断装置

主要工作:

发现中断源,响应中断请求(中断响应的实质就是交换指令地址及处理机的状态信息)

保护现场

启动处理中断事件的中断处理程序处理器状态已从目态切换到管态

关键部件:

中断寄存器:由若干个中断位组成,每个中断源分别占用一位,规定值为1时,表示有中断信号,为0时表示无

中断字:中断寄存器的内容称为中断字

处理器状态的转换

中断是目态向管态转换的唯一途径!系统调用实质上也是一种中断。

●OS提供Load PSW指令装载用户程序PSW返回用户状态

如何发现中断信号?

在每条指令执行周期的最后时刻扫描中断寄存器,询问是否有中断信号?

若无中断信号,继续执行下一条指令

若有中断,中断硬件将该中断寄存器内容按规定编码送入PSW的中断码字段,并且把中断寄存器的相应位置0

保护现场(PSW)

要保护的内容有:

程序基本状态

程序计数器PC

条件码:反映指令执行后的结果特征

处理器状态

中断码:保存程序执行当时发生的中断事件

中断屏蔽码:指明程序执行中发生中断事件时,是否响应出现的中断事件

最后,将中断处理程序的程序状态字送于PSW寄存器,就引出了中断事件处理程序。

 

(2)中断源分类

中断事件具体处理方法

机器故障中断事件

程序性中断事件

外部中断事件

●I/O中断事件

自愿中断事件

(3)中断装置

2.3 进程及其实现

程序执行的动态性

程序存放在外存中不会消失,产生和消亡的只是与之相关的内存信息。

 

程序只有进入了内存才能执行,在执行过程中可能会被另一个执行的程序打断暂失去CPU

 

用进程一词来描述程序这一动态的执行过程。程序P1被装入内存后被称为进程P1

程序的共享性

●“可再用程序

可被其它程序调用,但每次只能被一个程序调用

有独立的工作区

 

●“可再入程序

纯代码

由调用程序提供工作区

可同时被多个程序调用

 

进程是一个程序的一次执行过程

能完成具体的功能

是在某个数据集合上完成的

执行过程是可并发的

进程是资源分配、保护和调度的基本单位

动态性    结构性     并发性

制约性    共享性     独立性

进程与程序的区别

程序是静态的概念;进程是动态的概念

进程是一个独立运行的活动单位

进程是竞争系统资源的基本单位

一个程序可以对应多个进程;一个进程至少包含一个程序

进程的状态

进程在其创建后至消亡前这段时间可以划分成三种基本状态:

就绪:一个进程已经具备运行条件,但由于无CPU暂时不能运行的状态(当调度给其CPU时,立即可以运行)

运行:进程占有CPU,并在CPU上运行。同一时刻处于此状态的进程有且仅有一个

等待(阻塞、睡眠):进程因等待某种事件的发生而暂时不能运行的状态(即使CPU空闲,该进程也不可运行)

 

三态模型及其状态转换

 

五态模型及其状态转换

进程的挂起(suspend)是指将内存中的进程对换到磁盘镜像区中的过程。

进程被挂起的原因主要有:

系统中所有进程都处于等待状态

系统资源不足

对换一些定期执行的进程

用户要求挂起自己的进程

父进程要求挂起自已的子进程

●OS要求挂起某些进程

进程的内存映像

进程控制块(PCB)

用户程序

用户数据

堆栈

把进程物理实体和支持进程运行的环境合称为进程上下文(process context)

进程在主存实际存放并非连续的

进程被创建时,OS会为其创建必要管理信息,这是一系列数据结构,用于记录进程的状态和特征,描述进程的运动变化过程。我们将这种管理信息称为进程控制块(Process Control Block

进程与PCB之间一一对应,OS通过PCB可以得知进程的所有信息

PCB含有:

标识信息

每个进程需要一个唯一的ID

现场信息

●PSW和其它寄存器值

用户堆栈指针

控制信息

进程的状态

时间片余量,已占用CPU时间

进程在辅存中的地址

数据段、代码段指针

队列指针

OS通过PCB来调度进程,调度时不是移动整个进程,只是通过移动PCB来实现。

把同一状态的所有进程的PCB链接在一起的数据结构称为进程队列(Process Queues),简称队列。

进程切换是让处于运行态的进程中断运行,让出处理器,这时要做一次进程上下文切换、即保存老进程状态而装入被保护了的新进程的状态,以便新进程运行。 

进程切换必定是在核心态下进行的

内核以下种情况允许发生进程切换

进程进入等待状态时(自愿放弃yield)

完成系统调用或中断处理,返回用户态但不是最具有资格获得CPU(抢占preempt),进入就绪状态

进程执行结束时,进入终止状态

进程切换步骤

保存被中断进程的处理器现场信息

修改被中断进程的进程控制块的有关信息,如进程状态等

把被中断进程的进程控制块加入有关队列

选择下一个占有处理器运行的进程

修改被选中进程的进程控制块的有关信息

根据被选中进程设置操作系统用到的地址转换和存储保护信息

根据被选中进程恢复处理器现场

模式切换

当中断发生时,暂时中断正在执行的用户进程,把进程从用户状态切换到内核状态,去执行操作系统例行程序以获得服务,这就是一次模式切换。

内核在被中断了的进程的上下文中对这个中断事件作处理(进程的上下文未发生切换),即使该中断可能不是此进程引起的。

模式切换步骤

保存被中断进程的处理器现场信息

根据中断号置程序计数器

把用户状态切换到内核状态,以便执行中断处理程序

进程切换与模式切换

模式切换不同于进程切换,它并不引起进程状态变化,也不一定引起进程的切换,在完成了中断调用之后,完全可以再通过一次逆向的模式切换来继续执行用户进程。

核心态运行的进程是不能被抢占的,但可以继续响应中断。

进程的控制

创建

创建一个PCB

赋予一个统一进程标识符

为进程映象分配空间

初始化进程控制块(状态为 New,优先级)

设置相应的链接(把新进程加到就绪队列的链表中)

阻塞和唤醒

撤销

回收进程占用的资源

撤销该进程的PCB并回收PCB空间

挂起和激活

 

2.4 线程及其实现

(1)线程概念

单线程进程和多线程进程

用户级线程和内核级线程

用户级线程 (ULT)

在内核之上,在用户模式下通过线程库来实现。线程库提供对线程的创建、调度和管理的支持而无需内核支持。

创建和管理执行速度较快,因为它不需要内核的支持,但是由于内核感知不到用户线程的存在,所以当一个用户线程阻塞时会引起整个进程的阻塞。

内核级线程 (KLT)

由操作系统在内核模式下执行线程的创建、调度和管理。

创建和管理执行速度较用户线程慢,但可以实现内核的并发线程。

(2)线程的实现

线程控制块TCB

每个线程都有一个TCB

执行状态: CPU寄存器, PC, 栈指针

调度信息:线程状态,时间片和CPU时间

指向PCB的指针

(3)线程的状态

线程的状态

线程状态有:运行、就绪和阻塞,线程的状态转换也类似于进程。

挂起状态对线程是没有意义的,如果进程挂起后被对换出主存,则它的所有线程因共享了进程的地址空间,也必须全部对换出去。

线程的状态转换与进程状态转换类似,OS对线程也是通过TCB管理调度的。

 并发多线程程序设计的优点

提高了响应速度:多线程交互式应用程序可以允许程序在它的一部分被阻塞或正在执行一个冗长的操作时持续运行,从而提高了了对用户的响应速度。

资源共享:缺省情况下,线程共享它们所属进程的存储器和资源。代码共享的优点在于它允许应用程序在同样的地址空间内拥有多个不同的活动线程。

经济实惠:为进程创建分配存储器和资源代价高昂。因为线程共享它们所属进程的资源,所以线程的创建和上下文转换更为划算。

提高了多处理机体系结构的利用率:在多处理机体系结构中,多线程的优点就更加显著了。在这种系统中,多个线程可以在不同的处理器上并行运行

 

2.5 处理器调度

(1)作业调度算法

处理器调度的工作

挑选谁获得CPU执行

如何在进程之间分配处理器时间

处理器调度级别

高级调度

中级调度

低级调度

高级调度(作业、长程调度)

在多道批处理操作系统中:

用户将要完成的任务提交给计算机系统,并保存在磁盘的一个批处理后备作业列表中。

高级调度将按照预定的调度策略决定把后备队列中的部分满足资源要求的作业调入内存,为它们创建进程,分配所需资源。并做好进程完成后的善后工作。

高级调度决定了多道程序的道数。

处理器调度与进程状态转换

中级调度(平衡负载、中程调度)

决定主存储器中所能容纳的进程数,这些进程将允许参与竞争处理器资源。

中级调度根据存储资源量和进程的当前状态来决定辅存和主存中进程的对换。

低级调度(进程、短程调度)

主要功能是按照某种原则决定就绪队列中的哪个进程能获得处理器并将处理机出让给它进行工作。

进程调度程序是操作系统最为核心的部分,进程调度策略的优劣直接影响到整个系统的性能。

有两类低级调度方式:

第一类称剥夺方式(Win98/2K/XP,Linux,Unix)

高优先级剥夺原则

时间片剥夺原则 

第二类称非剥夺方式(DOS , Windows 3.x)

调度算法性能的衡量

使响应时间最短:从提交到响应

●E.g. 用户敲下键盘后回显的速度

周转时间:从提交到完成

呑吐率:每个时钟单位处理的作业数

呑吐率与响应时间相关,如果响应时间过短可能导致切换开销增加,而呑吐率下降。

公平性:以合理的方式让各个进程共享CPU

批处理系统的调度性能指标

假设作业i提交给系统的时刻是ts,完成的时刻是tf,所需运行时间为 tk,那么:

作业与进程

作业是任务实体,进程是完成任务的执行实体;没有作业任务,进程无事可干,没有进程,作业任务没法完成。

作业概念更多地用在批处理操作系统,而进程则可以用在各种多道程序设计系统。

先来先服务(FCFS)

●First-Come, First-Served (FCFS)

早期系统里,FCFS意味着一个程序会一直运行到结束(尽管其中会出现等待I/O的情况)

如今,当一个程序阻塞时会让出CPU 

   例题:      Process        Time
                P1         28
                P2          9
                P3           3

如果三个进程的到达顺序是: P1 , P2 , P3  

等待时间分别是:  P1  = 0; P2  = 28; P3 = 37

平均等待时间是:  (0 + 28 + 37)/3 = 22

平均作业周转时间是: (28 + 37 + 40)/3 = 35

如果换一种执行顺序的话: P3 , P2 , P1

等待时间分别是: P1 = 12; P2 = 3; P3 = 0

平均等待时间是: (12 + 3 + 0)/3 = 5

平均周转时间是: (3 + 12 + 40)/3 = 18

第二种排列方式比第一种要好,平均周转时间缩短为18

●FCFS的优缺点

简单易行(+)

如果短作业处在长作业的后面将导致周围时间变长。

时间片轮转(Round Robin)

每个进程都可以得到相同的CPU时间(CPU时间片),当时间片到达,进程将被剥夺CPU并加入就绪队列的尾部 

●n个就绪队列中的进程和时间片Þ

每个进程获得  的CPU 时间,大约是q个时间单位 

没有进程等待时间会超过 (n-1)q

RR算法分析

时间片取选

时间片取值太小:呑吐率下降。

   进程切换开销显著增大。    

   (时间片不能小于进程切换的时间)

时间片取值较大:响应速度下降

时间片取值无穷大:退化成FCFS

一般时间片的选取范围为 10ms~100ms

上下文切换的时间大概为 0.1ms~1ms

大约有1%的时间开销用于上下文切换

●RR算法优缺点

公平算法(+)

对长作业带来额外的切换开销(-)

不同的时间片对RR算法的影响

最短作业优先(SJF)

以进入系统作业所要求的CPU时间长短为准,总是选取时间要求最短的投入运行。

        ●SJF是非剥夺算法

最短剩余时间优先(SRTF)

●SRTF(Shortest Remaining Time First)SJF的抢占算法。

如果一个新作业所需的CPU时间比当前正在执行的作业所剩余时间短,那么新作业将抢占CPU

例题:       到达系统时间     所需CPU时间

              P1        0        8

              P2        1        4

              P3        2        9

              P4        3        5

SRTF: T = (17+5+26+10)/4=14.5 (平均等时间6.5)

分析SRTF/SJF

●SRTF/SJF保证了短作业的优先执行,有很好的作业响应时间。如果每个作业的长度最相等的话,该算法将退化成FCFS算法。

●SRTF/SJF是降低作业响应时间最理想的方法,SRTF是抢占式的,而SJF是非抢占式的,通常我们讨论SRTF

饥饿现象

长时间作业可能无法获得CPU

预测技术

该算法需要事先知道作业所需的CPU时间

有些系统事先会询问作业所需时间,为了防止作弊,系统将kick那些占用时间较长的作业

预测一个作业的CPU时间并非易事

●SRTF优缺点:

优化了响应时间(+)

难以预测作业CPU时间(-)

不公平算法(-)

最高响应比优先(HRRF

优先数调度(Priority)

UNIX的动态优先数

多级反馈队列调度

彩票调度

第三章 并发进程

3.1 并发进程

并发进程间的关系

并发进程执行时分别在各自的变量集合上操作,则称他们是独立或无关的。

           进程 A        进程 B

           x = 1;        y = 2;    

           x = x + 1;    y = y – 2;

 

并发进程执行时共享某些变量,一个进程的执行结果可能会影响其它进程的执行结果,则称他们是交互或相关的。

           进程 A        进程 B

           x = 1;        y = 2;    

           x = y + 1;    y = y * 2;

两个交互进程可能会产生的问题1

Ti是订票终端,x=2代表剩余票数,为所有终端共享

       T1 :                    T2:

       …                    …

       T=x; ①                T=x; ②

   if T>=1 then ③      if T>=1 then ④

        x =T-1; ⑤          x =T-1; ⑥

        …                    …

剩余票数最终结果不确定!可能是1,也可能是0.

两个交互进程可能会产生的问题2

//B表示申请和归还主存的容量,x表示空闲主存容量

procedure borrow (int B)

{

   if B > x then ① 

 {申请进程进入等待队列等主存资源} ⑤

     x = x - B;

 {修改主存分配表,申请进程获得主存资源}

}

procedure return (int B)

{

   x = x + B; ②

 {修改主存分配表} ③

 {释放等主存资源的进程} ④

}

可能会导致申请进程永远等待(?)

交互的并发进程与时间有关的错误

错误的两种表现形式:

结果不惟一(飞机订票)

永远等待(内存管理)

 

无关的并发进程不会产生此类错误,如何判断并发的进程是无关的?Bernstein条件:

●R(pi)={a1,a2,…an},程序pi在执行期间引用的变量集

●W(pi)={b1,b2,…bm},程序pi在执行期间改变的变量集

若两个程序的变量集交集之和为空集:        R(p1)∩W(p2)∪R(p2)∩W(p1)∪W(p1)∩W(p2)={   }

则并发进程的执行与时间无关。

例题 (p211)

   有如下四条语句:

  S1:  a := x + y    S2:  b := z + 1

  S3:  c := a – b    S4:  w := c + 1

 于是有:

 R(S1)={x,y}

R(S2)={z }

R(S3)={a,b}

R(S4)={c}

W(S1)={a}

W(S2)={b}

W(S3)={c}

W(S4)={w}  

●S1S2可并发执行,满足Bernstein条件。其他语句并发执行可能会产生与时间有关的错误

进程间的交互

竞争

多个进程访问同一共享资源时发生

可能出现两个问题:死锁和饥饿

解决竞争关系的手段 进程的互斥

协作

多个进程为完成同一任务需要分工协作

处理协作关系的手段 进程的同步

互斥是一特殊的同步,即逐次使用互斥资源

 

3.2 临界区管理

临界区(互斥区)

由于相关的并发进程共享变量会引起与时间相关的错误,故引入一种机制来解决这种错误。

互斥:确保一个时刻仅有一个进程在访问共享资源。

共享变量代表的资源叫临界资源

在并发进程中涉及到临界资源的程序段叫临界区

一个时刻仅能让一个进程进入临界区

临界区(critical section)与互斥(mutual exclusion)是同一事物的两种描述方式

使用临界区的原则:

实现互斥机制的基本手段

(lock):阻止某进程做某事

在进入临界区前应该上锁

在离开临界区时要开锁

进程见到锁必须等待直到锁被打开

所有的同步互斥都引入了进程的等待!

使用锁是为了实现互斥,必须满足临界区的使用原则,因此对锁的使用必须谨慎。

Peterson算法

int turn = 1 or 2;     /* 令牌归谁所有 */
BOOL inside1 = false;    /* P1不想进其临界区 */
BOOL inside2 = false;    /* P2不想进其临界区 */
Process P1
{
   inside1 = true;
   turn = 2;
   while (inside2 && 
       turn == 2); 
     临界区;
   inside1 = false;
}
Process P2
{
   inside2 = true;
   turn = 1;
   while (inside1 && 
       turn == 1);
     临界区;
   inside2 = false;

原语(原子操作)

原语是操作系统内核中执行时不可中断的过程,即原子操作。

许多系统提供了特殊的硬件指令,指令执行过程是原子地。

用硬件指令来管理临界区

测试并建立指令(TestAndSet)

对换指令(Swap) (自学)

TS指令(TestAndSet)

●BOOL TS(BOOL &s)        

{        
   BOOL result = s;
   s = false;
   return result;
} 

●TS指令管理临界区时,可把一个临界区与一个布尔变量s相连,strue表示临界区空闲,可把它看成一把锁。进程进入临界区前要保证TS(s)的返回值为true

TS指令实现临界区管理

●算法描述如下

BOOL s = true; // free

       LockAcquire() {
       while ( !TS(s) ); //loop if busy 
   }

临界区;

       LockRelease() {
       s = true;
   }

       

算法解释:

如果临界区空闲则s=trueTS返回true并将s设为false,此时临界区为设为占用状态,同时跳出while循环进入临界区。

如果临界区占用则s=falseTS返回false并且s值不变,所以此时循环将继续。

存在忙式等待!

用TS指令实现一个减少忙等的算法

将上锁和解锁代码看成另一个临界区,所以用一把新锁guard保证这个临界区代码不会被打断执行!(即两个函数都变成了原子操作)

框线代码必须一并完成,否则有何后果?

BOOL guard = true;
int lock = FREE;
LockAcquire() {
   // 短暂的忙等
   while ( !TS(guard) );
   if (lock == BUSY) {
     sleep() && guard = true;
   }
   else {
       lock = BUSY;
       guard = true;
   }
}
LockRelease() {
   // 短暂的忙等
   while ( !TS(guard) );
   if (有等待进程) {
      wakeup(); //释放一个到就绪队列
   } 
   else {        
       lock = FREE;
   }
   guard = true;
}

我们已经在TS指令的基础上实现了锁及其操作原语,解决了互斥问题。

(互斥)是一种特殊的同步,广泛意义上的同步是指并发进程可以按照某种规则共享多个资源。

●e.g.如果系统有两台打印机,就可以同时让两个进程同时享用。此时锁的局限性就体现出来了 » 信号量出现了。

3.3 信号量与PV操作

信号量

信号量是一种广义上的锁

●1965年,由荷兰学者Dijkstra提出

信号量是一个非负的整数,并有两个操作原语:

●P():如果信号量的值为负数则等待,否则将信号量值减1

●V():将信号量的值加1,如果有进程在等待则唤醒它

在荷兰语中,P表示 “proberen” (测试, V表示“verhogen”(增加)。常用的符号还有:waitsignal等。

信号量用于制约控制并发进程的执行速度,是一种卓有成效的进程同步(互斥)机制!

整型信号量

P(s) 
{
   while (s <= 0);
   s = s – 1;
}
V(s)
{
   s = s + 1;
}

●s是一个整型数值的信号量,代表资源的个数。

TS指令的区别

●TS指令的执行效果与s等于1时相同

当资源不足时,P(s)将陷入与TS指令一样的忙式等待

这个我们写的代码有什么两样?

这是OS内核提供的原子操作,内部实现复杂。

记录型信号量

记录型信号量s是一个如下内容的结构体变量

struct semaphore {
       int value;
       PCB *queue;  
}s;
P(s) {
   s.value = s.value - 1 ;
     if (s.value < 0) {
       ProcessState = “waiting”;
     sleep();          
     }
}
V(s) {
  s.value = s.value + 1;
  if (s.value < = 0) {
     wakeup();
       ProcessState = “ready”;
  }
}

信号量分类

按用途分为:

公共信号量(允许所有进程对信号量进行P操作,通常用于互斥)

私有信号量(仅允许信号量拥有进程才能执行P操作,通常用于同步)

按信号量的取值分为:

一般信号量(整型、记录型)

二元信号量(只能取值为01)

用PV操作解决进程间互斥问题

semaphore mutex = 1;
 Process Pi {
   P(mutex);
 临界区;
   V(mutex); 
}

 

互斥信号量属于公有信号量,通常互斥信号量的初值为1,表示临界区同时仅能让一个进程进入。

int A[m];
semaphore mutex = 1; 
Process Pi { 
   int Xi;
   //按旅客定票要求找到A[j]
   P(mutex);
   Xi = A[j];
   if Xi >= 1 then { 
      Xi = Xi - 1; 
         A[j] = Xi; 
      V(mutex);
       //输出一张票;
   }else{
       V(mutex);
       //输出票已售完;
   }
}

/*解决单类机票问题*/

信号量初值不为1时的情况

铁轨是资源,用一个初值为2的信号量来表示空闲铁轨数为2,可供两列火车同时通过。

信号量的数值表示了可用资源的个数, P操作代表请求一个资源,V操作代表释放一个资源。

/*火车控制问题*/

semaphore road = 2;

Process Pi {

   P(road);

   //通过交叉路口;

   V(road);

}

信号量的一些使用规则

必须置一次且只能置一次初值

初值不能为负数

只能用于执行PV操作

s > 0,则s代表还可以使用的资源数

s < 0,则s表示等待队列里的进程数

●P操作代表请求一个资源

●V操作代表释放一个资源

哲学家吃通心面问题

问题描述

   5个哲学家围在桌旁,每人面前有一盘子,每两人之间放一把叉子。每个哲学家思考、饥饿,然后想吃通心面。只有拿到两把叉子的人才可以吃面,并且每人只能直接拿自己左手或右手边的叉子。

semaphore fork[5];

fork[i] = 1;

 

process Pi { //i=0,1,2,3,4

   思考;

   P(fork[i]);             

   P(fork[(i+1)mod 5]);          

   吃通心面;

   V(fork[i]);

   V(fork[(i+1)mod 5]);

}

 哲学家吃通心面可能产生死锁

上述解法可能出现永远等待

当五个人同时都取到左手的叉子时发生

 

有若种办法可避免死锁:

至多允许四个哲学家同时吃;

奇数号先取左手边的叉子,偶数号先取右手边的叉子;

每个哲学家取到手边的两把叉子才吃,否则一把叉子也不取。

同步问题

同步问题实质上是让那些随机发生的事件变得有序。

实现的方法是调节并发进程的执行速度,让某些进程必须等待另一些进程的执行而得以继续。

一个简单的同步问题:公共汽车上司机和售票员的活动分别如下:

司机:启动车辆;正常行车;到站停车

售票员:关车门;售票;开车门

同步要点:司机要等门关闭才能开车,售票员要等停车才能开门

用PV操作解决进程间同步问题

使用私有信号量解决同步问题

某进程在等待点上等待一个信号让它继续执行,这个信号量即为该进程的私有信号量,仅能由本进程进行P操作。

为了让进程等待,私有信号量的初值一般为0.

公共汽车问题应该有2个私有信号量,分别指示司机和售票员的行动。semaphore s1=0,s2=0.

P司机{

   P(s1);

   行车;

   V(s2);

}

P售票员 {

   V(s1);

   售票;

   P(s2);

}

生产者-消费者问题(经典同步问题)

生产者(P)与消费者(C)共用一个缓冲区,P进程不能往的缓冲区中放产品,C进程不能从的缓冲区中取产品。

因为共享了缓冲区,故并发的PC进程有可能产生与时间相关的错误,因而要采取措施使两进程执行同步。

分别为PC进程设置两个信号量(私有还有公有?初值是多少?),用于指示进程是否可以执行。假设缓冲区只能存放一件产品,初始状态为空。

生产者-消费者问题的解法(1-1-1)

Semaphore empty = 1;        //生产者的信号量
Semaphore full = 0;        //消费者的信号量
P {                         
 while (true) 
 {
   生产一个产品;    P(empty);    
       送产品到缓冲区;
   V(full);
 }    
}
C {
 while (true) 
 {
   P(full);
   从缓冲区取产品;
   V(empty);
   消费产品;
 }

信号量及PV操作使用注意事项

●PV操作必须成对出现,有一个P操作就一定有一个V操作。

当为互斥操作时,它们同处于同一进程

当为同步操作时,则不在同一进程中出现

如果P(full)P(empty)P(mutex)两个操作在一起,那么P操作的顺序至关重要,一个同步P操作与一个互斥P操作在一起时同步P操作在互斥P操作前,而两个V操作无关紧要

苹果桔子问题

问题描述

桌上有一只盘子,每次只能放入一只水果

爸爸专向盘子中放苹果(apple),妈妈专向盘子中放桔于(orange)

一个儿子专等吃盘子中的桔子,一个女儿专等吃盘子里的苹果 

问题分析

●盘子→缓冲区;爸妈→生产者;儿女→消费者

●这是多个生产/消费者,一个缓冲区,多种产品的问题

●设置三个信号量

●sp用于指示盘子能放几个水果,初值=?

sg1,sg2分别用于指示桔子和苹果的个数,初值=?

苹果桔子问题的解法

读者学者问题:

有两组并发进程:读者和写者,共享一个文件,要求:

允许多个读者同时执行读操作

任一写者在完成写操作之前不允许其它读者或写者工作

写者执行写操作前,应让已有的写者和读者全部退出

简单一句话:某一时刻,允许存在多个读者,但仅能有一个写者。

一个写者到达时

如果发现有读者正在读,则等待;否则开始写

一个读者到达时

如果有写者正在写,则等待;否则开始读

读者和写者会发生竞争问题,读者和读者可以共存。所以先设置一个信号量W解决读者和写者之间的竞争。

如何判断是否有读者以及读者离开时是最后一个?

设置一个int变量rc记录当前正在读文件的读者数量。

初值应该各设成多少?

●semaphore W = ?;

●int rc = ?;

理发师问题

理发店理有一位理发师、

   一把理发椅和n把供等候

   理发的顾客坐的椅子

如果没有顾客,理发师便

   在理发椅上睡觉

一个顾客到来时,它必须叫醒理发师

如果理发师正在理发时又有顾客来到,则如果有空椅子可坐,就坐下来等待,否则就离开

理发师问题解法

int waiting = 0;        /*等候理发的顾客数*/
int CHAIRS = n;    /*为顾客准备的椅子数*/
semaphore customers,barbers,mutex;
customers = 0; barbers = 1; mutex = 1;

barber() {
 while(TRUE){
   P(customers);
   
   P(mutex);
   waiting = waiting – 1;
   V(mutex);
 
   cut_hairs();
   V(barbers);
 
 }
}
customer() {
  P(mutex);
  if waiting < CHAIRS{
 
     waiting = waiting + 1;
   V(mutex);
     P(barbers);
     V(customers);
   get_haircut();
  
  }else{
   V(mutex);    
  }
  leave();
}

把学生和监考老师都看作进程,学生有N人,教师1人。考场门口每次只能进出一个人,进考场原则是先来先进。当N个学生都进入考场后,教师才能发卷子,学生交卷后可以离开考场,教师要等收上来全部卷子并封装卷子后才能离开考场。全部流程如下图所示,使用给定的信号量与变量,试用PV操作解决上述问题中的同步和互斥关系。

Semaphore    entrymutex = 1 ;        //入场/互斥
Semaphore    waitforstudent = 0 ;    //等待学生全部入场
Semaphore    d_paper = 0 ;        //发卷信号量
Semaphore    g_paper = 0 ;        //收卷信号量
int        studentCount = 0
Process teacher {
P(entrymutex);
   进考场
V(entrymutex);
 
P(waitforstudent);
 
for (i=1;i<=N;i++) //发卷
     V(d_paper);
   监考
P(g_paper); //等待学生交齐试卷
   收/封试卷
 
P(entrymutex);
   离场
V(entrymutex);
}
Process student_i {
//进考场
P(entrymutex);
studentCount++;
if (studentCount == N) then
      V(waitforstudent);
V(entrymutex);
P(d_paper); //等待发卷
考试
//交卷离场
P(entrymutex);
studentCount--;
if (studentCount == 0) then
      V(g_paper);
V(entrymutex);
}
 

3.5 进程通信

进程间通信机制的方式

低级通信机制

信号(signal)通信机制

信号量及其原语操作(PV、锁、管程)控制的共享存储区(shared memory)通信机制

高级通信机制

管道提供的共享文件(shared file)通信机制

消息传递(message passing)通信机制

●socket通信机制

信号(signal)通信机制

也称软中断(p84),通过发送一个指定的信号来通知进程某个异常事件发生,并进行适当处理。

与中断类似,信号被进程接收到后也有信号处理程序,处理完毕可返回原来断点继续执行。

与硬中断的区别

软中断运行在用户态,反应延时较长

硬中断运行在核心态,响应比较及时

信号可以从内核发出,也可以由一个进程发出。UNIX的系统信号多达十几种,可以使用kill –l查看系统中信号的种类,该命令也用于发送信号。

kill –s sig_name pid

Linux的信号处理函数

●sigset_t sigmask; //信号掩码集

   //信号集合处理函数

●int sigaddset(sigset_t* set,int signo);

●int sigdelset(sigset_t* set,int signo);

●int sigemptyset(sigset_t* set); 

   //修改进程的信号掩码

●int sigprocmask(int how, //SIG_BLOCK/SIG_UNBLOCK…

                  const sigset_t* set,

                  sigset_t* oset);

   //捕捉信号

●int sigaction(int sig, 

                    const struct sigaction* act,

                    struct sigaction* oact);

struct sigaction{

void (*sa_handler) (int);      //函数指针

sigset_t sa_mask;        //处理信号时需要阻塞的信号

int sa_flags;            //特殊标志和选项

void (*sa_sigaction) (int,siginfo_t*,void*);

};

实现捕捉异步信号并自己处理

void catchsigfunc(int signo){    };

 

int main(){

   struct sigaction act;    

   act.sa_handler = catchsigfunc;

   act.sa_flags = 0;

   sigemptyset(&act.sa_mask); 

   sigaction(SIGINT,&act,NULL);

}

共享文件(shared file)通信机制

也称管道,它是连接两个进程的一个特殊的文件,它基于文件系统机制实现,共有两个端口:读和写。

一次只能一个进程读/写管道

双方必须知道对方的存在(仅能用于父子进程)

双方收发信息要实现同步

读进程先读到的是先写入管道的内容(先进先出)

命名管道(FIFO)用于更普遍的情况

Linux的管道相关函数

●int pipe(int fildes[2]);

●int main(){

int fd[2];

pipe(fd);//创建一个无名管道

}

●ssize_t write(int fildes,

               const void* buf,

               size_t nbyte);    //向一个文件写

●ssize_t read(int fildes,

               void* buf,

               size_t nbyte);    //从一个文件读

共享存储区(sm)通信机制

相互通信的进程间设有公共内存,一组进程向该公共内存中写,另一组进程从公共内存中读,通过这种方式实现两组进程间的信息交换。

●Linux下的实现函数有:

●int shmget(key,size_t size,int flags);

●void *shmat(int shmid,

               const void* shmaddr,

               int flags);//将返回一个共享内存地址

●int shmdt(const void* shmaddr);//断开与共享内存的链接

●int shmctl(int shmid,int cmd,struct shmid_ds* buf);

使用ipcs –m可以查看当前系统的shm信息

消息传递(mp)通信机制

一个进程将一组数据作为一个消息发送给另个进程

原来的生产消费者问题,除了使用信号量,还加了一个有界缓冲实现的。现在生产者可以将产品直接发送给消费者,发送和接收原语已经实现了同步,大大简化了编程过程。故称这种通信机制为高级通信机制。

消息传递方式

直接通信 ― 信息直接传递给接收方

间接通信 ― 借助于收发双方进程之外的共享数据结构作为通信中转,如消息队列。该共享的内存称为信箱。

Linux的消息队列函数

●int msgget(key_t key,int msgflg);

●int msgsnd(int msqid,            //消息队列ID

           struct msgbuf* msgp,        //用户缓冲

           size_t msgsz,

           int msgflg);

●int msgrcv(int msqid,

           struct msgbuf* msgp,

           size_t msgsz,

           long msgtyp,int msgflg); 

●int msgctl(int msqid,int cmd,struct msqid_ds* buf);

●struct msgbuf{

   long mtype;    //消息类型

   char mtext[1];    //消息文本

};

发送的消息大小 = sizeof(msgbuf)+strlen(mymessage)

3.6 死锁

(1)死锁的基本概念:

饥饿与死锁:

饥饿:进程长时间的等待 e.g.低优先级进程总是等待高优先级所占有的进程

死锁:循环等待资源 A和B分别占有打印机和扫描仪 同时分别申请扫描仪和打印机

死锁 -> 饥饿 (反之不亦然)

           饥饿可能终止;如果无外部干涉,死锁无法终止

 

死锁的产生条件

PV操作使用不当产生的死锁、同类资源分配不当引起的死锁。

死锁与进程的推进速度、资源的数量以及资源分配策略有关

典型的死锁问题:

             交通问题:车辆同时右转发生死锁

             哲学家吃通心面问题:哲学家同时拿起左手的叉子发生死锁

产生死锁的四个必要条件:

互斥使用:一个时刻,一个资源仅能被一个进程占有

不可剥夺:除了资源占有进程主动释放资源,其它进程都不可抢夺其资源

占有和保持:一个进程请求资源得不到满足等待时,不释放已占有资源

循环等待(上面三个条件同时存在产生的结果):每一个进程分别等待它前一个进程所占有的资源

死锁的解决方案:

死锁的防止 (Prevention):破坏四个必要条件之一

死锁的避免 (Avoidance):允许四个必要条件同时存在,在并发进程中做出妥善安排避免死锁的发生

银行家算法:

问题描述:银行家(OS)拥有一笔周转资金。 客户(进程)要求分期贷款(资源), 如果客户能够得到各期贷款,就一定能够归还贷款,否则就一定不能归还贷款。 银行家应谨慎的贷款,防止出现坏帐。

对每个请求进行检查,是否会导致不安全状态。若是,则不批准该请求。 检查状态是否安全的方法是,看他是否有足够的资源满足一个距最大需求最近的客户,如此反复下去。如果所有投资最终都被收回,则该状态是安全的,最初的请求可以批准。

如果系统中共有五个进程和A、B、C三类资源 A类资源共有10个,B类资源共有5个,C类资源共有7个。T0时刻系统资源分配情况如下:

每个进程目前还需资源为Cki-Aki:

序列{}能满足安全性条件:

currentAvail

Cki - Aki

Allocation

cur + Alloc

Possible

A

B

C

A

B

C

A

B

C

A

B

C

P1

3

3

2

1

2

2

2

0

0

5

3

2

TRUE

P3

5

3

2

0

1

1

2

1

1

7

4

3

TRUE

P4

7

4

3

4

3

1

0

0

2

7

4

5

TRUE

P2 

7

4

5

6

0

0

3

0

2

10

4

7

TRUE

P0

10

4

7

7

4

3

0

1

0

10

5

7

TRUE

优点:不需要加上种种限制条件

缺乏实用价值:进程运行前就要求知道其所需资源的最大数量;要求进程是无关的,若考虑同步情况,可能会打乱安全序列;要求进入系统的进程个数和资源数固定

死锁的检测和恢复 (Detection & Recovery):允许死锁的发生,系统及时地检测死锁并解除它

允许死锁发生,操作系统不断监视系统进展情况,判断死锁是否发生

一旦死锁发生则采取专门的措施,解除死锁并以最小的代价恢复操作系统运行

死锁检测的时机

当进程等待时检测死锁(系统开销大)

定时检测

系统资源利用率下降时检测死锁

资源分配图表示法:

资源类(资源的不同类型) 用方框表示

资源实例(存在于每个资源中) 用方框中的黑圆点表示

进程 用圆圈中加进程名表示

申请边 进程指向资源类的一条有向边, P -> R

分配边 资源实例指向进程的一条有向边,R ->P

进程―资源分配图检测死锁方法:

如果能在“进程―资源分配图”中消去某进程的所有请求边和分配边,则称该进程为孤立结点。经一系列简化,使所有进程都成为孤立结点,则该图是可完全简化的;否则则称该图是不可完全简化的。

系统为死锁状态的充分条件是:当且仅当该状态的“进程―资源分配图”是不可完全简化的。该充分条件称为死锁定理。

第四章  存储管理

4.1 存贮器工作原理

存储器使用的地址叫物理地址(绝对地址),其空间是由存储器地址总线扫描出来的空间,其大小取决于实际安装的主存容量。

目标程序地址叫逻辑地址(相对地址),被限制从0开始编址,一个用户程序的逻辑地址集合称为该程序的逻辑地址空间。

地址转换(重定位、地址映射):将程序的逻辑地址转成物理地址的过程,分为静态转换和动态转换。

静态转换:当用户程序被装入内存时,一次性实现逻辑地址到物理地址的转换,以后不再转换。一般在装入内存时由软件完成。若编译时完成则需要程序员事先知道要装载的地址。

动态转换:在程序执行过程中要访问数据时再进行地址变换,需要额外的硬件来记录程序装载的内存基址。

存储保护

使在内存中的各道进程,只能访问它自己的区域,避免进程内存空间重叠,特别是不能访问OS的内存空间。

通常需要两个硬件支持(不需要转换地址): 基址寄存器(Base Register):存放装载的起始地址;限长寄存器(Limit Register):存放的最大逻辑地址

4.2 连续存储管理

所谓连续,是指进程在主存里占用一块连续的空间。

如果系统事先把内存用户区划分为若干分区,分区大小可以相等,也可以不等,一个进程占据一个分区。我们称其为固定分区存储管理。

若内存不预先划分好,而是等进程装入时,根据进程的需求和内存空间的使用情况来决定是否分配。我们称其为可变分区存储管理。

存储分配:系统维护一张主存分配表,里面记载了内存的分区划分和使用状态。分配主存时总选择那些分区占用标志为0且长度小于等于进程所需空间的分区块。回收只要相应分区占用位置0即可。

固定分区的地址转换和存储保护:

静态转换:装入时检查绝对地址是否落在分区内

动态转换:执行时检查,如下图所示。

可变分区存储管理:

存储分配: 系统维护两张表,一张记录尚未分配的内存状态,另一张记录已分配的情况。

                    初始状态已分配表为空,未分配表仅有一条记录,整个用户区就是空闲区。系统总是从空闲中选择一个足够容纳进程的分区分配,并将分配情况记在已分配表中,剩余的分区空间记录进未分配表。

回收:将已分配表的标志改为“空”,再将其加入未分配表中。
 

可变分区分配算法:

首次适应:分配第一个足够大的分区,可以从头开始查找,也可以从上次分配结束的地点开始查找

最优适应:查找整个分区表,分配最小的足够大的分区 总是产生最小剩余分区,不浪费一个更大的空间,但会导致剩余分区太小难被再利用

最坏适应:查找整个分区表,分配最大的足够大的分区 总是产生最大剩余分区,它可能比最优适应产生的剩余分区更容易利用

紧凑技术:

“碎片”问题:经过一段时间的分配回收后,内存中存在很多很小的空闲块。它们每一个都很小,不足以满足分配要求;但其总和满足分配要求。这些空闲块被称为碎片,造成存储资源的浪费。

解决方法(紧凑):通过在内存移动程序,将所有小的空闲区域合并为大的空闲区域。 使用静态地址转换的不可以使用紧凑。 如果采用紧凑技术前要评估开销。

4.3 分段式存储管理

段式存储管理的地址转换和保护:

每个程序段都有一个段号,段号从0开始,每一段也从0开始编址,段内地址是连续的,逻辑地址包括段号和段内位移。

通过基址和限长寄存器的值可以找到段在内存的位置,一个进程所有段的这些寄存器值的列表被称作“段表”。段表可设置标志位决定该段的读写性。

物理地址 = 逻辑地址中段号对应的段基址 + 段内位移

4.4 分页式存储管理

将内存划分成固定大小的块(页框),逻辑地址也划分成相同大小的区(页面),于是也实现了将进程分散存储的目的。

分配过程就是将一个页面装载入页框,为了实现分配和回收,一般采用一个向量表记录内存每一块的使用情况,1表示被分配,0表示空闲。         

                00110001110001101 … 110010

进程总是在装载最后一个页面时可能产生碎片,所以碎片大小不会超过一个页框,它属于内部碎片。

X86/Pentium的页框大小为4KB。

逻辑地址的页面号从0开始,每个页面的页内位移范围为0至4K-1 (对x86CPU 32位地址而言)。

每个进程都要记录每个逻辑页面所对应的物理块号,我们称这些值列表为“页表”,同时设置一些标志位用于访问权限:读、写、读/写、有效和无效。

物理地址 = 页框地址(页框号x页框长) + 页内位移 存储保护:检查页号是否越界(为什么不检查页内位移的越界了?),同时检查存取权限。

分段

分页

信息的逻辑单位

信息的物理单位

段长是任意的

页长由系统确定

段的起始地址可以从主存任一地址开始

页框起始地址只能以页框大小的整数倍开始

(段号,段内位移)构成了二维地址空间

(页号,页内位移)构成了一维地址空间

会产生外部碎片

消除了外部碎片,但会出现内部碎片

两级页表:

将页表占用的那部分连续内存再分页,实现了页表的分散存储,为了找到页号,先要把该页号所在的页表页找到。于是形成了两级页表,第一级为页目录表(该部分连续),第二级为页表。

一个采用二级页表机制的地址格式如下:

分段+分页:

段页式存储管理:将进程逻辑地址空间按逻辑段划分,同时对每一段进行分页处理。物理内存仍然按照页框大小划分。

地址转换机构MMU

MMU (Memory Management Unit):完成逻辑地址到物理地址转换功能的低层硬件设施。

主要功能有(以页式存储管理分例):管理页表基址寄存器、分解逻辑地址、访问页表、如果页表不在内存当中或出现越界现象时,MMU发出缺页中断或越界中断、管理快表TLB。

缓冲技术:

缓存:比访问原始数据速度更快的数据副本存储器。

缓存解决的是低速和高速设备之间的瓶颈问题,是当代计算机领域提高计算机速度的基本手段。

相联存储器TLB:TLB (Translation Look-aside Buffer):一种比主存访问速度快,但容量较小的高速缓冲区。利用它解决CPU运算速度快和主存访问速度相对慢之间的瓶颈问题。

MMU对TLB的管理:

如果访问TLB中的快表未命中,则MMU会访问主存中的页表:如果主存页表有效则将条记入TLB;如果主存页表失效则需要告诉OS缺页,请求帮助调页入主存。

4.5 虚拟存储

请求分页式虚拟存储管理

仅装入立即使用的那些页面,执行过程中访问不在主存的页面时,则产生一个缺页中断,请求系统将其从辅存调入主存,主存空间不足时则将某些页面对换到辅存之上。

每次对换的单位是一个页面

逻辑地址如何向物理地址转换同分页存储管理机制一样

查找页面时先访问第一级存储:TLB

需要对换页面时,根据不同的页面置换算法 (如 FIFO,LRU…)

如果缓存未命中就到下一级存储去找(内存、磁盘)

如果缓存中的数据被修改了,则称这些数据为脏数据,使用回写技术

请页式虚存管理的页表:

驻留标志:指示对应页是否已装入主存

辅存地址:记录页面在辅存中的位置

其它标志

       修改位(M):当一个页被修改后会置该位。该位被置也就意味着该页被调出主存时必须回写到辅存上。该位也称脏数据位。

       引用位(R):该页被引用时设置,用于页面置换算法。

       访问权限位(A):限定了该页的访问权限(R,W,R/W)

页面置换算法:

(1)FIFO(先进先出) 淘汰最早进入内存的页,也就是一个在内存中呆着最久的页 公平算法,保证每个页面占用内存时间相等,但是如果用不常用的页替换掉经常使用的页,情况将变得相当糟糕 。

(2)OPT(最优置换) 淘汰一个以后不再访问的页,或是距现在最长时间后再访问的页 该算法的确是最优的,但是我们无法预测未来。 

(3)RANDOM(随机置换) 每次都随机挑选一个页面淘汰,简单但是效率低下。

(4)LRU(最近最少使用) 淘汰最近一段时间里最久未被访问的那一页 该算法依据局部性原理,在最近很少被访问的页面,在未来可能被引用的机率也不大

(5)2nd Chance/Clock 给访问过的页面一次留在内存中的机会,需要和引用位结合使用

(6)Clock+ 利用引用位和修改位把页面划分成四种类别,先淘汰既老也无修改的页面,再淘汰老页面,同时将扫描过的页面引用位清零,至此就一定能找到一个符合条件页面淘汰。

页面置换算法的评估指标为缺页中断率f:如果进程需要n页,内存页框有m块(1≤m≤n),如果进程执行的过程中成功访问页面的次数为S,不成功的次数为F,则总的访问次数为A = S + F ,则f = F / A。可知好的算法带来最低的缺页中断率。

页框分配策略:

OS如何给多个并发进程分配页框?

固定分配:进程生命周期中,页框数保持不变

可变分配:根据进程的执行情况动态更改其页框数

固定分配缺乏灵活性,但可变分配增加了OS的开销

页面置换观点:

局部:与固定分配结合使用,发生缺页中断从进程的页框中选择一个淘汰。

全局:与可变分配结合使用,OS保留若干空闲页框,缺页时从系统空闲页框中分配一个给进程。若无空闲页框,则从内存中选择一个淘汰(可能是其它进程的页面) 通常使用可变分配+局部置换策略。

 

页面装入策略:

请页式 通过缺页中断的方式,请求把所需页面调入内存。 优点:确保只有被访问的页面调入内存 缺点:进程执行的开始阶段缺页中断次数过多,另外每次仅调一页增加了访问磁盘的IO次数

预调式 由OS预测进程可能要访问的页面,并在使用之前先调入内存。 优点:一般是调入多个相连续的页面,节省了寻道时间,减少了磁盘IO次数 缺点:如果预测成功率不高,则执行效率会下降。 该方法一般用于进程执行的开始

 

页面清除策略

请页式 当一页要被淘汰且被修改过(脏页),才把它回写辅存 不足:写出一页在读入一页之前进行,每次这种缺页都引起了两次IO操作,降低了CPU使用效率

预约式 对所有更改过的页面,在淘汰他们之前批量回写辅存 不足:若刚回写一批页面,但在淘汰前又被修改了,那么此前的回写等于是白写了

页缓冲技术 淘汰的页面先不回置换回辅存,而是存在一个缓冲当中并分成两个队列:修改页面和非修改页面队列 OS不时地把脏页回写并加入非修改页面队列 非修改页面队列中的页面被再次引用时回收,或是被淘汰

 

第五章  设备管理

5.1 I/O硬件原理

端口 设备与计算机的连接点,如串行端口、并行端口

总线 一组线和一组定义在线上传输数据的协议

控制器 用于控制/操作端口、总线或设备的一组电子器件。 有些控制器较为简单,可以集成在主板上,如串口控制器。 有些控制器实现功能复杂,需要做成一块独立的集成电路板并与计算机相连,通常称为适配器。如图形适配器、SCSI适配器。 有的设备内置了适配器,如硬盘控制器板就在硬盘的一侧。控制器有一个或多个用于数据和控制信号的寄存器。CPU通过读写这些寄存器来控制通信。

控制寄存器:可以被主机发布命令或改变设备状态

状态寄存器:包含一些主机可读的位信息

数据寄存器:记录主机可读或写入的数据

IO地址:给控制器寄存器的编址。一个设备控制器全部寄存器的地址范围也就组成了IO地址空间(范围)。编址方式有:IO独立编址:使用I/O专用指令访问,如IN、OUT;内存映像编址:将寄存器值映射到内存空间里,可以使用一般的内存访问指令即可访问

IO控制方式:

CPU一般不与设备直接打交道,而是将命令发送给控制器,根据操作方式可分成以下四类 :询问方式(Polling)、中断方式(Interrupt)、DMA方式(Direct Memory Access)和通道方式(Channel)。 主要差别在于:CPU和设备并行工作的方式和程度不同,和CPU与何种设备通信也有关系。

询问方式又称程序直接控制方式,CPU和设备控制器之间有点类似于生产和消费者,需要两个寄存器位来协调二者的关系。 状态寄存器中的“忙”位 控制寄存器中的“命令就绪”位 协调过程如下: CPU不断测试“忙”位,直至该位被清除(置0) CPU置控制寄存器的“写”位,并向数据寄存器写入一个字节 CPU置“命令就绪”位为1 控制器发现“命令就绪”,且是“写”命令,于是先置“忙”位为1,再从数据寄存器中读取一个字节并向设备执行“写”的IO操作 控制器最后清除“命令就绪”位以及“忙”位

询问方式只要三种CPU指令就足够了:读设备寄存器、逻辑与、根据是否为0进行跳转。所以基于询问方式的操作效率还是很高的,但这是基于设备总是很快就绪的情况下作出的结论。 CPU在第1步处于忙等状态,CPU不断读取状态寄存器直到“忙”位被清除。如果设备长时间不能就绪,那这段CPU时间就白白浪费了,如何利用起来? 可以让CPU在这段时间为其它进程服务,那设备准备好了如何通知CPU呢? 中断!

中断向量包含了特殊的中断处理程序的入口地址,通常这个和中断号、IO地址范围保存在一张表内。 中断号(IRQ)用于识别中断种类,每一个中断号(IRQ)都映像到一个中断向量上。 中断方式实现了一定程序上的并行操作,但是CPU还是全程参与数据传输操作,是否能直接让主存与设备交换数据而不占用CPU呢? DMA方式

DMA工作流程:1.CPU向磁盘控制器发出命令传递C个字节的磁盘数据到地址为X的内存区;2.磁盘控制器初始化DMA传输,向DMA控制器发送每个字节;3.DMA控制向X地址内存传输字节,增加内存地址并减少C的值;4.C=0时,DMA发出中断,通过CPU传输完毕。

DMA可以在主存和设备(一般为块设备)之间一次传送多个字节,传输过程无需CPU的参与,提高了CPU的使用率。 DMA需要哪些部件 主存地址寄存器 字计数器 数据缓冲区 设备地址寄存器 中断机制。周期窃取 DMA在主存和设备之间交换数据时需要占用内存总线,如果此时CPU也要使用总线则总是将占有权让给DMA,我们称这种占有为“周期窃取”。 DMA方式一次只能传送一个连续数据块,那离散的数据块怎么办? 通道方式

DMA方式下,要完成n个离散数据块的传输,CPU不得不发出n次IO指令。 如何才能实现发出一次指令就能完成任务? 装一个新的CPU来专门处理这些IO请求,IO处理器 =通道。通道可以执行一段由通道命令编写而成的通道程序。可以与CPU并行工作。

通道方式工作原理:生成通道程序并装入内存->进程i 进入阻塞状态->发送IO请求至通道->调度一个新进程进入CPU执行->从内存中取出通道程序->执行通道命令,在内存和设备传输数据->执行结束后发出中断->进程i 进入就绪状态,等候低级调度

通道分类:(1)字节多路通道 —— 以字节为单位传输信息,它可以分时地执行多个通道程序。当一个通道程序控制某台设备传送一个字节后,通道硬件就控制转去执行另一个通道程序,控制另一台设备传送信息。 (2)选择通道 ——以成组方式工作的,即每次传送一批数据,选择通道在一段时间内只能执行一个通道程序。当这台设备数据传输完成后,再选择与通道连接的另一台设备,执行它的相应的通道程序。 (3)数组多路通道——上述二者的结合体。

 

5.2 I/O软件原理

IO软件是操作系统的一个部分,它的设计方式是层次式的,每层向上提供服务,向下隐藏实现。

设备驱动层(把IO操作转换成硬件操作):该层包含了与设备密切相关的代码,每种设备类型都应该有对应的设备驱动程序。 设备驱动负责解释用户的IO请求,并向设备发出命令,并监督它们正确执行;它向上层提供统一的使用接口。

设备无关层:

用户进程所使用的库函数可以针对多种设备,如 write(fd,buffer,bytes)     该函数可以写文件,还可以写管道,fd就是一种文件设备的描述符。 stdin; stdout     这两种描述分别代表了键盘和显示器。 除了这些,设备驱动程序还看得懂另外诸如“dev/hdc”的设备描述符吗? 在用户接口和设备驱动层之间还需要一层来解释这些人看得懂、机器看不懂的文字,该层不考虑设备的硬件细节,所以称为“设备无关层”,也叫内核IO子系统。

内核IO子系统(操作系统IO软件)功能:(1)设备命名与保护:负责将设备名映射到相应的设备驱动程序 检查用户是否有请求设备的权限。(2)文件的逻辑记录到物理记录的转换:向用户软件提供统一大小的逻辑块,并将其转换成适合存储设备的物理块。(3)缓冲技术 块设备和字符设备都需要缓冲,常用技术有:单缓冲、双缓冲和多缓冲。(4)设备跟踪、挂起与释放 负责监控设备的使用情况,并据情况接受或拒绝设备的请求。(5)错误处理 一般由驱动程序完成,一旦发现错误会向上层报告,之后由设备无关软件进行处理。

5.3 缓冲技术

单缓冲:

每批数据处理时间为max(C,T) + M ,C < T : M + T ,C > T : M + C 。输入输出只能单向工作(半双工)

双缓冲:

多缓冲:操作系统从自由主存区域中分配一组缓冲区组成循环缓冲,称为缓冲池。 多缓冲的缓冲区是系统的公共资源,可供各个进程共享,并由系统统一分配和管理。

5.4 驱动调度技术

5.5 设备分配

设备独立性

通常用户不指定特定的设备,而指定逻辑设备,使得用户进程和物理设备独立开来,再通过其它途径建立逻辑设备和物理设备之间的对应关系,称这种特性为“设备独立性”。

设备分配技术

独占方式 静态:作业执行前分配,动态:直到需要时才分配

共享方式:一般不进行分配,主要工作是驱动调度

虚拟方式:分配给用户的不是真实的设备

5.6 虚拟设备

独占方式分配的设备成为系统效率的瓶颈 :静态分配:设备在进程执行期间只有少量时间在工作 ,动态分配:慢速设备分配给一个进程后,其它申请设备进程不得不等待 。

脱机外围设备操作:使用廉价的外围计算机专门负责作业的输入输出

联机同时外围设备操作 ,又称作假脱机操作,用一台计算机完成脱机外围设备中三台计算机实现的功能。 它同时接受多个进程对独占设备的请求,让每个进程都感觉自已拥有设备,我们称这种设备为“虚拟设备”。

第六章 文件管理

6.1 文件

文件定义: 一组信息的序列集合,这个集合有一个标识,即文件名

文件不但反映了用户概念中的逻辑结构,而且和存放它的辅助存储器的存储结构紧密相关。

文件系统:OS的组成部分,管理文件的存储、检索、更新,提供安全可靠的共享和保护手段。(1)文件的命名(2)实现文件从逻辑结构到存储结构的映射(3)统一管理文件存储空间,实施存储空间的分配与回收(4)实现文件信息的共享,并提供文件的保护和保密措施(5)向用户提供一个方便使用的接口。

文件的属性:

  • 名称:按照人们容易读取的形式选取的唯一标识符
  • 类型:被支持不同类型的文件系统所用
  • 保护:决定谁能读、写、执行的访问控制信息
  • 大小:当前文件的尺寸(一般以字节为单位)
  • 位置:文件在存储设备上的位置指针
  • 时间、日期和用户标识:创建时间、上次修改时间以及文件所有者ID,这些数据也可用于保护和使用跟踪

文件的命名:

  • 各个操作系统的命名规则略有不同,其一般形式为“文件名.扩展名”
  • 命名规则一般包含:(1)文件长度:DOS限制文件名为“8.3”格式,Windows的文件名允许长达255个字符(2)特殊字符:Windows文件名中不允许出现\、/、<、>等字符(3)大小写:Windows不区分文件名的大小写,但Unix和Linux区分
  • 通常扩展名用于定义文件的类型,常见的扩展名有:.bak .c .gif .html .mpg .o

文件的类型:

操作系统一般通过文件的扩展名得知文件的类型,并对它所能识别的类型进行处理,如: .exe/.com:是二进制可执行文件 .bat:是字符型的批处理文, 对于类似.doc/.txt的文件类型,需要相应的应用程序解释执行,OS只需要对这些类型进行程序关联即可。有些文件必须符合OS所要求的结构才能被解释,如可执行文件。OS无法支持所有未来可能出现的文件类型,所以它一般只认为文件是由字节流组成的并不去解释这些位。

文件的逻辑结构:

文件的类型也表达了文件的逻辑结构。所谓逻辑结构,就是指用户眼中信息的组织形式。

流式文件:文件中的信息仅仅是一串比特流,没有明显的结构,可以通过插入一些特殊字符进行分界。大多数现代操作对用户只提供此种文件类型。

记录式文件:文件的信息单位是逻辑记录,一个逻辑记录包含了若干数据项,一般需要高级语言程序和数据库管理系统支持。 成组:磁盘空间分配是按块为单位的,逻辑记录的大小不可能和物理块相等,通常将若干逻辑记录打包成组后再装入物理块 分解:把一个物理块里的逻辑记录分离出来的过程。

文件的存取方式

  • 顺序存取(基于磁带模型)。文件信息按顺序进行处理,存取时按照从前到后的顺序,每读/写完一个记录,指针自动推进以指示下一个要读/写的记录位置。
  • 直接存取(基于磁盘模型)。若文件记录长度N固定,则可实现直接访问。要读/写第i个记录时,相对于文件首部的偏移是N x i 用户每次存取时要告之访问相对记录号(块号)
  • 索引存取。一个逻辑文件中的记录过多时,读取一个想要的块可能要花费大量的时间去查找 对记录创建索引表,文件的记录按键值排序,用户通过键值查找文件记录(通常使用折半查找) 此方法需要占用存储空间存放索引表,索引表的大小与文件记录的数量成正比

6.2 文件目录

目录的作用

  • 搜索文件:实现“按名存取”
  • 登记文件:创建新文件时应该在目录里增加一个条目
  • 撤销文件:删除文件时要将目录相应项一并删除
  • 列出文件:将目录中文件的属性以列表方式显示出来
  • 文件更名:更改文件的名称但不改变文件内容

目录的内容 每一个文件信息构成了目录里的一项,称为文件控制块(FCB),它主要包括: 文件名、扩展名、文件属性(只读、隐藏、类型等)、文件大小 、在磁盘的起始块号、创建日期时间、上次修改日期时间

文件目录也用文件形式保存在磁盘上,被称为目录文件。

一级目录结构 每一个目录项对应一个文件,所有的文件都包含在同一个目录中 优点: 简单,易于理解和实现 缺点: 限制了用户对文件的命名 使用二级目录结构解决此问题

二级目录结构:解决不同用户之间引起的名称混乱问题,为每个用户创建一个独立的目录,即用户文件目录(UFD)。该目录与一级目录结构相似,列出该用户的所有文件。 系统还有一级主文件目录(MFD),用来管理所有用户文件目录(UFD)。 用户名和文件名构成了路径名,如”A/Afile2”。B用户要访问A用户的文件必须指出全部路径名。

树形结构:是二级目录的推广。每一级目录可以是下一级目录的说明,也可以是文件的说明。它允许用户创建自己的子目录,相应地组织文件。 树形结构有根目录,系统内的每个文件都有唯一的路径名,有两     种路径搜索方法: 绝对路径:从根经过所有子目录到达指定文件的路径,如:     “/usr1/code/file2” 相对路径:从当前目录开始定义     的路径,如:“code/file2”(当前目录是usr1) “.”:表示当前目录 “..”:表示父目录,如:“../lib/file1”

6.3 文件组织与数据存储

文件的物理结构是指逻辑文件在物理存储空间中的存放方法和组织关系。也称作文件的磁盘分配方案。

要找到文件的物理位置,目录里记录了文件的起始物理块号。 

用户凭文件名在目录中查找。

文件在存储空间的组织形式不需要和逻辑结构相同,但也可以相同 。

逻辑文件在存储空间组织形式: 顺序文件 、链接文件 、索引文件  。

文件分配表(FAT)

FAT是连接文件的变种。磁盘的簇中不存放指向下一簇的指针,这些离散簇的链接关系由一张文件分配表确定。 FAT表里的每一项指示一个簇号,每一项使用的位数限制了FAT能表达的最大磁盘容量。 如:FAT16(一簇32KB)支持最容量为2GB。

6.4 文件系统其他功能实现

基本文件系统:向设备驱动程序发送读写命令,以磁盘地址标识要读写的物理块。

文件组织系统:实现逻辑块号向物理块号的转换以及空闲空间管理器。

逻辑文件系统:管理文件系统的数据结构,如目录、FCB、文件保护属性等。

现有的文件系统 Windows:FAT16/FAT32/NTFS Unix:UFS Linux:Ext2/Ext3

文件系统的实现:

磁盘结构

       引导控制块:有引导OS所需要的信息通常是分区的第一个块

       分区控制块:包括分区的详细信息,如分区的块数,块的大小,空闲块的数量和指针等。UFS称之为超级块

       FCB:UFS称其索引节点inode

       目录结构

内存信息

        目录的缓存

       系统范围的打开文件表:每个被打开文件的FCB信息

       单个进程的打开文件表:包含一个指向系统范围打开文件表中合适条目的指针

猜你喜欢

转载自blog.csdn.net/qq_37865996/article/details/84946261