操作系统之CPU虚拟化

518 阅读5分钟

这是我参与8月更文挑战的第3天,活动详情查看:8月更文挑战


没有操作系统的情况下,通常只会跑一个程序,它占用100%的CPU、内存、存储空间等一切资源。但是在现代人的操作习惯上,通常需要同时运行多个程序。在程序运行中,CPU负责执行程序中编写的指令。那么同时运行多个程序,CPU又该如何正确的执行每个程序的指令呢?

CPU虚拟化核心问题是如何用单个cpu模拟出多个cpu效果,也即CPU的复用。实现复用方式有两种,时分复用,将60秒分为两部分,A程序执行前60秒B程序执行后60秒,下一个60秒也是如此,从而达到复用效果,并且拆分的时间细小到一定程序,程序就会感觉自己独占CPU。另外一种方式是物理拆分,比如运行两个程序,通过物理隔离方式让CPU一分为二,两个程序各占用一个。

物理隔离的方式基本没有见过,能否实现也是另说,只探讨时分复用的虚拟化方式。

时分复用方案设计

大体思路我们已经达成共识并且看起来非常简单,A进程先执行,B进程在执行。

由此,问题就来了,A进程执行时,如何打断cpu执行执行A转而执行B呢?中断CPU执行并重新获取CPU执行权限可以通过硬件的中断实现,CPU的硬件可以设置中断程序,当中断时可以跳转到执行中断程序。通过中断程序,可以停止A进程的执行。但B进程如何启动呢?

来回顾下进程执行流程。

  1. 从硬盘加载进程的可执行文件到内存中。可执行文件中存储了进程的代码指令以及所需的数据。
  2. 为进程分配所需要的堆栈空间
  3. CPU从内存读取代码指令,重复执行该动作,直到进程结束。

所以,如果让B启动,还需要在中断程序中写入一段代码来加载并运行进程。现在实现了从一个进程停止到执行另外一个进程的过程。下一个问题是当B进程停止,如何让A进程恢复到原来执行的位置继续执行?所以当A被中断停止执行时,需要保存当前A进程执行到几个指令,当前的堆栈变量也要保存。然后当再次执行到A进程时,将这些变量进行复原,从而恢复执行。这个过程称为上下文切换,这段中断程序又被称为操作系统。

上下文切换过程中上下文变量保存在哪里呢?磁盘肯定不现实,因为磁盘IO和CPU运算不在一个级别,通常是存在内存中。

用户模式与内核模式

你以为这就完了吗?这才刚刚开始。AB两个进程运行。比如都进行磁盘写入,并且同一个文件。这显然会产生不可描述的数据。为此,希望能有一个中间商,从中进行协调,当某个进程在对文件进行写入时,不让其它进程进行写入。除了磁盘资源,还有网络带宽,CPU,内存等资源也希望得到控制。

为了满足以上需求,对于一些重要的操作,比如读写磁盘,读写网卡,分配内存等操作只能够让操作系统来执行。其它进程要执行这样的操作,会触发硬件中断,通过操作找到对应的操作系统处理程序进行执行(有一个提前分配好的操作对应处理程序的映射表,这个表称为陷阱表)。

收到限制的程序运行环境称为用户模式,而不受限制的运行模式称为内核模式。当然从用护模式进入到内核模式在返回,需要两次进程切换。

进程调度

当需要同时执行的进程变多时,问题变得复杂了。当有10个进程同时执行,在中断把CPU执行权限回归到操作系统,操作系统应该选择10个中的哪个进行执行呢?下面简单的介绍一些调度策略。

策略指标,判断一个策略的好坏可以通过周转时间(任务完成时间 - 任务到达CPU时间)判断。周转时间越低,说明CPU调度策略越优异。

FIFO

先进先出执行。其有点是简单。但其缺点是某些情况下周转时间长。因为如果有三个进程a,b,c,分别执行100,10,10秒。a先执行会导致b,c进程的周转时间变长。

最短任务优先

由于上面例子不足,所以设计一个优先队列,进程用时最短的先执行,用时最长的后执行。要实现这个,首先需要解决的就是如何判断任务执行时长。如何评估进程时间本人也不太清楚。