如何高效管理CPU:并发和进程
# 4. 如何高效管理CPU:并发和进程
接下来我们就要开始学习操作系统最核心的图像,多进程图像。我们之前讲过,多进程图像是操作系统的核心图像之一,明白了它,就明白了操作系统的一大部分,编写出操作系统也就指日可待了。
那么,为什么会有多进程图像?我们前面说过,操作系统是帮我们管理计算机硬件的,例如CPU,内存,显卡和外设等; CPU无疑是最核心的硬件了,操作系统的首要目标就是管理CPU,就是在管理CPU的时候,引出了多进程图像,这是一个重要的概念。通过多进程,操作系统管理明白了CPU,CPU管理好了,其他硬件自然而然地就带动起来了,所以多进程图像是操作系统的核心图像
# 管理CPU:从使用做起
既然多进程是因为管理CPU才引出来的,所以我们首先就要明白操作系统怎么管理CPU。我们从简单的例子出发:先使用CPU。
就好比管理一间课室,作为一个管理者,得充分利用教室。那么首先得用起来,才谈得上是管理。如果一间课室都没人使用,这个教室是可有可无的,谈不上管理。
如何使用CPU呢?首先我们得明白CPU的工作原理,我们之前讲过很多次,从图灵机开始一直到现代计算机,CPU工作原理就是4个字:取指执行。 这是一个最基础的知识。
在冯诺依曼结构里,我们把一段程序放到内存里,然后设置CPU的初值(例如PC寄存器),那么CPU就会自动的执行程序了,例如我们执行这段程序:(左侧是指令的地址,右边是汇编指令)
50:mov ax, [100]
51: mov bx, [101]
52: add ax, bx
.....
100: 0
101:1
2
3
4
5
6
这个例子中,我们设置PC=50,那么CPU就开始工作了。
CPU会首先发出一条取指指令,要取出内存中地址为50的地址,CPU会将50放到地址总线上;
内存得到取指的指令后,就会将内存里地址为50的内容放到数据总线上,传回给CPU
CPU得到指令后,就开始执行。这个指令是 mov ax, [100]
因此就会将地址100处的内容取出来,赋值给ax。
CPU取指后,PC是自动累加的,当执行完指令后,就开始取指并执行下一条指令。不断取指执行,这就是CPU的工作原理。
CPU就好比一个厨师,指令就好比菜谱,厨师看到菜谱的每个任务后,就开始执行。
因此,设置一个PC的初始地址后,CPU就开始工作了。这就是使用CPU的方法,也是管理CPU的最简单的、最直观的方法。
# IO指令对CPU的影响
这种最简单的、最直观的使用CPU的方法,有没什么问题?
我们用一个程序举例,来引出这个问题
int main(int argc, char* argv[])
{
int i , to, *fp, sum = 0;
to = atoi(argv[1]);
for(i=1; i<=to; i++)
{
sum = sum + i;
fprintf(fp,“%d”, sum);
}
}
2
3
4
5
6
7
8
9
10
我们可以让PC寄存器的值设置为这段程序的初始地址,CPU就开始工作了。其中fprintf是一个IO指令;我们测试在大量循环的情况下,IO指令和计算指令的执行速度有什么不同
没有IO指令的情况下,都是计算指令的时候,执行10^7^次循环耗时0.015秒;那么一次循环耗时大约 0.015秒/10^7 ^
有IO指令的情况下,1000次循环已经要0.859秒了。那么一次循环耗时大约 0.859秒 / 10^3^
我们可以简单的做个比值 ,用 ( 0.015秒/10^7 ^) / (0.859秒 / 10^3 ^),也就是两次循环的耗时比值
结果5.7×10^5^,我们可以近似看成10^6^ : 1。
这个数字代表了什么含义?我们可以理解为,执行一次IO指令所花的时间,可以执行10^6^ 个计算指令
通过这个例子,我们可以知道,IO指令的执行特别慢。CPU是在电路上工作的,而IO的话需要访问磁盘,磁盘是机械设备,机械设备当然比电子设备慢多了。
那么IO指令对CPU的效率有什么影响呢?我们再来举一个例子。假设一个程序,有10^6^个计算指令,但有1条IO指令。让CPU执行到IO指令的时候,就得停止工作,等待IO的完成。
那么也就是说,执行这段程序的时候,CPU只有50%的时间在工作,效率为50%。
可能有人觉得能不能跳过这条IO指令,往下执行?当然是不可以的。有可能后续的程序,需要依赖这个IO指令得出来的数据。
这还是在IO指令的数量占比很低的情况下,CPU的效率都已经这么低了,如果一个程序有很多的IO指令,那么CPU的工作效率可以说接近0,大部分时间都在等待IO。
就好比我们管理一间课室,我们可以很简单的就让它使用起来,但是如果它利用率低的话,如果一年才使用一次科室,那么它的效率是很低的。
# 如何提高CPU的效率
有没办法提高CPU的效率呢?我们可以举个生活的例子。就好比我们要烧水煮茶,我们首先得烧杯水,等水烧开是需要时间的,那我们肯定不会在一旁等水烧开,而是开始准备茶具等。等水烧开后,水壶会有提醒(类似计算机中的中断),再回去倒水。
同理,如果我们在遇到 IO指令的情况下,能让CPU去执行其他程序,CPU不就忙碌起来了?CPU的效率也有很大的提升
也就是说,在内存里有多个程序,相互之间切换调度,CPU就忙碌起来了。在讲操作系统的历史的时候,我们说过这个概念叫多道程序交替执行。这么一个很简单的思想,就大幅度提高了CPU的工作效率
我们来看多道程序下,CPU的使用效率。举一个具体的例子:
第一部分是单道程序的执行示意图,从左往右执行,DEV1和DEV2为IO设备,执行顺序下面的数字为时间,执行过程为 A 程序使用CPU → A程序使用DEV1 → A程序使用CPU……………………当A程序执行完了,下面就是执行B程序
而如果是多道程序的话,在一开始的时候,B设备要用IO设备,因此CPU交给A程序使用;当A程序执行到DEV1设备的时候,CPU交由B程序执行……以此类推,最后我们可以算出CPU和IO的利用率:
单道程序 | 多道程序 | |
---|---|---|
CPU利用率 | 40/80 = 50% | 40/45=89% |
DEV1利用率 | 15/80=18.75% | 15/45=33% |
DEV2利用率 | 25/80=31.25% | 25/45=56% |
我们可以看到CPU的利用率提高到了89%,并且也带动了各个外设的使用。所以多道程序同时在内存里,同时出发,交替执行,这才是CPU应该工作的样子
我们称这种方法为并发。这个概念非常重要,如果要问如何管理好CPU,答案就是并发。并发的并是同时,发是出发,所以同时出发,交替执行。
# 如何并发
那如何做到并发,就是提高CPU效率的核心了。实际上切换到另一个程序也很简单,修改PC的值就可以了。在遇到IO指令的时候,就切换PC。
但是,只修改PC寄存器就可以了吗?肯定是不行的。如果程序1用到了寄存器ax和bx,程序2也用到了并修改,那么程序1的执行结果就会出错。我们在切换的时候要保存程序1的用到的寄存器的值,
在汇编里,讲子程序的时候讲到了这一点,也叫保护现场。
举个生活的例子,比如我们正在看书,看到第10页,突然有人敲门,我们就会暂时放下手中的事情,去敲门;但处理完开门后,回来继续看书的话,肯定不是从头开始看,而是继续从第10页开始看,看的时候脑海还会回想之前场景
所以一个程序在执行的时候,切出去的时候需要记录很多信息,以备将来能返回。实际上记录的就是程序切出去的瞬间,其寄存器等信息。由此可以看到,运行中的程序和静态的程序,是不一样的。如果是一个未运行的静态程序,完全不用记录这些信息
就好比一本书,如果都没有看,那么什么都不用记录;但如果看到一半,临时放下的话,得记住看到哪里、相关的场景是什么养。
那么进程就描述了这种运行中的程序和静态程序的不一样,那么不一样主要体现为,需要用一种数据结构来存放、记录程序运行时的样子。我们把这个数据结构叫做PCB(Process Control Block)。
# 进程的概念
既然运行中的程序,和静态的程序非常不一样,我们把运行中的程序,叫做进程。进程的概念非常重要。
# 小结
本堂课我们首先讲了操作系统要管理好CPU,那么首先得使用CPU;但只执行一个程序的话,CPU的效率低,为提高效率,我们就需要交替执行多个程序,为了完成程序的切换,我们需要记录程序执行的瞬间;我们还引出了一个概念:进程。
反过来说,操作系统使用CPU,就是启动一个进程让CPU执行;为了更好的管理CPU,操作系统就启动多个进程,并让进程交替执行,这就是为什么要有多进程图像的原因,以及多进程图像的基本轮廓