概述
操作系统作为用户和硬件之间的中间人。
操作系统的目标:用户需求:操作系统需要 容易学容易用,可靠性高,速度快。系统需 求:OS 必须易于设计实现维护,灵活可靠错 误少高效。
计算机系统分为四大部分:硬件、OS、应用 程序和用户。
操作系统时用户与计算机硬件之间的接口。
操作系统提供的接口有两类:命令级接口,
提供键盘或鼠标命令。程序级接口:提供系 统调用。
操作系统是计算机系统资源的管理者。OS 是 资源分配者是一个控制程序。
OS是扩充裸机的第一层系统软件。
大型机系统 Mainframe System
OS有:批处理系统、分时系统。批处理可以 分为多道和单道两种。多道程序:多个作业 保存在内存中,CPU 在它们之间进行切换。
发 展 : no software->resident moniters->muli- programming->multi-tasking。
Time Sharing system分时系统
分时系统是多个用户分时共享,把系统资源 分割,每个用户轮流使用一个时间片。
桌面系统 Desktop:PC
集群系统 Clustered:一组互联主机构成的统 一的计算机资源。有对称集群,多个节点跑 程序,互相监视。非对称:一台机器处于热 备用模式。集群用于高性能计算。
实时系统 Real Time 有软实时 硬实时。用于 工业控制、显示设备、医学影像、科学实验 手持 Handheld:PDA cellular telephone 嵌入式.
操作系统市场格局,三大体系:Unix 服务器 Win桌面 Android 手机
特权指令:用户程序不能直接使用,如 IO 时 钟设置 寄存器设置,系统调用不是特权指令 双模:用户态:执行应用程序时。内核态:
执行操作系统程序时。
内核态:能够访问所有系统资源,可以执行 特权指令,可以直接操作和管理硬件设备。
操作系统内核程序运行在内核态下。使用内 核栈
用户态:只能访问属于它的存储空间和普通 寄存器,只能执行普通指令。用户程序以及 操作系统核外服务程序运行在用户态下。使 用用户栈
操作系统结构
操作系统服务:
用户界面:CLI GUI
程序执行 IO 操作 文件系统操作 通信 错误检 测 资源分配 统计 保护和安全
操作系统的用户界面
操作系统接口:命令接口和程序接口(系统调 用)
命令接口:CLI GUI
程序接口:系统调用指 OS 提供的服务。
系统调用是进程和 OS 内核间的程序接口。
一般用 C/C++写,大多数由程序提供的叫做 API应用程序接口,而不是直接的系统调用。
三个常见的 API 是 win32 API,POSIX API 和 JAVA API。
系统调用很像函数,但是系统调用是 OS 内部
实现的,是 OS 内核里的。Time()是一个系统 调用
系统调用有三种传参方式:寄存器传参 参数 存在内存的块和表中,将块和表的地址通过 寄存器传递,linux 和 solaris 用这种 参数通过 堆栈传递。
操作系统结构:
简单结构
MSDOS,小、简单功能有限的系统,没有划 分为模块,接口和功能层次没有很好的划分 原始 UNIX:受到硬件功能限制,原始 UNIX 结构受限,分为两部分:系统程序和内核。U NIX LINUX单内核结构
层次结构
OS被划分为很多层,最底层 0 层是硬件,最 高层是用户接口。通过模块化,选择层,使 得每个层使用较低层的功能和服务。
微内核结构 microkernel system
只有最基本的功能直接由微内核实现,其他 功能都委托给独立进程。也就是由两大部分 组成:微内核和若干服务。好处:利于拓展、
容易移植到另一种硬件平台设计。更加可靠
(内核态运行的代码更少了),更安全。缺 点:用户空间和内核空间的通信开销很大 。 Windows NT windows 8 10 mac OS L4 单/宏内核 monolithic kernel
与微内核相反,内核的全部代码,包括子系 统都打包到一个文件中。更加简单更加普遍 的 OS 体系。优点:组件之间直接通讯,开销 小;缺点:很难避免源代码错误 很难修改和 维 持 ; 内 核 越 来 越 大 。 如 OS/360 , VMS Linux
模块 modules
大多数现代操作系统都实现了内核模块。面 向对象,内核部件分离,通过已知接口进行 沟通,都是可以载入到内核中的。总而言之 很像层次结构但是更加灵活。Linux solaris。
混合系统 Hybrid
大多数现代操作系统不是单一模型。Linux 和 solaris是 monolithic+module 。 Windows 大 部 分是 monolithic 加上 microkernel。Mac 是层次 Hybrid
虚拟机
采用 layered approach 系统生成
Bootstrap程序存在 ROM 里来定位 kernel,装 入内存执行
Process 进程
进程是一个正在执行的程序。进程包括:PC, 寄存 器, 数 据段 ( 全局 data),栈 ( 临时 data),堆
(alloc) 。 进 程 状 态 : new,running,ready,waiting,terminated.
进程状态会因为程序(系统调用),OS(调度),外 部(中断)动作而改变状态。Wait->run 和 ready-
>wait一般不可能发生。单处理器下,最多一 个 run,ready 进程构成就绪队列,wait 进程构 成多种等待队列。运行最多 1 最少 0,等待最
多 n 最少 0,就绪最多 n-1 最少 0。
OS用 PCB 来表示进程,PCB 包括 Process state,Program counter, CPU registers,CPU scheduling
information, Memory-management information
Accounting information File management I/O status information
Linux的 PCB 保 存 在 struct task_struct里
进程调度
调度队列有:作业 job 队列 就绪队列 设备队 列 进程在这些队列内移动。下图是队列图,
新进程开始就处于就绪队列
长程/作业调度:选择应该被带入就绪队列的 进程,调度频率低,控制多道程序设计的程 度(内存中进程数),现代操作系统没有长程 unix win。
短程/CPU 调度:从接下来要执行的进程中选 择并分配 CPU,频率很高。
中程调度:能将进程从内存或 CPU 竞争中溢 出,降低多道程序设计的程度。进城可以被 换出,之后再换入。增加了中程调度的队列 图
进程可以分为 IO 型和 CPU 型(CPU-bound) 上下文切换:CPU 切换进程时,必须保存当 前状态再载入 I 新状态的过程。进程的上下文 储存在 PCB 中。上下文切换是 overhead,过 程中不能进行其他事务。
进程操作
创建进程:进程标识符 pid;父子进程资源共 享模式:共享全部资源/部分/不共享;执行模 式:并发执行/父进程等待子进程结束再执行;
地址空间:子拷贝父/子进程装入另一个新程 序。UNIX 使用 fork 创建新进程,pid=fork()调 用后返回时,除了返回的 pid 不同,父子有完 全相同的用户级上下文,子进程返回 0,父返 回子的进程号。一般调用 fork 后,一个进程 会使用 exec 用新程序取代进程的内存空间 进程终止:父进程终止时,不同 OS 对子进程 不同:不允许子进程继续运行/级联终止/继承 到其他父进程上。
合作进程:独立进程:运行期间不会受到其 他进程影响。进程合作优点:信息共享,运算 速度提高,模块化,方便。生产消费问题的两种 缓冲:无限缓冲 unbounded-buffer 生产者可以 无限生产;有限缓冲,缓冲满后生产者要等待 进程通信 IPC
分为直接通信和间接通信,常用的通信机制 有信号,共享储存区,管道,消息,套接字。直接 通信,需要明确地命名通信的接受者或发送 者,send(P,msg),receive(Q,msg)。一个链接至于 2个进程有关。每对进程共享一个通信链,链 是无向或双向。简介通信:创建 mailbox,通 过 mailbox 收发 msg,实际上就是端口,邮箱 有唯一的 id,进程有 mailbox 才能可以通信,
一个 link 和多个进程有关,一对进程可能和 多个进程有关,两个进程可能有多条线路,
每条对应一个 mailbox
Thread 线程
进程概念体现了两个特征:资源拥有单位/调 度 单 位 unit of resoutce ownership/unit of dispatch,OS 将它们分别处理,调度单元被称 为线程或轻量进程 LWP lightweight process.资 源拥有单元被称为进程任务。线程就是进程 内一个执行单元或可调度的实体。重量型/传 统线程=单线程的进程
线程能力:
有状态转移,不运行时保存上下文,有一个执行 栈,有局部变量的静态存储,可以存取所在线程 的资源,可以创建撤销其他线程,不拥有系统资 源(拥有的少量资源,资源是分配给进程)一个进 程中的多个线程可以并发执行(进程可以创建 线程执行同一个程序的不同部分),系统开销小、
切换快(进程的多个线程都在进程的地址空间 活动)
动机:线程共享 code section data sectioin 和 OS资源。优点:创建新线程耗时少,线程间切 换耗时少,线程间通讯因为共享 OS 资源和内 存,无需调用内核;适合多处理器系统 用户级线程:不依赖于 OS 核心(内核不了解 用户线程的存在), 应用进程利用线程库提供创 建 同步 调度和管理线程的函数来控制用户线 程。一个线程发起系统调用而阻塞,则整个 进程在等待。
内核级线程:依赖于 OS 核心,由内核的内部 需求进行创建和撤销 ,用来执行一个指定的 函数。一个线程发起系统调用而阻塞,不会 影响其他线程。时间片分配给线程,所以多 线程的进程获得更 多 CPU 时间。
多线程模型:
多对一:将多个用户级线程映射到一个内核 线程。由线程库在用户空间进行,如 Solaris Green和 GNU Portable。优点:无需 OS 支持,
可以调整(tune)调度策略满足应用需求,无系 统调用线程操作开销很低。缺点:无法利用 多处理器 不是真并行,一个线程阻塞时整个 进程也阻塞
一对一:一个用户到一个内核。每个内核线 程 独 立 调 度 , 线 程 操 作 由 OS 完 成 , win NT/XP/2000 linux Solaris 9 later。优点:每个 内核线程可以并行跑在多处理器上,一个线 程阻塞,进程的其他线程可以被调度。缺点:
线程操作开销大,OS 对线程数的增多处理必须 很好
多对多:多用户到多内核,允许 OS 创建足够 多的内核线程, Solaris prior v9 win NT/2000 with ThreadFiber。两极模型:多对多的变种 , 一部分多对多,但是有一个线程是绑定到一 个内核上。IRIX HP-UX Tru64 Solaris 8 earlier fork: 复 制 调 用 线 程 还 是 复 制 全 部 线 程 , UNIX两种都有;exec 替换整个进程也就是所 有线程。如果 fork 后跟 exec 那么 fork 就复制 调用线程即可。两种线程取消:异步取消:
立即终止目标线程,延迟(deferred)取消:目 标线程不断检查自己是否该终止。信号处理:
信号由特定事件产生,信号必须要发送给进 程,信号被发送后需要被处理。选择:发送 信号到信号所应用的线程;到进程内的每个 线程;进程内的某些线程;规定特定线程接 收信号。线程池:优点:用现有县城处理请 求比等待创建新线程快;限制了可用线程的 数量。线程特有数据:允许县城自己保存数 据拷贝,在无法控制创建线程时很有用,比 如用线程池的时候。
调度程序激活 Scheduler Activations 多对多和两级模型需要通信来维护分配给应 用适当数量的线程;SA 提供 upcalls,一种从 内核到线程库的通信机制;这种通信保证了 应用可以维持正确数量的线程。Win xp linux WIN XP实现一对一模型,但是通过 fiber 库 也支持多对多。每个线程包括:ID 寄存器集 用户栈和内核栈 私有数据存储区。后面三个 都是线程的上下文。主要数据结构 ETHREAD 执行线程块 KTHREAD 内核线程块 TEB 线程 执行环境块 后者在用户空间 前两者内核空间。
Linux把线程叫做 tasks,除了 fork,额外提供 了线程创建通过 clone 系统调用完成,它允许 子任务和父任务共享地址空间
CPU Scheduling
CPU调 度 时 机 : run->wait,run->ready,wait-
>ready以及终止时。调度只能发生在 1 和 4 时 叫非抢占,否则叫做抢占。
分派程序(dispatcher)将 CPU 的控制交给由短 程调度选择的进程。功能有:上下文切换 切 换到用户模式 跳转到用户程序的合适位置来 重启程序。分派程序停止一个进程而启动另 一个所需要花费的时间叫分派延迟(dispatcher latency)。
调度算法的选择准则和评价:
面向用户:周转时间 turnaround time(进程从 提交到完成所用时间 包括就绪和阻塞中的等 待时间 带权周转时间=周转时间/CPU 执行时 间) 响应时间 等待时间 截止时间 公平性 优先 级。面向系统:吞吐量 throughput 处理机利 用率 CPU utilization 设备均衡利用。调度算法 自身:易于实现 开销较小。最佳算法准则:
CPU利用率 吞吐量 周转时间 等待时间 响应 时间 公平
调度算法
First-Come, First-Served (FCFS) Scheduling: 根据就绪状态的先后来分配 CPU,非抢占,
最简单,利于长进程,不利于断进程,利于 CPU型不利于 IO 型。
Shortest-Job-First (SJF) Scheduling:对预计执 行时间短的作业优先分派 CPU,分为抢占式:
如果新来的进程时间比当前的还短,抢占,
这 种 SJF 叫 做 Shortest-Remaining-Time-First
(SRTF)。非抢占:允许当前进程运行完再运 行最短的。SJF 被证明是最佳算法,它能给出 最小平均等待时间。
SJF是无法实现的,因为不知道下一个 CPU 脉冲 burst 时长。
最高响应比优先 HRRN(Highest Response Ratio Next):SJF 的变种,响应比 R = (等待时间 + 要求执行时间) / 要求执行时间,是 FCFS 和 SJF的折中。满足短任务优先且不会发生饥饿 现象
优先级调度
每个进程都有优先级数字相关联,总是把 CPU分配给就绪中最高优先级的进程。确定 进程优先级的两种方法:静态优先权:创建 时就确定好 动态优先权:给予某种算法调整 一般数字越小优先级越高。SJF 是以下一次 CPU的脉冲长度作为优先数的优先级调度特 例。优先级调度也可以是抢占/非抢占的。问 题:无穷阻塞或借,低优先级的进程可能永 远无法执行。解决方法:老化(aging)逐渐增加 在系统中等待时间长的进程的优先级,也就 是动态优先级。
时间片轮转调度 Round Robin(RR) 通过时间片轮转提高并发性和响应时间,提 高资源利用率。算法:将就绪中的进程按照 FCFS排队;每次调度时将 CPU 分给队首进程,
让其执行一个时间片;时间片结束时发生时 钟中断;调度程序暂停当前进程执行将其送 至就绪的队尾,然后上下文切换至新的队首;
对称没用完一个时间片的话就让出 CPU(如阻 塞)
如果就绪队列中有 n 个进程时间片为 q,那么 每个进程得到 1/n 的 CPU 时间,长度不超过 q。每个进程必须等待的时间不超过(n-1)q,
知 道 下 一 个 时 间 片 位 置 。 例 如 5 个 进 程 , 200ms时间片,那么每个进程每 100ms 不会 得到超过 20ms 的时间。
RR算法性能依赖于时间片大小,如果 q 很大,
就和 FCFS 一样了;如果 q 很小,那么 q 也要 足够大来保证上下文切换,否则开销过大。
时间片长度的影响因素:响应时间一定时,
就绪进程越多,时间片越小;应当使用户输 入通常能在一个时间片内完成,否则相应 平 均周转和平均带权周转都会延长。一般来说 RR比 SJF 有更高的平均周转,但是响应时间 更好。时间片固定时,用户越多响应时间越 长。
多级队列调度
将就绪队列根据性质或类型的不同分为多个 独立队列区别对待,综合调度。每个作业固 定归入一个队列。不同队列可能有不同的优 先级 时间片 调度算法。例如系统进程、用户 交互、批处理等这样的队列分法。
一般,分成前台 foreground(交互式 interactive) 和后台(批处理),后台一般 RR,前台 FCFS。
多级队列在队列间的调度算法有:固定优先 级,即先前台后后台,有饥饿;给定时间片,
如 80% 执 行 前 台 的 RR , 20% 执 行 后 台 的 FCFS。
Multilevel Feedback Queue Scheduling多级反 馈队列
是 RR 和优先级算法的综合。与多级队列的区 别,这个允许进程在不同的就绪队列切换,
等待时间长的进程会进入到高优先级队列中。
优点:提高吞吐量降低平均周转而照顾断进 程;为 IO 设备利用率和降低响应时间而照顾 IO进程型;不需要顾及进程执行时间,动态 调节。
Process synchronization
The Critical-Section Problem
N个进程都计算同样的共享数据,每个进程都 有一个临界区,其中共享数据被访问。问题:
需要保证只有一个进程进入临界区。临界区 问题的解决必须满足三个要求:互斥(mutual exclusion); 空 闲 让 进 (progress) ; 有 限 等 待 (bounded wait)。让权等待不是必须的。
Peterson算法
只用于两个进程的情况,并且假设 load 和 store是原子操作,是一种软件解决方法。
Bakery Algorithm (面包房算法)
硬件同步
单处理机很简单:临界区禁止中断,这样就 OK了。
抽象出两个硬件实现的原子操作:赋值和交 换,然后来解决临界区,testandset 的共享变 量是 lock 初始 false,swap 也是一样,但是多 了局部变量 key 不是共享的。
硬件方法优点:进程数随意,简单,支持多 个临界区;缺点:无法让权等待,可能饥饿,
可能死锁。会引起忙等 信号量 semaphores
两个操作 wait(P)和 signal(V)。信号量分为计 数信号量,值域不受限制,二值信号量,只 能是 01 所以也叫互斥锁(mutex locks)。为了 等待资源进行无限循环是忙等,通过对信号 量 的 修 改 增 加 了 block(run->wait) 和 wakeup(wait->ready) 来 避 免 了 忙 等 。 wait(semaphore *S) { S->value--; if (S->value <
0) { add this process to S->list; block();} } signal(semaphore *S) { S->value++; if (S->value
<= 0) { remove a process P from S->list;
wakeup(P);} }具有忙等的信号量值非负,但是 这种实现可以为负,负数的绝对值代表等待 该信号量的进程数,0 代表无资源可用。Wait 和 signal 成对出现,互斥操作就在同一进程出 现,同步操作在不同进程。连续的 wait 顺序 是需要注意的,但是连续的 signal 无所谓。同 步 wait 和互斥 wait 相邻时,要先同步 wait。
优点:简单、表达能力强;缺点:不够安全,
使用不当会死锁,实现复杂
优先级倒置(priority inversion) 当优先级较低的 进程持有较高优先级进程所需的锁定时的调 度问题
Bounded-Buffer Problem 有限缓冲区 生产者- 消费者问题
是很多相互合作进程的抽象。算法:设置 N 个缓冲项;信号量 mutex 初始化为 1,用来保 证对缓冲池访问的互斥要求;信号量 full 初始 化为 0,表示满缓冲项的个数;信号量 empty 初始化为 N 表示空缓冲项的个数。生产者:
do {…produce an item in nextp …wait(empty);
wait(mutex); …add nextp to buffer … signal(mutex); signal(full);} while (1);消费者:
do {wait(full); wait(mutex); …remove an item from buffer to nextc …signal(mutex);
signal(empty); …consume the item in nextc …}
while (1);
Readers-Writers Problem
数据库读写的抽象。第一读写问题:允许多 个读者同时读,但是只有一个写者,也就是 没有读者会因为写者在等待而等待其他读者 的完成,写者可能饿死。第二读写问题:写 者就绪后,写者就立即开始写操作,也就是 说写者等待时,不允许新读者进行操作,读 者可能饿死。
共享数据有访问的数据、mutex 初始 1,保证更 新 readcount 时互斥,wrt 初始 1,为读写公用,
供写者作为互斥信号量,被第一个进入临界 区和最后一个离开临界区的读者使用,其他 读者不适用。Readcount 初始 0,用来跟踪多 少进程正在读。写进程: do {wait (wrt) ; //
writing is performed signal (wrt) ;} while (TRUE); 读进程:do {wait(mutex); readcount+
+; if (readcount == 1) wait(wrt);signal(mutex);
…reading is performed …wait(mutex);
readcount--; if (readcount == 0) signal(wrt);signal(mutex); } while (TRUE);
Dining-Philosophers Problem 哲学家进餐 典型的同步问题,问题描述:N 个哲学家坐在 圆桌,每个哲学家和邻居共享一根筷子;哲 学家吃饭要用身边的两只筷子一起吃;邻居 不允许同时吃饭;哲学家只会思考或者吃饭。
共 享 数 据 : 数 据 集 / 一 碗 米 饭 ; 共 享 变 量 chopstick[5]初始为 1;第 i 个哲学家进程:do {wait(chopstick[i]) wait(chopstick[(i+1) % 5]) … eat …signal(chopstick[i]); signal(chopstick[(i+1)
% 5]); … think …} while (1);这个解决方案可 以保证没有 2 个哲学家同时使用 1 个筷子,但 是很显然会导致死锁,如果 5 个哲学家同时饥 饿,同时拿起左手筷子,就死锁了。
一些其他的可能解决:最多只允许 4 个哲学家 坐在桌上/临界区内必须拿起两根筷子/使用非 对称的解决方法:奇数先拿左手,偶数先拿 右手。这些额外限制都能防止死锁。
Deadlock 死锁
死锁指多个进程因竞争共享资源而造成的一 种僵局,若无外力作用,这些进程都将永远 不能再向前推进。进程按以下顺序使用资源:
申请 使用 释放。申请和释放为系统调用。
四个必要条件:
互斥:至少有一个资源处于共享模式,一次 只能有一个进程使用该资源 占有并等待(hold and wait):一个进程必须至少占有一个资源并 且等待另一个资源,且该资源被其他进程占 有 不可抢占:资源不能被抢占,只能在进程 使用完成后释放 循环等待:进程间循环等待 资源,A 等 B 占的 B 等 C 占的 C 等 A 占的。
资源分配图,由点 V 和边 E 组成,V 被分为 两部分:系统活动进程的集合 系统所有资源 类型的集合。进程 Pi 到资源 Rj 的有向边记为 Pi->Rj,表示进程 Pi 已
经申请了资源类型 Rj 的一个实例,叫请求边;
资源 Rj 到进程 Pi 的有 向边表示资源类型 Rj 的一个实例已经分配给 了进程 Pi,叫做分配边。
进程用圆表示,资源类
型用方表示,资源实体是方内部的点。
如果分配图无环->没有进程死锁,如果有环,
那么可能死锁。如果每个资源恰好只有一个 实例,有环则必死锁。如果环所在的资源类 型是只有一个实例的,则必死锁。如果每个 资源有多个实例,有环不一定死锁。
P4可能 释放 R2 的 实例 ,这 个资 源分 配 给 P3,这样就打破了死锁。
死锁处理
保证系统不进入死锁:预防 避免 prevention avoidence;允许进入死锁但是需要恢复:检 测 接触 detection recovery。U L W 三个系统都 忽略问题假装没有死锁,是鸵鸟方法。
死锁预防
通过限制请求的方式来预防死锁。
互斥:对于非共享资源必须互斥,例如一台 打印机不能被多个进程共享,因此要互斥,
而共享资源不需要互斥,也不睡导致死锁,
类似只读文件。
占有并等待:必须保证:一个进程申请一个 资源时不能占有其他资源。进程在执行前就 要申请并分配资源,是资源静态预分配的方 法;缺点:低资源利用率、可能饥饿。
非抢占:如果一个进程战友资源并且申请了 另一个不能立即分配的资源,那么它现已分 配的资源都可以被抢占,也就是被隐式释放 了。抢占资源分配到进程所等待的资源的连 表上。进程需要获取到原有的资源和申请的 新资源后才能运行。
循环等待:按照资源请求递增的方式分配资 源,通过资源的有序申请破坏了循环等待条 件。
死锁避免
前面的方法虽然避免了死锁,但是降低了吞 吐率,我们可以通过获取一些额外的事先信 息从而避免死锁 prior information。
最简单和最有效的模型要求每个进程声明它 可能需要的每种类型的资源的最大数量。死 锁避免算法动态检查资源分配状态,确保永 远不会出现循环等待。资源分配状态由可用 和已分配资源的数量以及进程的最大需求定 义。
安全状态:对于所有进程,如果存在一个安 全序列,那么系统就处于安全状态。对于进 程序列 P1,P2,…,Pn,如果对于每一个 Pi,Pi 仍 然可以申请的资源数小于当前可用的资源加 上所有进程 Pj(i>j)所占有的资源,那么这一序 列是安全序列。这种情况下,进程 Pi 的资源 即使不能立即可用,那么 Pi 可等待知道所有 Pj释放其资源,当它们完成时 Pi 就可以运行,
Pi运行结束后,Pi+1 就可以获得到所需的资 源,如此进行。
安全状态->没有死锁;不安全状态->可能有死 锁;避免->保证系统永远不进入非安全状态。
资源分配图,单实体资源类型避免算法:
引入一种新边 cliam edge 需求边,Pi->Rj 表示 进程 Pi 在未来可能请求资源 Rj,用虚线表示。
当进程真正请求资源时,用请求边覆盖掉需 求边。当资源被分配给进程后,用 assignment edge分配边来覆盖掉请求边,当资源被释放 后,分配边恢复为需求边。系统必须事先说 明需求边。
算法:假设进程 Pi 申请资源 Rj。只有在需求 边 Pi –>Rj 变成 分配边 Rj->Pi 而不会导致资源 分配图形成环时,才允许申请。
用该算法循环检测,如果没有环存在,那么 资源分配会使系统继续安全状,否则就会不 安全,Pi 就要等待。
银行家,多实体资源类型避免算法:
每个进程实现说明最大需求;进程请求资源 时可能会等待;进程拿到资源后必须在有限 时间内释放它们。
数据结构:
N进程数,m 资源类型的种类数;Available:
长度为 m 的向量,表示每种资源的现有实例 数量,available[j]=k 表示 j 型资源还有 k 个;
Max:n*m 的矩阵,定义每个进程的最大需求,
max[i][j]=k表示进程 Pi 最多可以申请 k 个 Rj 型资源;Allocation:n*m 的矩阵,表示每个 进 程 所 分 配 的 各 种 资 源 类 型 的 实 例 数 , allocation[i][j]=k表示已经为 Pi 分配了 k 个 Rj 型实例;Need:n*m 矩阵,表示每个进程还
需要的剩余的资源,need[i][j]=k 表示进程 Pi 还 可 能 继 续 申 请 k 个 Rj 型 的 实 例 。 Need=max-alloction。
安全状态检测算法:
1. 设 work 和 finish 分别是长度为 m 和 n 的向 量,初始化:work=available,finish[i]=false;
2.寻找 i 满足 finish[i]=false 且 need[i]<=work,
如果 i 不存在跳到第四步;
3.work=work+allocation[i],finish[i]=true, 返 回 第二步;
4.如果所有的 finish 都是 true,那么系统处于 安全状态。
算法需要 m*n*n 的操作数量级确定系统状态 资源请求算法:
Request[i]为 Pi 的请 求向 量, 如果 request[i]
[j]=k那么进程 Pi 需要资源类型 Rj 的数量为 k。当进程 Pi 请求资源时,动作如下:
1.如果 request[i]<=need[i]跳到第二步,否则出 错,因为进程 Pi 已经超过了其最大需求。
2.如果 request[i]<=available 跳到第三步,否则 Pi必须等待,因为没有可用资源
3.假定系统可以分配给进程 Pi 请求的资源,
进 行 下 面 的 操 作 : Avaible=avaible- request[i];allocation[i]=allocation[i]
+request[i];need[i]=need[i]-request[i]; 如 果 产 生的资源分配状态是安全的,那么交易完成 且进程 Pi 可以分配到资源,如果新状态不安 全,那么进程 Pi 必须等待 Request[i]并且恢复 到原有的资源分配状态。
死锁检测
允许系统进入死锁状态的话,那么系统就需 要提供检测算法和恢复算法。
等待图,单实体资源类型检测算法:
等待图是资源分配图的变形,节点都是进程,
Pi->Pj表示 Pi 在等待 Pj 释放 Pi 所需的资源。
当且仅当等待图中有一个环,系统死锁,检 测环的算法需要 n*n,n 为点数。
多实体资源类型检测算法:
数 据 结 构 : Available , alloction 是 一 样 的 , request:n*m 的矩阵,表示当前各进程的资源 请求状况,request[i][j]=k 表示 Pi 正在请求 k 个资源 Rj。
算法:1.设 work 和 finish 分别是长度为 m 和 n 的 向 量 , 初 始 化 : work=available , 如 果 allocation[i]非 0,finish[i]=false 否则初始化为 true;
2. 寻 找 i 满 足 finish[i]=false 且 request[i]<=work,如果 i 不存在跳到第四步;
3.work=work+allocation[i],finish[i]=true, 返 回 第二步;
4.如果某个 finish 是 false,那么系统处于死锁 状态,且对应下标的进程 Pi 死锁。
算法需要 m*n*n 的操作数量级确定系统状态 死锁检测算法的应用
检测算法的调用时刻及频率取决于:死锁发 生频率以及思索发生时受影响的进程数。如 果经常发生死锁,那么就要经常调用检测。
如果在不确定的时间调用检测算法,资源图 可能有很多环,通常不能确定哪些造成了死 锁
死锁恢复
检测到死锁后的措施:通知管理员 系统自己 恢复。打破死锁的两种方法:抢占资源 进程
终止。
进程终止
两种方法来恢复死锁:终止所有死锁进程 一 次终止一个进程直到不死锁。许多因素都影 响终止进程的选择:优先级 进程已经计算了 多久,还要多久完成 进程使用了哪些类型的 资源 进程还需要多少资源 多少进程需要被终 止 进程是交互的还是批处理的
抢占资源
抢占资源需要处理三个问题:
选择一个牺牲品 victim:要代价最小化 回滚:回退到安全状态,但是很难,一般需 要完全终止进程重新执行
饥饿:保证资源不会总是从同一个进程中被 抢占。常见方法是为代价因素加上回滚次数。
Main Memory主存
层次存储中主存 cache 寄存器为 volatie 易失的。
逻辑地址/虚地址/相对地址:由 CPU 生成,首 地址为 0,逻辑地址无法在内存中读取信息。
物理地址/实地址/绝对地址:内存中储存单元 的地址,可以直接寻址。
物理地址中的逻辑地址空间是通过一对基址 寄存器和界限地址寄存器控制的 base and limit register。如果基址寄存器为 300040,界限寄 存 器 为 120900 , 那 么 程 序 的 合 法 访 问 从 300040到 420910(含)的所有地址。
地址绑定的三种情况:
编译时间:如果编译时就知道进程在内存中 的地址,那么就可以生成绝对代码 absolute code。
装载时间:编译时不知道在哪,那么编译器 生成可重定位代码 relocatable code。
执行时间:如果进程在执行时可以移动到另 一个内存段,需要硬件支持也就是 base and limit目前绝大多数都是采用这种。
Memory-Management Unit (MMU)
就是将虚拟地址映射到物理地址的硬件设备。
在 MMU 中,base 寄存器叫做重定位寄存器,
用户进程送到内存前,都要加上重定位寄存 器的值。PA=relocation reg+LA。用户程序只 能处理 LA,永远看不到真的 PA。
Dynamic Loading(动态加载)
进程大小会收到物理内存大小的限制,为了 有更好的空间使用率,采用动态加载。一个 子程序只有在调用时才被加载,所有子程序 都可以重定位的形式存在磁盘上,需要的时 候装入内存中。OS 不需要特别支持,是程序 设计做的事。当需要大量的代码来处理一些 不常发生的事时很有用,如错误处理。
Dynamic Linking(动态链接)
将链接延迟到运行时,DDL。动态链接需要 一个存根 stub,它是一小段代码,用来指出如 何定位库程序。
Swapping(交换技术)
进 程 可 以 暂 时 从 内 存 中 交 换 到 备 份 存 储 backing store上,当需要再次执行时再调回。
需要动态重定位 dynamic relocate
备份存储:是快速硬盘,而可以容纳所有用 户的所有内存映像,并为这些内存映像提供 直接访问,如 Linux 交换区 windows 的交换文 件 pagefile.sys
Roll out roll in:如果有一个更高优先级的进程 需要服务,内存交换出低优先级的进程以便
装入和执行高优先级进程,高执行完后低再 交换回内存继续执行。
交 换 时 间 的 主 要 部 分 是 转 移 时 间 transfer time。总转移时间与所交换的内存大小成正比。
系统维护一个就绪的可立即运行的进程队列,
并在磁盘上有内存映像。
Contiguous Allocation(连续分配)
内存通常分为两个区域:一个驻留 resident 操 作系统,一个用于用户进程,由于中断向量 一般位于第内存,所以 OS 也放在低内存。重 定位寄存器用于保护各个用户进程以及 OS 的 代码和数据不被修改。Base 是 PA 的最小值;
limit包含了 LA 的范围,每个 LA 不能超过 Limit。MMU 地址映射是动态的。
Multiple-partition allocation:分区式管理将内 存划分为多个连续区域叫做分区,每个分区 放一个进程。有固定分区和动态分区两种。
动态分区:
动态划分内存,在程序装入内存时切出一个 连续的区域 hole 分配给进程,分区大小恰好 符合需要。操作系统需要维护一个表,记录 哪些内存可用哪些已用。从一组可用的 hole 选择一个空闲 hole 的常用算法 first best worst- fit三种。分别是分配第一个足够大的/分配最 小的足够大的/分配最大的。First 和 best 在时 间和空间利用率都比 worst 好。还有一个 next- fit是每次都从上次查找结束的位置开始找,
找到第一个足够大的。
碎片 fragmentation
first和 best 都存在外部碎片的问题。外碎片指 所有的总可用内存可以满足请求,但是并不 连续。外碎片可以通过紧凑 compaction 拼接 defragmentation减少。重定向是动态并且在执 行时间完成可以进行紧凑操作 重新排列内存 来将碎片拼成一个大块,但是拼接的开销很 大。
内碎片是进程内部无法使用的内存,这是由 于零头和块大小造成的,比如块大小 8B,进 程有 9B,那么不得不给他 16B 的内存,就出 现了 7B 的内碎片。
分页存储管理
分页允许进程的 PA 空间非连续;将物理内存 分为固定大小的块,叫做帧 frame/物理块/页 框,将逻辑内存也分为同样大小的块叫做页 page,Linux Win(x86)是 4KB。
OS需要跟踪所有空闲帧,叫帧表。
运行一个 n 页的程序就需要找到 n 个空帧然后 装载进去。
OS需要维护一个页表来进行 LA 到 PA 的转换。
分页技术避免了外碎片,只有内碎片存在。
物理地址 逻辑地址 页表项数计算:
页表项数和内存大小相关 2^m B 的内存大小 对应需要 m 项的页表,物理地址长度为 m。
逻辑地址和页表大小及虚存空间有关:虚存 大小 2^m B 那么逻辑地址长度为 m,页表大 小 2^n B,则页偏移位数 n,页号位数 m-n。
地址映射过程:逻辑页号拼上 offset 经过页表 查 到 物 理 页 号 , 然 后 得 到 物 理 真 号 拼 上 offset,然后进入到内存中找 frame。
页表的实现
页表放在内存中。PTBR page-table base reg 指 向页表,切换页表只需要改变这个寄存器就 可以 PRLR page-table length register 说明页表
长度,这样的模式下每次数据/指令访问都需 要两次内存访问,一次查页表一次查数据/指 令。为了加速这个过程,引入了特殊的转换 表缓冲区 TLB,是一种硬件 cache。部分 TLB 维护了 ASID addressspace identifier,用来唯一 地标识进程,为进程提供空间保护。
Effective Access Time 有效访问时间 EAT Associative lookup=t1查 TLB 表的时间 Memory access time=t2 内存访问时间 αTLB命中率
那么 EAT=(t1+t2)* α+(t1+t2+t2)* α 也就是查 TLBmiss 后,需要进内存查一次页 表,再去取一次数据,命中就直接取数据。
保护 protection
内存保护通过与每个帧关联的保护位实现 。 Valid bit存在页表中的每一个条目上。
Shared code共享代码:如果代码是可重入即 只 读 代 码 reentrant code 或 者 是 纯 代 码 pure code,可以共享,共享代码在各个进程中的逻 辑地址空间相同。然后每个进程再花较小的 空间保存私有代码和数据即可。
分级页表 Hierarchical page table
由于现代计算机逻辑地址空间很大,导致页 表会很大,而且页表还要连续,所以不现实。
因此要将页表划分变小。简单的实现方法:
两页分页算法,就是将页表再分页。就是将 页号部分再划分为页偏移和页码。下面是寻 址模式:
P1是用来访问外页表的索引,p2 是外页表的 页偏移,然后 d 是内页表的偏移。对于一个 32位的 LA,一般 10 位外 10 位内 12 位偏移
页大小为 210B,页表项大小为 2B,采用二级 页表,一页可存放 29个页表项,逻辑地址空 间大小为 216页,要使表示整个逻辑地址空间 的 页 目 录 表 中 包 含 的 个 数 最 少 , 则 需 要 216/29=27=128个页面保存页表项,即页目录表 中包含的个数最少为 128。
哈希页表
超过 32 位 LA 地址空间时,一般采用哈希页 表,将虚页号的哈希值存到哈希表里,哈希 表的每一项都是链表,链着哈希值相同的页 号。然后在查表时用虚页号与链表中的每个 元素进行比较从而查物理表号
反向页表
对于每个真正的内存帧才会有一个条目。每 个条目包含保存在真正内存未知的页的虚地 址及拥有该页的进程信息。因此整个系统只 有一个叶彪,对每个物理内存的帧也只有一 条相应的条目。拿时间换空间,需要为页表 条目中添加一个地址空间标识符 ASID。
分段 Segmentation
分页无法避免的是用户视角的内存和物理内 存的分离。分段管理支持用户视角的内存管 理方案,LA 空间是由一组段组成的,每个段 都有其名称和长度,地址指定了段名称和段 内 偏 移 。 因 此 LA 通 过 有 序 对 <segment- number,offset>构成。
段表将用户定义的二维地址映射成一维,每 一 个 条 目 包 含 base 和 limit 。 STBR segment table base reg 指向内存中段表的位置,STLR 一个程序使用的段长度,用户使用的有序对 中的 segment-number 必须小于 STLR。同样有 valid位,还有读写执行的权限设置,也可以 进行 code share。内存分配是动态存储分配问 题。
Virtual Memory虚存
虚存将用户的路基存储和物理存储分开;LA 空间可以大于 PA 空间;允许 PA 空间被多个 进程共享。
局部性原理:时间:指令的一次执行和下次 执行 数据的一次访问和下次访问都集中在一 个较短时期内;空间:当前指令和邻近的指 令 当前数据和邻近的数据都集中在一个小区 域内。
虚存是是具有请求调入功能和置换功能,能 仅把进程的一部分装 入内存便可运行进程的 存储管理系统,它能从逻辑上对内存容量进 行 扩充的一种虚拟的存储器系统。
按需调页 Demand Paging
指在需要时才调入相应的页的技术。采用 lazy swapper的方式,除非需要页面,否则不进行 任何页面置换。
页错误 Page fault
非法地址访问和不在主存或无效的页都会 page fault。Page fault rate 等于 1 不代表 every page is a page fault。
更完整的页表项 请求分页中
虚拟页号 物理帧号 状态位 P(存在位 页是否 已调入内存) 访问字段 A(记录页面访问次数) 修改位 R/W(调入内存后是否被修改过) 外存 地址(用来调页)
Effective memory-access time有效访问时间 EAT=(1-p)*memory access time + p*page fault time
Page fault time包括 page fault overhead, swap page out, swap page in, restart overhead等 为了计算 EAT,必须知道需要花多少时间处 理 page fault,page fault 会引起以下动作的产 生:
1.陷入 trap 到 OS 2.保存用户 reg 和进程状态 3.确定中断是否为 page fault
4.检查页引用是否合法并确定所在磁盘位置 5.从磁盘读页到内存的空闲帧(包含磁盘队列 中的等待 磁盘的寻到 旋转延迟 磁盘的传输延 迟)
6.在等待过程中的 CPU 调度 7.IO中断
8.保存其他用户寄存器和进程状态(如果进行 了 6)
9.确定中断是否来自磁盘
10.修正页表和其他相关表,所需页已经在内 存中
11.等待 CPU 再次分配给本进程
12.恢复用户寄存器、进程状态和新页表,重 新执行。
其中的三个主要 page fault 时间是缺页中断服 务时间 缺页读入时间和重启时间
写时复制 copy-on-write
COW copy on write允许父子进程开始时共享 同一页面,在某个进程要修改共享页时,它 才会拷贝一份该页面进行写。
COW加快了进程创建速度。当确定一个页采 用 COW 时,这些空闲页在进程栈或堆必须拓 展时可用于分配 或用于管理 COW 页。OS 此 案用按需填 0 zero fill on demand 按需填零页 需 要 在 分 配 前 填 0.Win linux solaris 都 用 了 COW
页面置换
寻找一些内存中没有使用的页换出去。内存 的过度分配 over-allocation 会导致 page fault 调 页后发现所有页都在使用。
使用 dirty/modify 位来减少页传输的开销,只 有脏页才需要写回硬盘。
基本页面置换过程:
1.查找所需页在磁盘上的位置。2.查找空闲帧,
如果有直接使用;如果没有就用置换算法选 择一个 victim,并将 victim 的内容写回磁盘,
改变页表和帧表。3.将所需页读入新的空闲帧,
改变页表和帧表。4.重启用户进程。
页面置换算法
采用最小页错误率的置换算法。
评估方法:
针对特定的内存引用序列,运行算法,计算 出页错误数。引用序列叫做引用串 reference string。
注意两个事实:给定页大小,只需要关心页 码,不用管完整地址;紧跟页 p 后面对页 p 的 引用不会引起页错误。
First-In-First-Out Algorithm (FIFO,先进先 出算法)
最简单的页面置换算法。必须置换一页时,
选择最旧的。不需要记录时间,只需要 FIFO 队列来管理页即可。15 次缺页
FIFO会出现可用帧越多,错误数越大的问题,
这种结果叫 Belady’s Anomaly Belady 异常;
Optimal Page Replacement OPT 最佳页面置 换
OPT时所有算法中页错误率最低,且绝对没 有 Belady 异常。置换最长时间不会使用的页。
或者说选择未来不再使用/在离当前最远位置 上出现的页置换。这个使用时长看下一次该 页号出现的距离即可。就是向未来看
Least Recently Used LRU 最近最久使用 LRU选择内存中最久没有引用的页面,考虑 的是局部性原理,性能最接近 OPT,但是需 要记录页面的使用时间,硬件开销太大。就 是向后看的算法。
LRU算法如何获取多长时间没引用?两种方 法:
计数器 counter :每一个页表条目都有一个 counter,每次被引用,就把时钟信息复制到 counter。当置换时,置换时间最小的页,最 近越使用,clock 越大。
栈实现:维护一个页码栈,栈由双向链表实 现。引用页面时将该页面移动到顶部,需要 改变 6 个指针。替换时直接替换栈底部就是 LRU页。
LRU Approximation LRU近似
很 少 有 计 算 机 有 足 够 的 硬 件 支 持 真 正 的 LRU,因此许多系统为页表中的每项关联一 个引用位 reference bit,初始化为 0,当引用一 个页时,读写都可以,对应页面的引用位设 为 1。替换时替换掉引用位为 0 的(存在的话),
虽然我们不知道他有多老。
Additional reference bits附加引用位算法 在规定时间间隔内记录引用位。在规定的时 间间隔内,时钟产生一个中断并且交控制权 给 OS,OS 把每个页的引用位转移到其 8 位字 节的高位,其他位向右移 1 位,抛弃最低位。
这些 8 位寄存器包含着该页在最近 8 个周期内 的使用情况,全 0 说明没用过,全 1 说明每个 周期至少都用过 1 次,值越大越最近使用。有 最小值的页是 LRU 页,被置换。被访问时左 边最高位置 1,定期右移并且最高位补 0.
Second chance 二次机会/clock 算法 NRU 基本算法是 FIFO,选择页时,检查引用位,
如果为 0 直接置换。如果为 1,给该页第二次 机会,选择下一个 FIFO 页。当一个页获得二 次机会时,引用位清零,且将到达时间设为 当前时间。因此,获得二次机会的页在所有 其他页置换或获得二次机会之前,是不会被 置换的。
一种实现二次机会算法的方法是采用循环队 列,用一个指针表示下一次要置换哪一页。
当需要一个帧时,指针向前移动知道找到一 个引用位 0 的页,在其向前移动的过程中,它 会清楚引用位。最坏情况下所有帧都会被给 二次机会,他就会清除所有引用位之后再选 择页进行置换,此时二次机会=FIFO。
Enhanced Second chance 改进 clock 增强二次 机会
通过将引用位和脏位作为有序对来考虑,可 以改进二次机会算法。两个位有四种可能:
(0,0)无引用无修改,置换的最佳页 (0,1)无引用有修改,置换前需要写回脏页 (1,0)有引用无修改,很可能会继续用 (1,1)有引用有修改,很可能会继续用且置换前 须要写回脏页
淘汰次序(0,0)-> (0,1)-> (1,0)-> (1,1) 当页面需要被置换时,使用时钟算法,置换 (0,0)的页,在进行置换前可能要多次搜索循环 队列。改进的点子在于给未引用但是修改了 的页更高优先级,降低了 IO 数。
Macintosh使用
Counting 基于计数的置换算法
为每个页保存一个用于记录引用次数的计数 器,具体方案有两种:
Least frequently used LFU:置换计数最小的。
但是有问题:一个页可能一开始狂用,但是 后来不用了,他的计数可能很大,但是不会 被替换。解决方法是定期右移次数寄存器。
Most frequently used MFU:置换计数最大的,
因为最小次数的页可能刚调进来,还没来得 及用。
这两种很没用,实现开销很大,而且还很难 近似 OPT。
Page Buffering 页面缓冲
通过被置换页面的缓冲,有机会找回刚被置 换的页。
被置换页面的选择和处理:用 FIFO 选择置换 页,把被置换的页面放到两个链表之一。即:
如果页面无修改,将其归入空闲页链表,否 则归入已修改页面链表。
需要调入新页面时,将新页面内容读入空闲 页面链表的第一项所指的页面,然后将其删 除。
帧分配 allocation of frames
每个进程都需要最小数目的页。两种分配模 式:
平均分配算法 Equal allocation
每个如果有 100 个帧 5 个进程,每一个进程获 得 20 个帧。