Linux内核态与用户态
Linux内核态与用户态
用户态与内核态
大部分操作系统中都划分了内核态与用户态,。对于Linux中,一个进程(应用程序)有的地址寻址空间一般是由操作系统位数决定,例如,32位操作系统的寻址空间是2的32次方(4G),对于一个进程而言,这就是他的范围,但是这个范围要再次划分,分为内核空间与用户空间
当然对应的空间也代表了不同的权限:内核空间(Kernel Space),这个空间只有内核程序可以访问;用户空间(User Space),这部分内存专门给应用程序使用。
也就是普通的应用程序是暂时无法访问内核空间的,只有在用户空间执行一些非特权指令。特权指令只能在内核空间执行。如果一个应用程序(进程)只是在用户空间进行操作,我们就叫他为程序在用户态进行执行。反之则是内核态
在内核态下,用户的寻址空间范围其实高达很多,基本上所有的位地址都可以达到,因为一个进程(应用)的寻址范围分两部分,一个是内核空间,一个是用户空间,大概可能一个1G 一个3G,具体看划分策略。
在用户态下,用户的寻址范围只能在一部分,也就是3G左右(以实际情况为准)
那么进程如何切换到内核态呢?
其实主要是由系统调用(System call)和中断来由用户态切换到内核态,流程如下:
当发生系统调用时,用户态的程序发起系统调用。因为系统调用中牵扯特权指令,用户态程序权限不足,因此会中断执行,也就是 Trap(Trap 是一种中断)。
发生中断后,当前 CPU 执行的程序会中断,跳转到中断处理程序。内核程序开始执行,也就是开始处理系统调用。内核处理完成后,主动触发 Trap,这样会再次发生中断,切换回用户态工作。
为什么要中断呢? 因为中断是为了保存进程在用户态的一些上下文,例如:堆栈信息,等进程切换回去的时候,就会涉及保存的信息回复等步骤
具体如图所示:

内核态和用户态的区别主要体现在以下几个方面:
- 权限:内核态是操作系统拥有最高权限的运行状态,可以访问系统的所有资源,包括 CPU、内存、I/O 等。而用户态只能访问受限的资源,应用程序在用户态下运行,不能直接访问操作系统内核数据结构和程序。
- 系统调用:在用户态下,应用程序需要通过系统调用来请求操作系统提供服务,而在内核态下,操作系统可以直接访问系统资源,不需要通过系统调用。
- CPU 指令:在内核态下,CPU 可以执行所有的指令,而在用户态下,CPU 只能执行受限的指令。
- 中断处理:在内核态下,操作系统可以响应所有的中断请求,而在用户态下,只能响应部分中断请求。
- 内存访问:在内核态下,操作系统可以访问所有的内存地址,而在用户态下,只能访问受限的内存地址。
- 运行环境:在内核态下,操作系统运行在内核空间,而在用户态下,应用程序运行在用户空间。
用户态线程和内核态线程之间的映射关系
以上我们一直在讨论的是进程,其实在绝大多数操作系统,我们的最小调度单位是进程,因为我们Linux系统本身就是一个巨大的进程,通过磁盘等来载入系统,所以相当于我们在内部一直在操作很多很多线程。至于两者之间的关系详情见博客
如果有一个用户态的进程,它下面有多个线程。如果这个进程想要执行下面的某一个线程,应该如何做呢?
这时,比较常见的一种方式,就是将需要执行的程序,让一个内核线程去执行。毕竟,内核线程是真正的线程。因为它会分配到 CPU 的执行资源。如果一个进程所有的线程都要自己调度,相当于在进程的主线程中实现分时算法调度每一个线程,也就是所有线程都用操作系统分配给主线程的时间片段执行。这种做法,相当于操作系统调度进程的主线程;进程的主线程进行二级调度,调度自己内部的线程。
这样操作劣势非常明显,比如无法利用多核优势,每个线程调度分配到的时间较少,而且这种线程在阻塞场景下会直接交出整个进程的执行权限。
由此可见,用户态线程创建成本低,问题明显,不可以利用多核。内核态线程,创建成本高,可以利用多核,切换速度慢。因此通常我们会在内核中预先创建一些线程,并反复利用这些线程。这样,用户态线程和内核态线程之间就构成了下面 4 种可能的关系:
多对一
用户态进程中的多线程复用一个内核态线程。这样,极大地减少了创建内核态线程的成本,但是线程不可以并发。因此,这种模型现在基本上用的很少。程序是存储在内存中的指令,用户态线程是可以准备好程序让内核态线程执行的。后面的几种方式也是利用这样的方法。
一对一
该模型为每个用户态的线程分配一个单独的内核态线程,在这种情况下,每个用户态都需要通过系统调用创建一个绑定的内核线程,并附加在上面执行。 这种模型允许所有线程并发执行,能够充分利用多核优势,Windows NT 内核采取的就是这种模型。但是因为线程较多,对内核调度的压力会明显增加。
多对多
这种模式下会为 n 个用户态线程分配 m 个内核态线程。m 通常可以小于 n。一种可行的策略是将 m 设置为核数。这种多对多的关系,减少了内核线程,同时也保证了多核心并发。Linux 目前采用的就是该模型。
用户态线程与内核态线程的两者关系
用户态线程工作在用户空间,内核态线程工作在内核空间。用户态线程调度完全由进程负责,通常就是由进程的主线程负责。相当于进程主线程的延展,使用的是操作系统分配给进程主线程的时间片段。内核线程由内核维护,由操作系统调度。
用户态线程无法跨核心,一个进程的多个用户态线程不能并发,阻塞一个用户态线程会导致进程的主线程阻塞,直接交出执行权限。这些都是用户态线程的劣势。内核线程可以独立执行,操作系统会分配时间片段。因此内核态线程更完整,也称作轻量级进程。内核态线程创建成本高,切换成本高,创建太多还会给调度算法增加压力,因此不会太多。
实际操作中,往往结合两者优势,将用户态线程附着在内核态线程中执行。