结合内核态和线程结构谈谈 --- 为什么线程切换比进程快?
前言
看过之前两篇文章,应该都大致了解了两件事情。
在Linux系统里面,进程线程几乎没有区别,没有为线程设置额外的调度算法,数据结构
进程/线程调度其实经历这几个阶段
进程A —> 陷入到内核态,内核进程A
内核进程A —> 进程调度器 —> 内核进程B
内核进程B —> 进程B
既然如此必然经过页表,跟栈的切换
之后,我思考一个问题,为什么说线程切换的效率要比进程低呢?线程切换的时候不也要走内核空间吗?进入内核空间就需要切换页表栈,一样有开销。
而且线程也没有特殊的调度算法和数据结构。带着这个问题我继续深入研究….
切换的开销
我们知道,进程是资源分配的单位,不同进程之间,很显然它的内存空间和栈空间是隔离的,进程A一般不能访问进程B的地址空间。
线程,作为一种轻量级进程,同一进程下的线程起码是共享地址空间的。
所以一般来说,进程切换需要切换地址空间也就是(page table),线程之间的切换则不需要page table。
这是这个问题的一般答案。但是有没有考虑过一个问题。(1)线程跟进程的结构很相似,没有区别也没有特别的调度算法 (2)也就是说,即使是线程,调度的时候也要进入内核态。但是内核态程序是有自己的页表的。
因此,不管怎么样,即使是同一进程下的线程切换,仍旧需要进入到内核态,切换到内核程序的页表。这样,似乎上面的答案就不成立了?
因此,我仔细阅读了,陷入内核空间的代码。
切换的过程
具体过程就不说了,以时间片到期为例。
- 内核的timer到期后,产生一个中断,发起一个进程调度
- 进程A —> 陷入到内核态,内核进程A
- 内核进程A —> 进程调度器 —> 内核进程B
- 内核进程B —> 进程B
这就是进程之间的调度的过程。那么我们就看看,陷入内核的时候发生了什么事情?
- 系统调用 - ecall
- usersevc-trampoline page
- usertrap
ecall究竟做了什么
再次仔细阅读ecall的源码,发现ecall其实就做了这几件事情
1 |
|
所以,ecall执行的东西并不多。并且这样就算已经陷入到内核态了,因为能够执行内核指令。但是此时页表和栈仍然没有切换。
为什么ecall这样设计?参考上一篇文章,这样设计是为了ecall的效率。
因此,进行线程调度的时候,进入内核态的过程应该是不会切换内存的。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!