本质是一些特权指令的执行权。
系统刚引导(启动)时,CPU处于特权模式。此时执行的程序默认有最高特权级(ring0),可以执行任意指令。
这些特权可以先赋予BIOS/UEFI引导程序,然后引导程序加载硬盘MBR或者boot管理器,把特权交给它;boot管理器进一步载入操作系统内核,赋予它ring0特权并执行它。
事实上,赋予ring0特权就等于不清空特权寄存器相应的flag(旗标?),使得这段代码可以顺利执行各种特权指令。
等操作系统加载后,它会初始化内存,设置其中一些内存区域为特权区域,禁止其它进程访问——换句话说就是配合特权指令,把计算机的其中一部分地址/数据控制起来,执行不了特权指令就没法访问它们。
一切结束后,操作系统设置寄存器旗标(flag),把CPU运行模式切换到ring3,执行用户程序(包括操作系统UI之类没有必要跑在ring0的代码)。
ring3的程序无权执行特权指令,因此无法访问到敏感区域——注意,如果设置有bug,比如允许ring3程序通过某种方式访问甚至改写ring0区域,那么这一整套保护就形同虚设了(这也是所谓“夺取系统控制权”的物理含义)。
当中断发生时,CPU自动返回ring0模式,由操作系统指定后续的中断处理过程以及权限等级。
总之,一切都由能否执行特权指令和能否访问特权区域区分:能执行特权指令、碰触特权区域的状态,那就是内核态;否则就是用户态。
而能否执行特权指令又是由某个特殊寄存器控制的,这个寄存器又只能通过特权指令控制;从用户态返回时,这个寄存器只能通过一个特殊指令从专用栈区恢复——不用说,这个栈区是低特权级执行的指令所不能碰触的。那么,这就保证了非特权应用绝对拿不到特殊指令执行权。
但这时就出现了一个新问题:如果用户需要数据存盘呢?
你看,磁盘访问指令必须是特权指令。不然用户态应用就能随便改写硬盘信息,那么把用户应用写进boot区,一重启(或者一启动被改写的系统应用),操作系统内核就被用户应用替代了(这就也是取得系统控制权)。
但如果磁盘访问指令是特权指令,用户态程序怎么存盘?
简单。操作系统允许你执行“系统调用”——把你要存盘的数据准备好,告诉操作系统你要存到哪里;操作系统审核发现你的确有这个权限,它就替你执行剩下的指令;否则就告诉你“没门!你可没资格碰那个文件!”
系统调用的执行我记得也和中断有关(当然不同CPU可能有不同实现),总之就是一定会先经特权程序审核、然后才会决定是否执行。
类似的,过去有个半虚拟内核叫XEN,它的原理就是修改内核,把客户机内核的很多调用换成对应的系统调用——于是,你就可以在物理CPU上跑另一个操作系统的内核、却又必须受宿主操作系统管理了。
这个东西后来进一步发展,就是近年来CPU直接支持的“虚拟化指令”。这个场景下,宿主操作系统内核运行于-1环,客户内核运行于0环;0环的特权指令并不会直接执行,而是要先经-1环审核、转换,这才能继续执行。
总之,归根结底,情况就是:
1、CPU有一个状态指示寄存器,这个寄存器可以控制CPU当前工作于ring0还是ring1、2、3
2、CPU指令分几个大类,其中一部分指令可以在任意特权级工作,另一些必须在ring0才能工作;其中,改变所在ring的指令必须受到特殊管控——因为改变所在ring就是所谓的内核态/用户态切换。