How To study Linux kernel and become a committer
前言
学习Linux内核究竟是为了什么 ?
- 从事内核开发者的人员相对较少, 方便找一份稳定的工作 ?
- 兴趣使然,对操作系统感兴趣,想走读些内核源码 ?
- 用户态性能分析自顶向下的需要 ?
对于我个人来说,更倾向于1
,2
两点, 自己也从事Linux 内核,虚拟化一些年头了,但是 对知识的掌握毫无体系可言,自己也在纠结如何学习 Linux内核。之前的学习方法往往是从 BUG 定位中,掌握一些细节,然后在扩展相关的一小部分代码。
这样的方法, 对解决BUG 没有什么问题。因为BUG定位,尤其是panic
, softlockup/hardlockup
, use-after-free
, 内存踩踏
. 以及一些其他的不难的BUG 没有什么问题,因为这些问题的定位不会要求对整个的子系统有一个成体系的了解,而是往 往要求对debug工具的熟练使用,以及对BUG触发点上那一点代码的深入了解。并且对于该 BUG的修复往往upstream 已经有了,直接backport 回来即可(缺乏更深度的思考)。
而如果要是提高下自己的目标呢?
例如 : 成为内核某个子系统的 committer ? (甚至maintainer)
其对自己掌握的内核知识,提出了更高的标准:
- 成体系了解该子模块(TopDown):
- 不再是某个点细节,而是很多很多的细节
- 需要对该子系统有比较深的见解,知道其为什么演进成当前的样子,当前的样子有什么 样的问题 ? 有没有什么样的解决方法 ?
基于如此高的要求, 需要对原有的学习方法进行重构。那重构成什么样呢 ?
最近再看杨振宁的一些视频, 他在节目中夸夸而谈其认识的一些有名的物理作家的生平和性 格, 以及一些研究方法。这是学术界最有成就的人所关注同行除了其学术论文之外的东西。 并且这些内容在其为数不多的公开演讲中高频出现。
所以,向优秀的同行学习,不仅是学习其学术成果,还要学习其自身的性格优点和学习方法。
既然顶级大佬都在关注同行,我们普通人为什么不关注呢 ?
NOTE
我们来思考一个问题,不同人差距很大,为什么会造成这种差距。而这种差距会随着时间 越来越小,还是越来越大?
首先来回答另一个问题, 如果不做出任何改变的话, 一定是越来越大。为什么会有初始差 距, 为什么会越来越大。为什么有些人其从事过不同的领域,但是其都有不小的成就。原 因就在于其效率高。
上面提到了影响效率的两个方面:
- 性格
- 学习方法
而性格即抽象又主观,而性格培养也需要方法,所以也可以概括为方法,所以整体来说, 是 方法 影响了效率.
所以,我们在工作之余,也需要花时间关注下优秀的开发者,如果有机会一定要和其多多交 流。而做这些事情,不是为了感叹,其多么多么牛B,而是总结其 学习方法。
Linux 学习方法
这是本篇文章最想表述的内容,源于 chengjian的一篇文章1:
他在写这篇文章时, 已经成为了内核的committer,但是他想成为maintainer (^ ^). 所以 思考了自己和maintainer之间的差距,并如何弥补这部分差距。(非常推荐大家看这篇文章, 因为这是比较优秀的工程师在其既有的学习方法上的改进(一定是有效的))
大概的总结就是:
- 自顶向下
- 先从了解一些操作系统相关的基础概念和原理入手, 对OS 有一个概念性的了解( 非 常非常非常关键,十分推荐走读: Operating Systems: Three Easy Pieces)
- 在走读一些书籍或者博客,对Kernel 子系统有一个框架性的了解(抓总概)
- 结合书籍,博客, 源码 , 抠细节.
- 多总结
- 多实践
- 多调试
- 多尝试修改
NOTE
这一点个人认为是非常重要的, 我们很多时候往往感叹,自己看了代码为什么会忘 记. 即便是自己已经将每一行代码都看过,甚至将每一行代码都注释过。但是一段 时间后,还是会忘, 并且忘的连渣都不剩。甚至看自己之前的注释都很陌生,回顾 这部分内容都得话很长时间。
原因就是缺乏总结和实践。总结不是将自己看过的代码罗列,而是自顶向下的从一 个模块出发,将每一处细节串联起一个网(树)。关于这个我们可以参考一些书籍, 看起是如何总结一个子模块,以及其中的细节的。
但是和书籍不同,书籍往往是为入门者提供一扇窗户,而自己总结的目的更多的是 帮助自己理清这部分知识, 以及其细节: 理解当前看的部分在整个子系统中的所起 到的作用,和该子系统中的其他部分的关系,甚至和其他子系统的关系。关于这部 分我们一定不要怕麻烦, 因为你现在不花时间,将来就会花更多的时间去重复 该事情. 另外要学习画图总结,多学习别的优秀的工程师的画图方法。
我们通过读代码从而得到的认知不一定是正确的,而验证其真伪的一个方式就是调 试, 所以,我们应该了解一些调试(可观测)的手段,在学习过程中去使用它。
另外,开发过模块的同学一定知道。自己写的代码,不用总结,也非常清楚其中的 细节。而看别人写的代码就没有那么轻松。而内核是Linus 写的,但是其是开源的, 我们可以任意修改!
理解内核某部分的捷径就是去修改它 – apkm 2
(关于调试和修改,因为自己也缺少这方面的实践,暂不展开)
- 从古至今, 知将来
学习内核要有历史的眼光和发展的眼光1
- 那它历史上可不一定是这样子的,需要你去回顾它。
- 它以后也不一定还是它现在的样子,需要你去不断的关注。
这是kernel 学习的难点:厚重的历史。我们看Linux 内核源码是总是会被其各种各样的 细节所绊住,很多细节,单看代码,很难搞懂。但是很多的细节代码,是在这几十年的历 史中一点点的引入的。我们如何能先屏蔽到这些细节,自顶向下的学习呢。我目前的观点 是结合书籍考古。
可以结合一些经典书籍,走读一些古老的代码(2.4, 2.6) 对内核的子模块有一个大体的 认知和理解后,在走读新版本的内核,其中的细节我们要多多走读
mail list
和git log
去了解更多细节。当然0.1 -> 2.4
内核也是有个演进的历史,我个人建议是将主要的一 些演进了解下。(例如, 调度器的O(n)->O(1)->CFS
的演进路线)1中提到
主线合入的内核的特性, 往往都是通过不断的改进, 经过了 N 轮重构和 review 才合 入的主线, 而越早期的讨论, 涉及的内容可能更多, 也更容易大家理解. 对于一些特性, 不理解它为什么这么实现的时候, 去找当时社区邮件列表的讨论, 往往是最直接有效的 途径.
最近在看匿名页和调度器,深有感触。在
2.4 - 2.6
内核演进过程中,很多子模块经过 了大量的修改(重构),而在一个功能引入时,往往会有竞品(别的作者提出的patch), 而这些内核开发者往往会在邮件列表中会详细讨论当前子系统的问题,以及这些patch所 带来的影响,其优缺点,以及选择哪个patch的具体原因。以及这些patch如何进一步的改 进。某些内容的营养价值很高,绝对的干货。但是 一个很大的问题是,这些信息是零散 的,而且每个信息的价值(对于当前)是不同的。如何在茫茫邮件列表中找到这些干货是 非常重要的。也是我这边之后所要探索的一个很重要的学习方法。
其他感悟
最后,我想用一句话来勉励自己:
开始往往不是最难的,最难的是重新开始。
参考链接
- Linux 内核特性演进史 – 成坚
- «linux内核设计与实现»