用户态:用户代码和数据被访问或者执行的时候,所处的状态
内核态:执行OS的代码和数据时,计算机所处的状态就叫内核态
两者最大的区别就在于权限,以printf函数为例,该函数的本质其实是向加载到内存即内核中的stdout文件写入数据,此时我们要从用户层进入内核,必须要从用户态转变成内核态,权限会扩大!下面就以printf函数的运行为例,来介绍用户态和内核态
目录
一、进程为什么能使用OS的代码??
进程运行自己的代码,只需要循着 用户区虚拟地址——》页表——》物理内存 这条路线就能找到对应的代码和数据,但是进程是如何找到OS的代码呢?
介绍虚拟地址空间的时候有这么一张图,用户代码放在代码区,也就是用户空间的那3G中,最上面还有1G的内核空间,没错,这里就存放着OS的代码!!
访问内核空间的方式和访问用户空间一样,也是通过页表访问,但是所有的进程使用的都是同一份内核代码,并不会因为进程的代码发生变化 ——》 内核空间的页表也就只有一份!!
所以我们在进行系统调用的时候,实际上是走的内核空间 ——》 内核空间的页表 ——》物理内存 这条路线
注意:像磁盘驱动函数,类似于这种我们看不到的函数,都是OS的代码,OS的代码早在开机运行的时候就被加载到内存了
二、用户态和内核态的相互切换
下面就以具体例子来分析,我们用户层要运行的代码如下
//test.c
int main()
{
printf("hello,world!\n");
return 0;
}
当上述代码被加载到内存时,对应的进程就会被创建。现在把进程加入到CPU运行队列中,CPU中有一个寄存器CR3,这个寄存器记录着当前进程的状态。CR3 = 0,表示当前状态是内核态;CR3 = 3,表示当前状态是用户态。
此时就会循着用户空间中的地址找到进程A的代码,运行prinft语句,此时是访问用户层的代码,处在用户态(CR3 = 3)
由于printf实际上是向stdout文件写入数据,所以必须要使用系统调用进入内核,此时需要切换成内核态(CR3 = 0),然后去执行OS的代码,至于如何找到内核代码,第一部分已经说过了
等到执行完内核代码,即向stdout文件中写入数据,就会返回到用户态(CR3 = 3),因为原本的代码还没有执行完,即继续执行printf 的下一句return 0