
CPU 指令集:是CPU实现软件指挥硬件执行的媒介,具体来说每一条汇编语句都对应了一条CPU 指令,而非常非常多的CPU 指令在一起,可以组成一个、甚至多个集合,指令的集合叫CPU 指令集。
CPU指令集有权限分级,大家试想,CPU 指令集可以直接操作硬件的,要是因为指令操作的不规范,造成的错误会影响整个计算机系统的。好比你写程序,因为对硬件操作不熟悉,导致操作系统内核、及其他所有正在运行的程序,都可能会因为操作失误而受到不可挽回的错误,最后只能重启计算机才行。
对开发人员来说是个艰巨的任务,还会增加负担,同时开发人员在这方面也不被信任,所以操作系统内核直接屏蔽开发人员对硬件操作的可能,都不让你碰到这些CPU 指令集。
针对上面的需求,硬件设备商直接提供硬件级别的支持,做法就是对CPU 指令集|设置了权限,不同级别权限能使用的CPU 指令集是有限的,以Inter CPU为例,Inter把 CPu 指令集操作的权限由高到低划为4级:
要知道的是,Linux系统仅采用ring0和ring 3这2个权限。CPU中有一个标志字段,标志着线程的运行状态,用户态为3,内核态为0。
ring 0被叫做内核态,完全在操作系统内核中运行。
执行内核空间的代码,具有ring 0保护级别,有对硬件的所有操作权限,可以执行所有(CPU指令集,访问任意地址的内存,在内核模式下的任何异常都是灾难性的,将会导致整台机器停机ring3被叫做⽤⼾态,在应⽤程序中运⾏
在用户模式下,具有ring 3保护级别,代码没有对硬件的直接控制权限,也不能直接访问地址的内存,程序是通过调用系统接口(System CallAPls)来达到访问硬件和内存,在这种保护模式下,即时程序发生崩溃也是可以恢复的,在电脑上大部分程序都是在,用户模式下运行的低权限的资源范围较小,高权限的资源范围更大,所以用户态与内核态的概念就是CPU指令集权限的区别。 我们通过指令集权限区分用户态和内核态,还限制了内存资源的使用,操作系统为用户态与内核态划分了两块内存空间,给它们对应的指令集使用。
在内存资源上的使用,操作系统对用户态与内核态也做了限制,每个进程创建都会分配虚拟空间地址,以Linux32位操作系统为例,它的寻址空间范围是4G(2的32次方)﹐而操作系统会把虚拟控制地址划分为两部分,一部分为内核空间,另一部分为用户空间,高位的1G(从虚拟地址OxC0000000到OxFFFFFFFF)由内核使用,而低位的 3G(从虚拟地址Ox00000000到OxBFFFFFFF)由各个进程使用。

3G-4G部分大家是共享的(指所有进程的内核态逻辑地址是共享同一块内存地址),是内核态的地址空间,这里存放在整个内核的代码和所有的内核模块,以及内核所维护的数据。
什么情况会导致⽤⼾态到内核态切换??
操作系统提供了中断指令
int0x80来主动进⼊内核,这是⽤⼾程序发起的调⽤访问内核代码的唯⼀⽅式。调⽤系统函数时会通过内联汇编代码插⼊int0x80的中断指令,内核接收到int 0x80中断后,查询中断处理函数地址,随后进⼊系统调⽤。
异常:当CPU在执行用户态的进程时,发生了一些没有预知的异常,这时当前运行进程会切换到处理此异常的内核相关进程中,也就是切换到了内核态,如缺页异常
中断:当CPU在执行用户态的进程时,外围设备完成用户请求的操作后,会向CPU发出相应的中断信号,这时CPU 会暂停执行下一条即将要执行的指令,转到与中断信号对应的处理程序去执行,也就是切换到了内核态。如硬盘读写操作完成,系统会切换到硬盘读写的中断处理程序中执行后边的操作等。
切换时CPU需要做什么??
当某个进程中要读写IO,必然会用到ring 0级别的CPU 指令集。而此时CPU的指令集操作权限只有ring 3,为了可以操作ring 0级别的CPU 指令集,CPU切换指令集操作权限级别为ring 0(可称之为提权),CPU再执行相应的ring 0级别的CPU指令集(内核代码)。
代码发生提权时,CPU是需要切换栈的!!前面我们提到过,内核有自己的内核栈。CPU切换栈是需要栈段描述符(ss寄存器)和栈顶指针(esp寄存器),这两个值从哪里来?。
从上述流程可以看出⽤⼾态切换到内核态的时候,会牵扯到⽤⼾态现场信息的保存以及恢复,还要进⾏⼀系列的安全检查,还是⽐较耗费资源的。