用户态以及内核态

来源:互联网 发布:java移植安卓 编辑:程序博客网 时间:2024/04/30 17:21

1.基础准备

(1)为什么要有用户态和内核态???

        由于需要限制不同的程序之间的访问能力, 防止他们获取别的程序的内存数据, 或者获取外围设备的数据, 并发送到网络, CPU划分出两个权限等级 --用户态内核态


(2)用户态和内核态???



用户态:当CPU正在运行用户代码时。

        (只能受限的访问内存, 且不允许访问外围设备. 占用CPU的能力被剥夺, CPU资源可以被其他程序获取)

核心态(内核态):当CPU正在运行内核代码时(内核代码是共享的)。

        (此时,CPU可以访问内存所有数据,包括外围设备,如硬盘, 网卡. CPU也可以将自己从一个程序切换到另一个程序)




用户模式:不可以访问内核空间(>=0x80000000)

内核模式:可以访问任何有效虚拟地址,包括内核空间。一个线程可以访问其他任何线程地址空间。



用户态(user mode)在计算机结构指两项类似的概念。

         在CPU的设计中,用户态指非特权状态。在此状态下,执行的代码被硬件限定,不能进行某些操作,比如写入其他进程的存储空间,以防止给操作系统带来安全隐患。用户态不允许程序进行处理器中要求特权态的操作,以避免操作系统崩溃。每个进程都在各自的用户空间中运行,而不允许存取其他程序的用户空间。

         在操作系统的设计中,用户态也类似,指非特权的执行状态。内核禁止此状态下的代码进行潜在危险的操作,比如写入系统配置文件、杀掉其他用户的进程、重启系统等。操作系统中的用户态,指权限等级中的一般级别,与之相对的是管理员或者超级用户(类Unix系统中,名为“root”或“superuser”等)的特权级别。用户态启动的每个进程,根据运行该进程的用户,都被系统赋予特定的权限。


核心态是操作系统内核所运行的模式,运行在该模式的代码,可以无限制地对系统存储、外部设备进行访问。

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


        用户态:Ring3运行于用户态的代码则要受到处理器的诸多检查,它们只能访问映射其地址空间的页表项中规定的在用户态下可访问页面的虚拟地址,且只能对任务状态段(TSS)中I/O许可位图(I/O Permission Bitmap)中规定的可访问端口进行直接访问

               内核态:Ring0在处理器的存储保护中,核心态,或者特权态(与之相对应的是用户态),是操作系统内核所运行的模式。运行在该模式的代码,可以无限制地对系统存储、外部设备进行访问

                                              

                                               


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

       虽然用户态下和内核态下工作的程序有很多差别,但最重要的差别就在于特权级的不同,即权力的不同。运行在用户态下的程序不能直接访问操作系统内核数据结构和程序。

        当我们在系统中执行一个程序时,大部分时间是运行在用户态下的,在其需要操作系统帮助完成某些它没有权力和能力完成的工作时就会切换到内核态。


0)切换

           所有用户程序都是运行在用户态的, 但是有时候程序确实需要做一些内核态的事情, 例如从硬盘读取数据, 或者从键盘获取输入等. 而唯一可以做这些事情的就是操作系统, 所以此时程序就需要先向操作系统请求以程序的名义来执行这些操作.这时需要一个这样的机制: 用户态程序切换到内核态, 但是不能控制在内核态中执行的指令。这种机制叫系统调用, 在CPU中的实现称之为陷阱指令(Trap Instruction)。

        我们在C程序中进行“系统调用”好像是个一般的函数调用,当实际上调用系统调用会引起用户态到核心态的状态变化,这是怎么做到的呢?      原来,C编译程序采用一个预定义的函数库(C之程序库),其中的函数具有系统调用的名字,从而解决了在用户程序中请求系统调用的问题。这些库函数一般都执行一条指令,该指令将进程的运行方式变为核心态,然后,使内核开始为系统调用执行代码。我们称这个指令为操作系统陷入(operating system trap)。


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信息装入相应的寄存器,开始

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


3. 用户态和内核态实例

1)例子------C代码

1.     void testfork(){  

2.     if(0 = = fork()){  

3.     printf(“create new process success!\n”);  

4.     }  

5.     printf(“testfork ok\n”);  

6.     }  

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

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


2)特权级

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

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


3)用户态和内核态

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

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

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




0 0
原创粉丝点击