浅谈Linux用户态和内核态

为什么要分用户态和内核态?

  在 CPU 的所有指令中,有一些指令是非常危险的,如果错用,将导致整个系统崩溃。比如:清内存、设置时钟等。如果所有的程序都能使用这些指令,那么你的系统一天死机 n 回就不足为奇了。所以,CPU 将指令分为特权指令和非特权指令,对于那些危险的指令,只允许操作系统及其相关模块使用,普通的应用程序只能使用那些不会造成灾难的指令。Intel 的 CPU 将特权级别分为4个级别:RING0、RING1、RING2、RING3。
  Linux的内核是一个有机的整体。每一个用户进程运行时都好像有一份内核的拷贝,每当用户进程使用系统调用时,都自动地将运行模式从用户级转为内核级,此时进程在内核的地址空间中运行。
  当一个任务(进程)执行系统调用而陷入内核代码中执行时,我们就称进程处于内核运行态(或简称为内核态)。此时处理器处于特权级最高的(0级)内核代码中执行。当进程处于内核态时,执行的内核代码会使用当前进程的内核栈。每个进程都有自己的内核栈。当进程在执行用户自己的代码时,则称其处于用户运行态(用户态)。即此时处理器在特权级最低的(3级)用户代码中运行。当正在执行用户程序而突然被中断程序中断时,此时用户程序也可以象征性地称为处于进程的内核态。因为中断处理程序将使用当前进程的内核栈。这与处于内核态的进程的状态有些类似。
  内核态与用户态是操作系统的两种运行级别,跟 Intel CPU 没有必然的联系, 如上所提到的 Intel CPU 提供 Ring0~Ring3 四种级别的运行模式,Ring0 级别最高,Ring3 最低。Linux 使用了 Ring3 级别运行用户态,Ring0 作为 内核态,没有使用 Ring1 和 Ring2。Ring3 状态不能访问 Ring0 的地址空间,包括代码和数据。Linux 进程的 4GB 地址空间,3G~4G 部分大家是共享的,是内核态的地址空间,这里存放在整个内核的代码和所有的内核模块,以及内核所维护的数据。用户运行一个程序,该程序所创建的进程开始是运 行在用户态的,如果要执行文件操作,网络数据发送等操作,必须通过 write,send 等系统调用,这些系统调用会调用内核中的代码来完成操作,这时必须切换到 Ring0,然后进入 3GB~4GB 中的内核地址空间去执行这些代码完成操作,完成后切换回 Ring3,回到用户态。这样,用户态的程序就不能随意操作内核地址空间,具有一定的安全保护作用。
  
  处理器总处于以下状态中的一种:
  
  1、内核态,运行于进程上下文,内核代表进程运行于内核空间;
  2、内核态,运行于中断上下文,内核代表硬件运行于内核空间;
  3、用户态,运行于用户空间。

用户态和内核态的转换

  什么情况下会发生从用户态到内核态的切换,一般存在以下三种情况:
  
1、系统调用

  这是用户态进程主动要求切换到内核态的一种方式,用户态进程通过系统调用申请使用操作系统提供的服务程序完成工作,比如前例中fork()实际上就是执行了一个创建新进程的系统调用。而系统调用的机制其核心还是使用了操作系统为用户特别开放的一个中断来实现,例如Linux的int 80h中断。

2、异常事件

  当CPU在执行运行在用户态下的程序时,发生了某些事先不可知的异常,这时会触发由当前运行进程切换到处理此异常的内核相关程序中,也就转到了内核态,比如缺页异常。

3、外围设备的中断

  当外围设备完成用户请求的操作后,会向CPU发出相应的中断信号,这时CPU会暂停执行下一条即将要执行的指令,转而去执行与中断信号对应的处理程序,如果先前执行的指令是用户态下的程序,那么这个转换的过程自然也就发生了由用户态到内核态的切换。比如硬盘读写操作完成,系统会切换到硬盘读写的中断处理程序中执行后续操作等。

  这3种方式是系统在运行时由用户态转到内核态的最主要方式,其中系统调用可以认为是用户进程主动发起的,异常和外围设备中断则是被动的。

  注意:系统调用的本质其实也是中断,相对于外围设备的硬中断,这种中断称为软中断,这是操作系统为用户特别开放的一种中断,如Linux int 80h中断。所以,从触发方式和效果上来看,这三种切换方式是完全一样的,都相当于是执行了一个中断响应的过程。但是从触发的对象来看,系统调用是进程主动请求切换的,而异常和硬中断则是被动的。


参考:
http://blog.csdn.net/liuyueyue0921/article/details/48225533
http://www.cnblogs.com/bakari/p/5520860.html

你可能感兴趣的:(Linux基础)