在计算机系统中,通常运行着两种程序————系统程序和应用程序,为了保证整系统程序不被应用程序所破坏,系统设置了两种状态
1)内核态,操作系统在系统态运行———运行操作系统程序。
2)用户态,应用程序只能在用户态运行———运行用户程序。
运行过程中处理机会在用户态和内核态来回切换,由此相应的现代操作系统将CPU指令也划分为两类,特权指令和非特权指令。
特权指令(在内核态运行的指令)
1)对内存的访问不受限制,不仅能访问系统储存空间也能访问用户储存空间
2)特权指令只许操作系统调用,不允许应用程序调用,否则会使得系统紊乱。
非特权指令(在用户态运行的指令)
一般的应用程序只能调用非特权指令,不能访问系统的硬件和软件,其内存的调用和访问范围也仅限与用户空间范围
特权指令包括:调用i/o,内存清零,修改程序状态字,设置时钟,允许禁止和中断,停机
非特权指令包括:控制转移,算数运算,访管指令,取值指令
UNIX 系统把进程的执行状态分为两种:
一种是用户态执行,表示进程正处于用户状态中执行;
另一种是核心态执行,表示一个应用进程执行系统调用后,或 I/O 中断、时钟中断后,进程便处于核心态执行。
这两种状态的主要差别在于:
处于用户态执行时,进程所能访问的内存空间和对象受到限制,其所占有的处理机是可被抢占的;
而处于核心态执行中的进程,则能访问所有的内存空间和对象,且所占用的处理机是不允许被抢占的。
用户态和内核态的切换
当在系统中执行一个程序时,大部分时间是运行在用户态下的,在其需要操作系统帮助完成一些用户态自己没有特权和能力完成的操作时就会切换到内核态。
用户态切换到内核态的3种方式
(1)系统调用
这是用户态进程主动要求切换到内核态的一种方式。用户态进程通过系统调用申请使用操作系统提供的服务程序完成工作。例如fork()就是执行了一个创建新进程的系统调用。系统调用的机制和新是使用了操作系统为用户特别开放的一个中断来实现,如Linux的int 80h中断。
想仔细了解系统调用的相关知识的同学可以看看我的这篇博客
https://blog.csdn.net/qq_37200329/article/details/99710222
(2)异常
当cpu在执行运行在用户态下的程序时,发生了一些没有预知的异常,这时会触发由当前运行进程切换到处理此异常的内核相关进程中,也就是切换到了内核态,如缺页异常。
(3)外围设备的中断
当外围设备完成用户请求的操作后,会向CPU发出相应的中断信号,这时CPU会暂停执行下一条即将要执行的指令而转到与中断信号对应的处理程序去执行,如果前面执行的指令时用户态下的程序,那么转换的过程自然就会是 由用户态到内核态的切换。如硬盘读写操作完成,系统会切换到硬盘读写的中断处理程序中执行后边的操作等。
关于中断和异常的区别我在这篇博客做了详细的介绍
https://mp.csdn.net/postedit/99710209
这三种方式是系统在运行时由用户态切换到内核态的最主要方式,其中系统调用可以认为是用户进程主动发起的,异常和外围设备中断则是被动的。从触发方式上看,切换方式都不一样,但从最终实际完成由用户态到内核态的切换操作来看,步骤有事一样的,都相当于执行了一个中断响应的过程。系统调用实际上最终是中断机制实现的,而异常和中断的处理机制基本一致。
用户态到内核态具体的切换步骤:
(1)从当前进程的描述符中提取其内核栈的ss0及esp0信息。
(2)使用ss0和esp0指向的内核栈将当前进程的cs,eip,eflags,ss,esp信息保存起来,这个过程也完成了由用户栈到内核栈的切换过程,同时保存了被暂停执行的程序的下一条指令。
(3)将先前由中断向量检索得到的中断处理程序的cs,eip信息装入相应的寄存器,开始执行中断处理程序,这时就转到了内核态的程序执行了。
内核态切换到用户态的途径
设置程序状态字
注意一条特殊的指令——陷入指令(又称为访管指令,因为内核态也被称为管理态,访管就是访问管理态)
该指令给用户提供接口,用于调用操作系统的服务。