观察神秘的RQ

鲁迅笔下有个妇孺皆知的人物,叫阿Q,用英文表达,或许可以写作A Q(AQ)。

在Linux内核中,有个名字很短的结构体,叫rq,发音和阿Q很类似。

rq结构体的名字只有两个字符,使用时,实例名也常常叫相同的名字。比如下面两个内核函数的参数都是struct rq *rq。

观察神秘的RQ_第1张图片

这应该是Linux内核中名字最短的结构体了吧。

我加入Intel的初期,部门的大老板是个中东裔的老头,个子不高,胖胖的,总是面带微笑。他的邮件落款特别有特色,只有一个字符n。看了这个落款后,我明白了,越是重要的大人物,邮件落款可能越短。落款特别长的常常是销售或者客服(^-^)。

在Linux内核中,rq这个名字也符合这个规律。名字很短,但是位置非常重要。

rq的全称是Run Queue,或者Ready Queue,翻译为中文,可以叫就绪队列。

简单来说,每个CPU有个RQ,类似食堂的服务窗口。当有线程要运行时,就来排队。

上面是理论,实际的RQ长什么样呢?某一时刻,到底有哪些线程在排队呢?

在NDB调试器的规划功能中,老早就列入了一条叫ready的扩展命令,用于观察CPU的RQ,但是迟迟没有人手真正实现。这两周,终于把这个功能落地了。

基本的实现思路是获取每个CPU的percpu变量lk!runqueues,加上这个CPU的percpu基地址得到RQ对象的地址。然后就可以打印RQ的各种属性了。

要列出每个CPU的排队线程,要更复杂一些。这次实现,我们参考了Linux内核中的sched-debug虚文间的写法,两层循环,外层遍历所有进程,内存遍历进程的每个线程。

如果线程的state属性为0,就代表它是runnable状态,它的cpu字段代表它所排队的CPU。如果on_cpu字段为1,则表示已经轮到它,它正在这个CPU上运行。

比如,以taskset -c 3 ./gematrix方式执行大矩阵乘法程序,将四个工作线程都绑定在3号CPU上,此时用ndb观察,3号CPU的排队情况便如下: 观察神秘的RQ_第2张图片

上图 表示有四个线程在3号CPU上排队,1482号线程正在执行。

初步功能调通后,发现一个问题。当系统很空闲时,显示的结果只有0号CPU上有swapper/0在运行。1、2、3三个CPU上都为空。

深入分析后,我们发现,虽然每个CPU都有自己的IDLE任务,但是它们却没有按普通进程那样用thread_node链表组合在一起。所以上面说的两层循环来遍历系统中的所有进程和线程,遍历不到它们。

我特意查看了kdb的输出和实现代码。它也只能列出一个idle线程。其它idle线程连自己的pid都没有,都是复用pid 0。

为了解决这个问题,我们只好临时用了个缓解方法。当遍历所有线程后,某个CPU上仍没有任何任务时,那么就取rq的curr字段。这个字段指向的就是这个CPU上正在执行的线程,也就是IDLE线程。

用了这个个方法后,再执行!ndx.ready -f 1就可以看到每个CPU都至少有一个线程在RQ上了。

观察神秘的RQ_第3张图片

线程调度是内核的敏感逻辑,牵涉到系统中最关键的CPU资源的分配,一旦出问题,轻则影响用户体验,重则导致系统死锁或者崩溃。

这部分代码的技术复杂度也非常高,是林纳斯本人一直亲自维护的一个模块。俗话说,无限风光在险峰,对于喜爱LINUX内核的各位高手来说,这部分代码也是非常值得研究和体会的。NDB的!ready命令可以把内核停下来,观察每个CPU的RQ,这样便可以一边看代码,一边看实情,让代码活起来,大大提高学习效率。

(写文章很辛苦,恳请各位读者点击“在看”,也欢迎转发)

*************************************************

正心诚意,格物致知,以人文情怀审视软件,以软件技术改变人生

扫描下方二维码或者在微信中搜索“盛格塾”小程序,可以阅读更多文章和有声读物

观察神秘的RQ_第4张图片

也欢迎关注格友公众号

观察神秘的RQ_第5张图片

你可能感兴趣的:(观察神秘的RQ)