微信公众号:奔跑吧linux社区
本文节选自《奔跑吧linux内核》第二版卷1第9.3.5章
现在JD有半价抢购活动,如果您觉得本文还可以,千万不要错过哟!
https://item.jd.com/13051268.html
1. 问题引入
假设Linux内核只有3个内核线程(见图9.15),0号线程创建了内核线程1和内核线程2,它们永远不会退出。当系统时钟中断到来时,时钟中断处理函数会检查是否有进程需要调度。当有进程需要调度时,调度器会选择运行线程1或者线程2。
假设0号线程先运行,那么在这个场景下会发生什么情况?
这是一个有意思的问题,涉及调度器的实现机制、中断处理、内核抢占、新建进程如何被调度、进程切换等知识点。我们只有把这些知识点都弄明白了,才能真正搞明白这个问题。
2.场景分析
这个场景中的主要操作步骤如下。
(1)start_kernel()运行在0号线程里。0号线程创建了内核线程1和内核线程2。函数调用关系是start_kernel()→kernel_thread()→_do_fork()。在_do_fork()函数会创建新线程,并且把新线程添加到调度器的就绪队列中。0号线程创建内核线程1和内核线程2后,进入while死循环,0号线程不会退出,它正在等待被调度出去。
(2)产生时钟中断。处理器采用时钟定时器来周期性地提供系统脉搏。时钟中断是普通外设中断的一种。调度器利用时钟中断来定时检测当前正在运行的线程是否需要调度。
(3)当时钟中断检测到当前线程需要调度时,设置need_resched标志位。
(4)当时钟中断返回时,根据Linux内核是否支持内核抢占来确定是否需要调度,下面分两种情况来讨论。
支持内核抢占的内核:发生在内核态的中断返回时,检查当前线程的need_resched标志位是否置位,如果置位,说明当前线程需要调度。
不支持内核抢占的内核:发生在内核态的中断在中断返回时不会检查是否需要调度。
不支持内核抢占的内核
在不支持内核抢占功能的Linux内核(见图9.16)里,即使0号线程的need_resched标志位置位了,Linux内核也不会调度内核线程1或者内核线程2来运行。只有发生在用户态的中断返回或者系统调用返回用户空间时,才会检查是否需要调度。处理流程如下所示。
(1)发生时钟中断。触发时钟中断时当前进程(线程)有可能在用户态执行,也可能在内核态执行。如果进程运行在用户态时发生了中断,那么会进入异常向量表的el0_irq汇编函数;如果进程运行在内核态时发生了中断,那么会进入异常向量表的el1_irq汇编函数中。在本场景中,因为3个线程都是内核线程