知乎有个问题:进入内核态是什么意思?这个问题暂时忘掉,让我们从另一个问题开始,一步步引出这个问题的答案。特权指令的问题在现代计算机中,同时运行着很多程序,比如你正在看这篇文章的办公软件、浏览器、QQ、微信等。这些程序一个一个的作为进程运行,每个进程都有自己的进程,为了防止大家越界,乱了规则,就必须有一个管家来管理大家,而这个管家就是操作系统。那么,无论是上述应用程序还是操作系统,它们本质上都是由CPU执行的二进制程序指令,而CPU能够执行的所有指令都在其指令集中。试想一下,如果对指令集中的所有指令一视同仁,任何程序都可以使用这些指令,会出现什么问题呢?操作系统的权限如何保证?如何管理这一堆进程?任何人都可以执行这些指令,人人平等!因此,必须将指令集中比较敏感的部分标出。这部分只能由操作系统执行,其他程序不能执行,从而捍卫操作系统的地位。!哪些指令比较敏感?与计算机资源的操作系统管理相关的指令是敏感指令。以x86架构的CPU为例,中断的打开和关闭,外部设备的输入输出,进程地址空间基地址CR3寄存器的修改,中断描述符表IDT基地址寄存器的修改,以及全局描述符表LDT基地址寄存器Modifications...这些都是敏感操作,普通应用绝对不能接触的指令:clistiinoutlidtlgdt...操作系统怎么可能执行这些敏感指令,而普通程序却不能呢?CPU在执行指令的同时,没有办法区分这条指令是属于应用程序的还是属于操作系统的。它只是一台没有感情的执行机器,只会默默地执行它……为了解决这个问题,x86CPU创造了一个特权级别,或者说CPU提供了四种工作模式,从Ring0-Ring3,每一种模式也叫一环,一共四环。Ring0是最高权限模式,可以执行所有命令。Ring3是最小权限模式,只能执行指令集中的一部分指令。也就是说,CPU发展出了四种工作模式。只有工作在Ring0模式下才能执行上述特权指令。如果你想在其他模式下执行那些指令,CPU就会抛出异常!有了CPU这个硬件技术支持,问题就好办了。我们可以让CPU在执行操作系统代码时运行在Ring0模式,而在执行普通应用程序代码时运行在Ring3模式,从而解决特权指令问题。内核地址空间但是这里我们又回到了之前的问题:CPU如何知道现在正在执行的代码是否属于操作系统?一种最简单的解决方案是将操作系统的代码放在内存的一个特殊区域,当CPU执行的指令地址来自这个区域时,工作模式切换到Ring0,离开这个区域后,它被切换到其他模式。仅此还不够,还得加一个措施:这个区域不能被应用程序访问,否则任何人都可以读写,这样好吗?因此,除了增加指令的特权级别外,内存访问也必须加上特权级别。x86架构的CPU是基于分段+分页内存管理方式组合的,所以Intel动了几个招,为不同的内存段定义了不同的访问方式,记录在段描述符中。在访问内存时,CPU会比较当前段寄存器中标记的权限和要访问的目标内存的段访问权限,满足要求才能访问,否则抛出异常!上面部分有些抽象,简单来说就是操作系统在内存中圈出一块land,把自己的代码放在这块land上,并设置访问权限:闲人不得进入,非Ring0权限禁止进入进入。之后,操作系统将它圈出的土地映射到各个进程的虚拟地址空间。这样,所有进程在查找自己的进程地址空间时都会看到:好家伙,这块区域被操作系统占用了,我们不敢写,也不敢读。操作系统圈子里的这片土地就是内核地址空间!它一般位于进程地址空间的较高区域。以32位Windows为例,在0x80000000~0xFFFFFFFF区域。我们把位于这个空间的代码称为操作系统的内核代码,有时也称为内核。应用程序代码处于活动状态的区域称为用户地址空间。另外,我们常把CPU执行内核代码的方式称为内核态,将CPU执行用户程序的方式称为用户态。CPU执行代码的过程就是不断在用户态和内核态之间徘徊的过程。进入和离开现在还有最后一个问题:如何实现内核态的进入和退出?如果没有约束,那么普通的应用程序可以跳转到内核地址空间执行,而不用只执行一条jmp指令?应用程序可以随意进出内核态,开心了内核一日游,天下不乱?而且内核所在的内存空间是有权限保护的,应用程序不能跳转到过去。进入!那我们该怎么办呢?CPU提供了一个特殊的入口,用于从用户态进入内核态。这些条目是:1.中断:当硬件设备有消息时,它会通过中断通知CPU。比如你移动了鼠标,按下了键盘,收到了一个数据包,收到了时钟的滴答声……·当中断发生时,CPU会将当前的执行上下文保存到栈中,并传递给内核以进行处理执行中断处理程序。通过中断进入内核,入口记录在中断描述符表IDT中,该表由操作系统在系统启动时安排。2、异常:当CPU在执行过程中发现一些异常情况,比如除法指令的除数为0,访问的内存地址无效,或者访问的内存地址属于特权页等,CPU就会触发一个例外。异常和中断的过程有些相似。当遇到异常时,CPU也会将执行上下文保存在栈上,并传递给内核执行中断处理程序。通过异常进入内核和中断一样,也是记录在IDT中。也是系统启动时由操作系统安排的。3、系统调用:在系统编程中,我们经常会调用操作系统提供的很多API函数,比如文件操作、内存操作、网络操作等,这些函数都是操作系统封装的应用程序编程API。接口,真正的底层实现是位于内核中的系统调用函数。应用层的API通过CPU专用指令(如sysenter/syscall)进入内核,完成相应的功能,进入内核后的入口也是操作系统事先安排好的。综上所述,让我们回答一开始就知道的问题:进入内核态是什么意思?为了控制权限,CPU引入了特权级的概念。CPU在不同的特权级别下工作。可以执行的指令和可以访问的内存区域是不同的。计算机启动之初,CPU以高权限运行,操作系统率先获得执行权限,圈出一块内存,将自己的程序代码放入其中,并设置这部分内存只具有高权限级别。访问。后来,操作系统在创建进程时,会将自己所在的内存区域映射到各个进程的地址空间中,这样所有的进程都可以看到在自己的进程空间中有一块叫做“kernel”的区域。该区域不可访问。所谓“进入内核态”就是当中断、异常、系统调用等发生时,CPU将工作模式切换到高特权模式Ring0,然后执行位于内核地址空间的代码.看完这篇文章,你明白什么叫进入内核态了吗?看不懂也没关系。我们用一张图来总结一下:怎么样?可通过以下二维码关注“科技宇宙”。转载本文请联系编程技术宇宙公众号。
