由内存分页看CPU系统界面

这个东西很复杂,似乎也不好找到「恰好足够」的资料,要么讲得太浅,要么就只能去翻硬件文档。但是仅仅为了一个要求模糊而且并不好玩的作业做到那种地步确实是有点没意思,不过还好它解答了我的疑问。下面这些内容目的只是给我足够的底气去处理作业中并不明确的部分。更加详细或者粗略的内容在网上有很多介绍,至少短期内不会出现在这里。

原本写了一堆东西去讨论这作业的意义,挺没意思的,连带着没用的都删了。课是挺好,怎么到人手里结果云泥之别呢。只能祝下一级学学妹们加油。

分页

现代CPU基本上只支持分页式的内存管理方式了。具体为将内存划分为大小相同的块,对于一个“进程”的虚拟地址,维护其(连续的)每块虚拟地址到(可能并不连续)物理地址的转换表。这张表显然足够大,就也存在内存里。

那么问题就是,在这整个过程中,到底从哪一部分开始是操作系统的事情。事实上,虚拟地址到物理地址的转换以及后续的访问、读取/写入是硬件的工作,内存分配、页表存储是操作系统的事。连接这两者的桥梁是页表格式的约定和一个特殊的寄存器。操作系统在调度进程运行前,将页表的地址送给特殊寄存器,由硬件直接按约定好的格式读取页表完成后续工作。

这种办法不是绝对的,至少X86架构是这么做的。段式存储策略类似,其存有特殊寄存器作为偏移量,也由系统先将偏移量装入寄存器再把控制权交出去。

问题

不过这里似乎有个套娃问题。我的系统代码也是运行在虚拟地址下的,系统从何知晓物理地址的事情。实际上,CPU也真的不允许系统瞎搞。在这里,办法是「提前」将页表的地址事先映射到虚拟内存地址上。……操作系统让CPU从页表里翻译到页表的物理地址,感觉怪怪的。「提前」是指这个过程一般在更底层的代码完成,例如boot阶段,因为系统无法自行访问到自己的页表,套娃是不存在的。

异常处理

不过系统是通过什么知道该做什么的,既然内存由系统分配,控制权交给了软件,那么系统是如何在必要时为软件分配内存。这其实是别的东西。

例如异常处理。常见的段错误、除0错误。这里又是一波CPU和操作系统的约定。CPU会在出现异常情况时,将异常原因装入寄存器,将当前运行到的代码地址保存,跳转到事先约定好的异常处理代码地址,从而启动由系统自定义的异常处理流程。而内存的分配一般也是通过这种形式完成。定义一种特殊的异常syscall,软件把参数传给寄存器后去触发中断把控制权交出去,系统分配完内存再ret回去回归到软件的正常运行流程。不过现代CPU似乎用了更快速的方式。

这里还挺有意思的,这种异常处理方式注定了它异常处理过程可能再发生异常。然而也不能无限套娃。一般情况下,第一次异常将进入异常处理流程,异常处理触发异常将进入二次异常处理,一般二次异常处理操作系统会初始化软件,因为先前的运行状态无法再维持了(似乎?)。二次异常过程如果再次发生异常,硬件将直接重启。

中断稍微不太一样,没有这个问题。

对于MIPS来说,它存在一个较短的中断位,中断发生时,在指定的寄存器的位置上设置中断信号和中断号,而后CPU会跳转到服务程序,由服务程序进一步定义不同中断的处理优先级,调起不同的处理程序。这一部分内容与协处理器相关,同时存在多条涉及协处理器的“特权指令”。

内核态/用户态

在这之中总会感觉缺了点什么。如果说操作系统也只是一段代码,那么操作系统与普通软件的区别在哪,把操作系统分配内存的代码拿过来不就不需要它了,系统如何承担机器和应用之间的桥梁。

实际上系统一般是将应用和机器完全隔离开了。这涉及到了内核态/用户态的问题。操作系统运行在内核态,只有内核态可以访问关键资源,所以操作系统代码与一般软件代码有地位上的差别。但这还不够,为什么操作系统可以运行在内核态。其实这是CPU提供的功能。CPU所提供的分级保护域概念,使得操作系统在占据高特权后能够自由决定接下来运行代码的权限。这种机制诞生了所谓特权指令,即只有在高权限下才能运行的指令,一般用于访问关键资源。所以,用户软件必须通过中断,让系统来为其准备需要的资源。


【DELETED】


由内存分页看CPU系统界面
https://blog.chenc.me/2020/05/04/paging-introduction/
作者
CC
发布于
2020年5月4日
许可协议