“ 小明同学最近贼郁闷,去年玩比特币亏得一塌糊涂,想在股市里翻盘,听信大V举荐的股票,买了康美和长生生物,被A股狠狠的收割了一把。本想往年好好工作,谁晓得遇上了O记大裁员。尽管拿了N+6,然而还是要找工作养家糊口啊!只能硬着头皮去面试了,还好,面试题目都不难,因为小明认真看过《奔跑吧》”
01 面试题目
在一个双核处理器的零碎中,在shell界面下运行test程序。CPU0的就绪队列上有4个过程,而CPU1的就绪队列有1个过程。假如test程序和这个5个过程的nice值都是为0。
test程序
执行test过程
问题:
- 请画出test程序在内核空间的执行流程图。
- 若干工夫之后,CPU0和CPU1的就绪队列变动如何?
小明听到这题目,嘿嘿一笑,so easy,难不到窝,这题目比炒A股简略多了!于是在黑板上开始边写边画,缄口结舌。
02 题目解析
站在用户空间的角度看,在shell界面下执行test程序,shell程序会调用fork零碎调用来创立一个新过程,而后调用exec零碎调用来装载test过程,因而新过程便开始执行test程序。
站在用户空间的角度看问题,咱们只能看到test程序被执行了,然而咱们是看不到新过程是如何被创立的,它会增加到哪个CPU里,它是如何执行的,以及CPU0和CPU1之间如何做负载平衡等等问题。
小明画的test过程执行流程图
从上图可知,咱们把test过程在内核空间的执行过程分成了6个步骤。
(1)调用零碎调用fork零碎调用来创立一个新过程
(2)do_fork()创立新过程。do_fork要做好多事件,比方:
a)创立新过程过程管制块PCB,task_struct数据结构。
b)拷贝父过程的task_struct数据结构内容到新过程。
c)拷贝父过程的页表项内容到新过程。
d)设置新过程的内核栈
(3)父过程调用wake_up_new_task()尝试去唤醒新过程。
a)调用调度类的select_task_rq()办法,为新过程寻找一个负载最轻的CPU,这里抉择CPU1。
b)调用调度类的enqueue_task()办法把新过程增加到CPU1的就绪队列里。
(4)CPU1从新抉择一个适合的过程来运行。
a)每次时钟滴答到来时,scheduler_tick()会调用调度类的task_tick()去查看是否须要从新调度。check_preempt_tick()查看是否须要从新调度。若须要调度,则设置以后过程的thread_info中的TIF_NEED_RESCHED标记位。假如在咱们这个场景里,CPU1筹备调度咱们的新过程P,那么就会设置以后过程curr的thread_info中的TIF_NEED_RESCHED标记位。
b)在中断返回前会查看以后过程curr的TIF_NEED_RESCHED标记位。如果须要调度的话,调用preempt_schedule_irq()来切换过程运行。
c)调度器的schedule()函数会调用调度类的pick_next_task()办法来抉择下一个最合适运行的过程。在咱们的场景中,抉择新过程P。
d)switch_mm()切换prev过程和新过程的页表。
e)在CPU1上,switch_to()切换新过程P来执行。
(5)新过程执行。
a)新过程的第一次执行会调用ret_from_fork()函数。
b)返回用户空间执行shell程序。
c)shell程序调用exec()零碎调用来执行test程序,最终新过程变成了test过程。
(6)负载平衡。
a)在每次时钟滴答到来时去查看是否须要触发软中断来做SMP负载平衡。scheduler_tick()->trigger_load_balance()。下一次做负载平衡的工夫点寄存在就绪队列的next_balance成员里。
b)触发SCHED_SOFTIRQ软中断。
c)在软中断处理函数run_rebalance_domains()里,从以后CPU开始遍历CPU拓扑关系图,从调度域的低层往高层遍历调度域,并寻找有负载不平均的调度组。本例子中的CPU拓扑关系图很简略,只有一层MC级别的调度域。
d)CPU0对应的调度域是domain_mc_0,对应的调度组是group_mc_0;CPU1对应的调度域是domain_mc_1,对应的调度组是group_mc_1。CPU0的调度域domain_mc_0管辖CPU0和CPU1,其中group_mc_0和group_mc_1这两个调度组会连贯到domain_mc_0的一个链表里,同理CPU1的调度域domain_mc_1也是同样治理着group_mc_1和group_mc_0这两个调度组。
e)假如以后运行的CPU是CPU1,也就是说运行到run_rebalance_domains()函数的CPU为CPU1,那么在以后MC的调度域(domain_mc_1)里去找哪个调度组是最忙碌的。在咱们的场景里,很容易找到CPU0的那个调度组(group_mc_0)是最忙碌的。计算须要迁徙多少的负载量到CPU1上能力放弃两个调度组负载平衡。
f)迁徙过程从CPU0到CPU1。
g)达到新的均衡。
笨叔点评:
这是一道很棒的面试题目,把过程方方面面的问题都考到了,比如说过程的创立,过程的调度,过程的执行,SMP负载平衡。外面任何的一点,都足以难倒很多人,比如说:
过程的实质是什么?
过程的调度实质是什么?
调度器怎么抉择下一个过程?
处理器是怎么就切换和执行到下一个过程?
CFS调度器和过程优先级到底有什么关系?
CFS调度器里的虚构时钟是个什么鬼?
runnable和running是啥关系?
什么是过程的权重?
什么是过程的负载?
调度域和调度组是个什么鬼?
如何能疾速画出一个零碎的CPU拓扑关系图?
SMP负载平衡里是怎么掂量一个就绪队列的负载的?
SMP负载平衡算法是怎么玩的?
负载和权重到底有什么关系?
Linux的负载平衡算法里说的负载是怎么计算的?
怎么了解衰减(decay)?
...
笨叔正在批改蓝色版本的《奔跑吧》,基于Linux 5.0+ARM64,欢送大家提意见和idea!好的idea,笨叔好酒相赠!
更多精彩内容,关注笨叔的微信公众号以及笨叔免费旗舰篇视频。