前述
对资源的虚拟化是操作系统的重要技术,而资源虚拟化有两种,一种是CPU虚拟化,一种是内存虚拟化。
假设一个计算机只有一个 CPU,虚拟化要做的就是将这个 CPU 虚拟成多个虚拟 CPU 并分给每一个进程使用,因此,每个应用都以为自己在独占 CPU,但实际上只有一个 CPU。这就是操作系统创造的假象——CPU虚拟化。
如何实现CPU虚拟化
CPU虚拟化的基本思想是让一个进程只运行一个时间片,然后切换到其他进程运行,如此轮转,通过以这种方式时分共享(time sharing)CPU,操作系统就提供了存在多个虚拟 CPU 的假象。 然而,实现时分共享CPU技术需要一些机制和策略。机制包括受限直接运行,上下文切换等,而策略是指在操作系统内做出某种决定的算法,比如调度策略。
实现时分共享CPU的机制
-
受限直接执行
操作系统通过时分共享CPU实现了CPU虚拟化,构建这样的虚拟化机制需要做到高效、可控。 先说高效,要想让程序高效运行,那操作系统就不能一直介入,这样是做不到快速的。
再说可控,既然操作系统不能一直介入,那完全放开管控可行吗,这虽然做到了高效,却使程序变得不可控,比如发起磁盘IO请求这种资源请求操作如果操作系统不介入的话就会引起很大问题。
操作系统为了以高性能的方式虚拟化CPU,同时保持对系统的控制,使用了一种受限的直接执行的技术。直接运行就是直接在CPU上不受管控地运行程序,那么可想而知受限的直接执行就是让想运行的程序在 CPU 上运行,但首先确保设置好硬件,以便在没有操作系统帮助的情况下限制进程可以执行的操作。- 控制权转移
受限直接运行的机制是在需要操作系统介入时让控制权转移。具体的话就是硬件通过提供不同的处理器模式来协助操作系统。在用户模式(user mode)下,应用程序不能完全访问硬件资源。在内核模式(kernel mode)下,操作系统可以访问机器的全部资源。这是一种受保护的控制权转移。以用户模式运行的应用程序通常不能发起对磁盘的 I/O 请求,不能访问任何物理内存页或在网络上发送数据包,而在内核模式下,操作系统可以完全访问系统的硬件。 - 陷阱(Trap)
那么用户模式和内核模式如何切换呢?答案是陷阱(Trap),程序在发起系统调用时 [通常通过一个称为陷阱(trap)的特殊硬件指令],硬件将控制转移到预先指定的陷阱处,同时将特权级别提升到内核模式、。当操作系统完成请求的服务时,它通过特殊的陷阱返回(return-from-trap)指令将控制权交还给用户,即陷入(trap)内核和从陷阱返回(return-from-trap)到用户模式程序。
- 控制权转移
-
进程切换
从上面我们知道用户模式和内核模式的切换是通过使用Trap 的方式,那么进程切换又是怎么实现的呢,在了解进程切换时,先了解程序如何转化为进程:-
程序如何转化为进程
程序最初以某种可执行格式驻留在磁盘上。操作系统运行程序必须做的第一件事是将代码和所有静 态数据(例如初始化变量)加载(load)到内存中。将代码和静态数据加载到内存后,操作系统在运行 此进程之前还需要执行其他一些操作,比如为程序的运行时栈分配内存,执行一些初始化任务,最后 启动程序,程序变成一个进程,OS 将 CPU 的控制权转移到新创建的进程中,从而程序开始执行。
-
中断
时钟设备可以编码为每隔几毫秒产生一次中断,而计算机本身就是一种时钟设备,操作系统正是通过使用中断获得 CPU 控制权,产生中断时,当前正在运行的进程停止,操作系统中预先配置的中断处理程序(interrupt handler)会运行。此时,操作系统重新获得 CPU 的控制权,至于是继续运行当前进程还是切换到另一个进程是由调度程序决定。
如果决定进行上下文切换,操作系统就会为当前正在执行的进程保存一些寄存器的值,并为即将执行的进程恢复一些寄存器的值。这样,操作系统就可以确保最后执行从陷阱返回指令时,不是返回到之前运行的进程,而是继续执行另一个进程。
-
实现时分共享CPU的策略
操作系统调度程序采用的上层策略就是指进程的调度策略,所谓的调度策略是指操作系统如何选择和管理在CPU上运行的进程的规则和算法,旨在有效地分配CPU资源,以提高系统的响应速度和吞吐量。
-
调度指标:
为了能够衡量不同调度策略需要有一些指标,在这里给出两个指标:- 周转时间:任务的周转时间定义为任务完成时间减去任务到达系统的时间
- 响应时间:响应时间定义为从任务到达系统到首次运行的时间
-
策略算法:
- 先进先出
- 最短任务优先 SJF
- 最短完成时间优先
- 轮转
- 多级反馈队列
-
多级反馈队列: 多级反馈队列是构建了优先级依次递减的多个队列,通过关注工作进程运行的表现将其移入对应的优先级,以这种方式可以同时满足各种工作的需求。这种利用反馈信息决定某个工作优先级的调度方式就叫多级反馈队列
-
按照比例份额调度:
调度程序的最终目标,是确保每个工作获得一定比例的 CPU 时间,而不是优化周转时间和响应时间。在这里介绍一些按照比例份额分配的调度策略:- 彩票调度:彩票数(ticket)代表了进程(或用户或其他)占有某个资源的份额。一个进程拥有的彩票数占总彩票数的百分比,就是它占有资源的份额
- 步长调度:当需要进行调度时,选择目前拥有最小行程值的进程,并且在运行之后将该进程的行程值增加一个步长
这两者没有作为 CPU 调度程序被广泛使用。一个原因是这两种方式都不能很好地适合 I/O;另一个原因是其中最难的票数分配问题并没有确定的解决方式
-
多处理器调度
多处理器就是多CPU,现代操作系统都是多核CPU- 缓存一致性问题
在多处理器系统中,由于每个CPU可能有自己的缓存,当多个处理器同时访问共享数据时,可能会导致数据不一致的情况
基本解决方案就是:通过监控内存访问,硬件可以保证获得正确的数据,并保证共享内存的唯一性。在基于总线的系统中,一种方式是使用总线窥探;每个缓存都通过监听链接所有缓存和内存的总线,来发现内存访问。如果 CPU 发现对它放在缓存中的数据的更新,会作废(invalidate)本地副本(从缓存中移除),或更新(update)它(修改为新值)。
- 缓存亲和度
一个进程在某个 CPU 上运行时,会在该 CPU 的缓存中维护许多状态。下次该进程在相同 CPU 上运行时,由于缓存中的数据而执行得更快 单队列调度:
单队列多处理器调度(Single Queue Multiprocessor Scheduling,SQMS)是简单地复用单处理器调度的基本架构,将所有需要调度的工作放入一个单独的队列中
多队列调度:
多队列多处理器调度(Multi-Queue Multiprocessor Scheduling,MQMS),在 MQMS 中,基本调度框架包含多个调度队列,每个队列可以使用不同的调度规则,比如轮转或其他任何可能的算法。当一个工作进入系统后,系统会依照一些启发性规则(如随机或选择较空的队列)将其放入某个调度队列。这样一来,每个 CPU 调度之间相互独立,就避免了单队列的方式中由于数据共享及同步带来的问题
- 缓存一致性问题