什么是用户态和内核态
操作系统是运行在硬件上的,用户软件是运行在操作系统上的,为了操纵系统的安全(因为不是每个程序员都能够保证不出错,如果程序员能够随意访问和修改系统资源,系统容易崩溃),用户软件只能运行在用户态下,不能直接访问操作系统内核数据结构和程序,比如操作硬件,读写文件等。如果想要完成这些操作,就必须要切换到内核态,由操作系统来帮忙完成
这两种状态的主要差别是
- 处于用户态执行时,只能受限的访问内存,且不允许访问外围设备,占有的CPU是可被其他程序抢占的
- 处于内核态执行时,则能访问内存的所有数据,包括外围设备(硬盘,网卡等),且所占有的CPU是不允许被抢占的
用户态切换到内核态的3种方式
- 系统调用。用户态进程通过系统调用申请使用操作系统提供的服务程序完成工作。
- 进程调用: 终端命令exit fork;
- 文件调用: chmod chown;
- 设备调用: read write;
- 信息读取: 比如cpu信息 getcpu;
- 通信: pipe mmap(文件内存映射)
- 异常。当CPU在执行运行在用户态下的程序时,发生了某些事先不可知的异常,这时会触发由当前运行进程切换到处理此异常的内核相关程序中,也就转到了内核态,比如缺页异常。
- 中断。当外围设备完成用户请求的操作后,会向CPU发出相应的中断信号,这时CPU会暂停执行下一条即将要执行的指令转而去执行与中断信号对应的处理程序,如果先前执行的指令是用户态下的程序,那么这个转换的过程自然也就发生了由用户态到内核态的切换。比如硬盘读写操作完成,系统会切换到硬盘读写的中断处理程序中执行后续操作等。
这3种方式是系统在运行时由用户态转到内核态的最主要方式,其中系统调用可以认为是用户进程主动发起的,异常和外围设备中断则是被动的。
具体的切换步骤:
- 从当前进程的描述符中提取其内核栈的ss0及esp0信息。
- 使用ss0和esp0指向的内核栈将当前进程的cs,eip,eflags,ss,esp信息保存起来,这个过程也完成了由用户栈到内核栈的切换过程,同时保存了被暂停执行的程序的下一条指令。
- 将先前由中断向量检索得到的中断处理程序的cs,eip信息装入相应的寄存器,开始执行中断处理程序,这时就转到了内核态的程序执行了。