这似乎有一些奇怪,为什么上来就直接开始讲中断呢?毕竟这是一个原先从来没有接触过的概念,课上给出的原因是中断将会在未来的课程中反复的出现,所以要提前讲解好,方便以后遇到时理解,但也预示着这并不是一个简单或易理解的点。我们会通过一个小小的例子来阐明什么是中断,中断有哪几种以及大概是怎样实现的。
一个例子
不妨我们有一个1+1=2的C++程序,它长这个样子:
#include<stdio.h>
int main(){
int a=1;
int b=1;
int c=a+b;
printf("1+1=%d\n",c);
return 0
}
我们来想想有哪些条件才能运行这个程序:显而易见的,我们需要这个程序本身,其次,这个程序不能直接命令硬件,所以我们需要操作系统,当然也需要计算机硬件,包括内存,CPU,显卡显示器等等,最后,我们需要用户(要不然谁来下达运行的命令呢?),有了这些必要的条件之后, 这个程序是如何运行的起来的呢?(默认我们已经有了可执行文件)
首先,用户要告诉操作系统要执行这个程序,可以在命令行里调用(就是在终端里输个命令),可以在图形界面调用(拿鼠标点一下),或者是其他程序调用,操作系统得到命令之后,就会找到这个程序的文件信息和在硬盘中的存储位置,并进行检查(怎么检查并不是这里的重点),之后它会创建一个新的进程(下文会解释),设置CPU上下文环境,并跳到程序开始处,程序开始运行。
等等,看到这里似乎会让人有些头大,什么是进程?什么是上下文环境?什么跟什么这都是?我们知道操作系统会同时运行很多程序,比如现在你的电脑上似乎同时运行着微信和steam,所以它必须管理这些正在运行的程序,并且是十分精确地管理,如果把操作系统比作一个人的话,创建进程就类似于从书架上拿下一本书放到桌面上,下一步就是阅读这本书,也就是开始这个程序的运行。
但是等等,我开始阅读这本书了,那我刚才读的那本(之前运行的那个程序)怎么办?啥都不管就直接推到一边吗?那当然不行了,你至少得记录一下上一本书读到了第几页第几行吧,这就是设置CPU上下文环境(有些粗略但是大概可以这样理解),就是记录一下上一本书的阅读状态,然后开启下一本书的阅读。
现在CPU要开始取出这个程序的第一条指令并开始运行了,但是很不幸,它并没有在物理内存中找到这个程序,为什么呢?这不得不提到现代操作系统的机制:按需加载(其实叫做按需分页)。操作系统不会一开始就把全部程序加载进内存,那样会造成浪费(要不然你的16/32G内存的电脑怎么可能运行得了100多G的黑神话呢),所以当CPU开始想取出程序的指令时,这个程序其实只被加载了小小的一部分,并不是全部都在内存中,但是现在CPU取不出想要的指令了,那该怎么办呢?
其实写到这里时我突然发现我书架上有一本《操作系统真相还原》,翻看了一下我绝对这是一本绝世好书,所以决定阅读完这本书中断那一章节的内容再回来继续写我的这篇文档。
既然需要的指令不在内存中,那当然无法继续运行了,所以,咔嚓!中断一下,操作系统会分配一页物理内存,将代码从磁盘读入到内存中,OK了,现在内存中有代码了,可以继续执行下去了,这次的中断就结束了。接下来会进行赋值,会进行计算,直到它运行到printf,这里又出问题了,因为printf的出现意味着要进行输出操作,并且最终可能显示在屏幕上,但是等等,一个小小的程序,怎么能直接操控显示屏这样的硬件呢?
当然是不能直接操控了,所以要由操作系统来搞定这个事情,但是操作系统搞定这件事(在屏幕上显示输出内容)肯定是要花时间的,那在这段时间内,CPU总不能一直等着吧,那太浪费了,所以操作系统要把原来的那个程序给中断掉,不能让CPU一直在它身上等着,CPU很忙,有很多别的事要做。操作系统在确定这是一个合法操作之后,将字符串1+1=2交给用于处理显示的系统将其转换成像素,将像素写入显卡的存储映像区,然后显卡将像素转换成显示器可以接受的一组控制/数据信号,激发液晶屏(渲染机制、存储映像区什么的看不懂也没关系,并不重要),然后到这里,显示就结束了。
显示结束了,可是CPU知道它结束了吗?在整个过程中CPU似乎没有不断地去询问显示结束了没,前面也提到过,CPU很忙,没空过问你这里,那怎么告诉CPU呢?发信号,发一个很强势的信号,告诉CPU它该停一停然后来处理这里的事了,所以还是中断,CPU停下了手里正在干的活,然后意识到,哎,显示已经结束了。
CPU既然已经知道显示结束了,那原来的程序就可以继续运行了,printf函数就可以返回了,程序也就运行到了return 0;,整段程序也就结束了。
为了使涉及到中断的这个例子更加通俗易懂,上述表达不可避免地损失了一些清晰度与严谨性,但是没有关系,具体的细节我们会在后文中非常详细地讲解,来弥补回这里的损失。