CPU、内核、应用程序的关系

因精力有限,本文简单记录一下对CPU、内核、应用程序之间的关系理解。

cpu做什么的?

cpu只会顺序的执行内存中的指令,电脑启动后cpu首先开始执行bios程序的指令,然后由bios把操作系统内核指令从磁盘上放入内存,然后执行一个跳转指令开始执行内核的指令。

cpu就是一个无情的指令执行芯片,它不停的执行指令,直到遇到某个指令让它主动跳转到其他指令位置继续执行,否则它只会继续向下执行,这个特性对我们理解内核很重要。

指令跳转本身也是一条指令。

内核做什么的?

内核主要是运行编写的程序,再就是响应外设(比如键盘,硬盘,网卡)。

当bios将cpu跳转到内核的指令位置之后,内核就得到了执行。

我们的程序编译后由内核运行,那么内核就会将cpu跳转到程序的指令位置,那么内核将会失去cpu控制权,这就是用户态。

硬中断

如果程序一直死循环占着cpu,那内核会如何拿回cpu的控制权呢?大概猜测了一下,cpu硬件有定时中断信号,硬中断会导致cpu立即跳转到内存中某个提前写好的指令段执行,就像网卡会通过硬中断来打断cpu来执行其他指令一样。

这些中断处理程序是内核实现的,并且启动时已经在内存中写好的,具体位置估计与CPU有约定或者内核启动时通过CPU指令向寄存器之类的设置过。

硬中断就是这种强制打断CPU的机制,有点像回调函数,马上会让CPU跳到内核的处理函数里,那么内核就可以做任何动作了,比如把当前程序的寄存器状态暂存到内存中,然后准备好其他程序的寄存器并跳转到对应指令开始执行,这样就实现了对死循环程序的内核调度。

软中断

另外一种case是程序调用了系统调用,比如程序里面调用了connect函数,但是它可能会导致线程阻塞一下,那么内核就会调度其他线程到CPU,这个过程大概如何工作呢?

connect函数其实是内核实现的系统调用函数,其内部有一行CPU指令叫做INT,也就是软中断指令。

当CPU遇到INT指令后就会跳转到内核预设的软中断处理指令位置继续执行,和硬中断的区别在于这其实是用户态程序主动发起的中断指令,虽然开发者没有意识到,但其实代码里调用connect就会导致用户程序里面有这个INT指令。因此,用户态相当于主动把CPU控制权交回了内核,这就是用户态–>内核态的切换原理了。所以,当我们程序遇到connect这种系统调用时,内核是可以重新获得cpu控制权的,可以做任何事情,比如把这个程序的寄存器暂存一下,然后调度其他程序到cpu上运行。

程序做什么?

程序很难不调用系统函数,因为我们需要经常访问网络、磁盘、内存,只要需要从内核手里要点什么资源或者信息,其实都需要从用户指令跳回到内核指令,让内核帮我们做好事情后再跳回我们的程序指令继续执行。

但是这些过程我们感知不深,因为我们无非就是include一个头文件,链接一下glibc库而已,但其实glibc里都是对系统调用的封装函数,里面都是带软中断指令的,所以内核其实早就在我们的程序里安插好眼线了。

从汇编角度看程序会加深理解,主要是指令如何操作寄存器与内存,可以看一下阮一峰的简单介绍:

https://www.ruanyifeng.com/blog/2018/01/assembly-language-primer.html

其实猜想一下,Go协程的实现原理和内核软中断非常相似,它把Connect函数封装后,在内部主动注册Epoll异步处理,将当前协程的寄存器状态保存下来,然后通过修改寄存器令协程函数返回到Go调度器代码。

关于中断相关的简单介绍,可以看一下:https://r00tk1ts.github.io/2017/12/21/Linux%E4%B8%AD%E6%96%AD%E5%86%85%E5%B9%95/

 

如果文章帮助您解决了工作难题,您可以帮我点击屏幕上的任意广告,或者赞助少量费用来支持我的持续创作,谢谢~