linux用户态和内核态切换理解

linux用户态和内核态切换理解

 

Linux使用了Ring3级别运行用户态,Ring0做为内核态,没有使用Ring1和Ring2。Ring3状态不能访问Ring0的地址空间,包括代码和数据。Linux进程的4GB地址空间,3G-4G部分你们是共享的,是内核态的地址空间,这里存放在整个内核的代码和全部的内核模块,以及内核所维护的数据。用户运行一个程序,该程序所建立的进程开始是运行在用户态的,若是要执行文件操做,网络数据发送等操做,必须经过write,send等系统调用,这些系统调用会调用内核中的代码来完成操做,这时,必须切换到Ring0,而后进入3GB-4GB中的内核地址空间去执行这些代码完成操做,完成后,切换回Ring3,回到用户态。这样,用户态的程序就不能随意操做内核地址空间,具备必定的安全保护做用。 

保护模式,经过内存页表操做等机制,保证进程间的地址空间不会互相冲突,一个进程的操做不会修改另外一个进程的地址空间中的数据。在内核态下,CPU可执行任何指令,在用户态下CPU只能执行非特权指令。当CPU处于内核态,能够随意进入用户态;而当CPU处于用户态,只能经过中断的方式进入内核态。通常程序一开始都是运行于用户态,当程序须要使用系统资源时,就必须经过调用软中断进入内核态. html

 

1. 用户态和内核态的概念区别 linux

究竟什么是用户态,什么是内核态,这两个基本概念之前一直理解得不是很清楚,根本缘由我的以为是在于由于大部分时候咱们在写程序时关注的重点和着眼的角度放在了实现的功能和代码的逻辑性上,先看一个例子: 安全

1)例子 网络

void testfork(){
  if(0 = = fork()){
    printf(“create new process success!\n”);
  }
  printf(“testfork ok\n”);
}




这段代码很简单,从功能的角度来看,就是实际执行了一个fork(),生成一个新的进程,从逻辑的角度看,就是判断了若是fork()返回的是0则打印相关语句,而后函数最后再打印一句表示执行完整个testfork()函数。代码的执行逻辑和功能上看就是如此简单,一共四行代码,从上到下一句一句执行而已,彻底看不出来哪里有体现出用户态和进程态的概念。 数据结构

若是说前面两种是静态观察的角度看的话,咱们还能够从动态的角度来看这段代码,即它被转换成CPU执行的指令后加载执行的过程,这时这段程序就是一个动态执行的指令序列。而究竟加载了哪些代码,如何加载就是和操做系统密切相关了。 架构

 

2)特权级 函数

熟悉Unix/Linux系统的人都知道,fork的工做其实是以系统调用的方式完成相应功能的,具体的工做是由sys_fork负责实施。其实不管是否是Unix或者Linux,对于任何操做系统来讲,建立一个新的进程都是属于核心功能,由于它要作不少底层细致地工做,消耗系统的物理资源,好比分配物理内存,从父进程拷贝相关信息,拷贝设置页目录页表等等,这些显然不能随便让哪一个程序就能去作,因而就天然引出特权级别的概念,显然,最关键性的权力必须由高特权级的程序来执行,这样才能够作到集中管理,减小有限资源的访问和使用冲突。 spa

特权级显然是很是有效的管理和控制程序执行的手段,所以在硬件上对特权级作了不少支持,就Intel x86架构的CPU来讲一共有0~3四个特权级,0级最高,3级最低,硬件上在执行每条指令时都会对指令所具备的特权级作相应的检查,相关的概念有CPL、DPL和RPL,这里再也不过多阐述。硬件已经提供了一套特权级使用的相关机制,软件天然就是好好利用的问题,这属于操做系统要作的事情,对于Unix/Linux来讲,只使用了0级特权级和3级特权级。也就是说在Unix/Linux系统中,一条工做在0级特权级的指令具备了CPU能提供的最高权力,而一条工做在3级特权级的指令具备CPU提供的最低或者说最基本权力。 操作系统

 

3)用户态和内核态 code

如今咱们从特权级的调度来理解用户态和内核态就比较好理解了,当程序运行在3级特权级上时,就能够称之为运行在用户态,由于这是最低特权级,是普通的用户进程运行的特权级,大部分用户直接面对的程序都是运行在用户态;反之,当程序运行在0级特权级上时,就能够称之为运行在内核态。

虽然用户态下和内核态下工做的程序有不少差异,但最重要的差异就在于特权级的不一样,即权力的不一样。运行在用户态下的程序不能直接访问操做系统内核数据结构和程序,好比上面例子中的testfork()就不能直接调用sys_fork(),由于前者是工做在用户态,属于用户态程序,而sys_fork()是工做在内核态,属于内核态程序。

当咱们在系统中执行一个程序时,大部分时间是运行在用户态下的,在其须要操做系统帮助完成某些它没有权力和能力完成的工做时就会切换到内核态,好比testfork()最初运行在用户态进程下,当它调用fork()最终触发sys_fork()的执行时,就切换到了内核态。

 

2. 用户态和内核态的转换

1)用户态切换到内核态的3种方式

a. 系统调用

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

b. 异常

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

c. 外围设备的中断

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

 

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

 

2)具体的切换操做

从触发方式上看,能够认为存在前述3种不一样的类型,可是从最终实际完成由用户态到内核态的切换操做上来讲,涉及的关键步骤是彻底一致的,没有任何区别,都至关于执行了一个中断响应的过程,由于系统调用实际上最终是中断机制实现的,而异常和中断的处理机制基本上也是一致的,关于它们的具体区别这里再也不赘述。关于中断处理机制的细节和步骤这里也不作过多分析,涉及到由用户态切换到内核态的步骤主要包括:

[1] 从当前进程的描述符中提取其内核栈的ss0及esp0信息。

[2] 使用ss0和esp0指向的内核栈将当前进程的cs,eip,eflags,ss,esp信息保存起来,这个

过程也完成了由用户栈到内核栈的切换过程,同时保存了被暂停执行的程序的下一

条指令。

[3] 将先前由中断向量检索获得的中断处理程序的cs,eip信息装入相应的寄存器,开始

执行中断处理程序,这时就转到了内核态的程序执行了。

 转自: http://www.cnblogs.com/tangr206/articles/3092225.html

相关文章
相关标签/搜索