中国人说“名正则言顺”,说明了起名字的重要性。
前贤已经有了深入linux内核。我们后人,站在前贤的肩膀上,自然不敢称“深入”,所以浅出就是本文的宗旨。
笔者这几年来,先后组织了多个培训讲座或者交流之类的会议,面试超过百人,和国内多个公司的工程师有过交流,发现两个有趣的现象:一是了解内核的人在国内总体上不多,很多人有过阅读内核书籍或者内核的经历,这里面百分之九十的人在一个星期之内就放弃了。
二是大多数人认为内核在工作中用处不大,感觉用不起来。
针对第一个问题,笔者做过调查问卷,公认学习内核最大的问题就是内核代码的难懂和跳跃。从一个函数跳到另一个函数,经常不清楚为何要执行某些逻辑,然后又跳到下一个函数。
跳跃超过三次,基本就看不下去,要放弃了。第二个问题和第一个问题强相关。因为对内核了解不够系统,很难形成一个完整的内核执行逻辑图。而工作中碰到的问题总是千变万化,个人了解的一块未必能碰到。比如一个文件系统只读问题,是内核vfs层的问题?是文件系统自身?还是块设备或者硬盘的问题?如果不能形成清晰的视图,也就很难有针对性的检查。
难学难用,这就涉及到方法论。通常人类的学习过程是从易到难,从部分到整体,从已知到未知。而内核的学习有特殊之处,众多人反馈内核几乎是九十度的学习曲线,极难找到入门的路径,从而快速流畅的阅读内核代码。从那时起,笔者开始对内核开始整理,希望能找到一条学习的路径,逐渐形成一份文档。然后通过一些培训活动逐渐验证,最终形成了本文。
本文可以归纳为两个思路。一个是对内核代码的分类。笔者把内核分为基础部分和应用部分。
中断,内存管理,任务调度归为基础部分。而把文件系统,设备驱动和网络归为应用部分。打开一份完整的内核代码统计一下,应用部分占了绝大多数代码,庞大复杂,但是应用部分冗余很多,很多代码具有相似性。而基础部分则是短小精悍。应用部分经常要调用基础部分提供的内存管理,任务调度等服务。所以本文把应用部分作为第一部分,而基础部分作为第二部分。为了在应用部分能快速理解和应用基础部分,首先要整理基础部分的服务,理解在内核中是如何使用基础部分的服务。
第二个思路是把内核分为内核架构和内核实现。内核架构是内核中一些通用,普遍性的层次,比如块设备字符设备,总线,文件系统的vfs等等。理解了内核架构,就对内核有整体上的掌握,了解内核设计者的思路,进而快速流畅的阅读内核代码。但即使理解了内核架构,也还有很多具体问题要攻克。比如驱动中一个寄存器的使用,设备链路状态如何检测,或者文件系统如何使用不同的io类型,同步和异步io的区别等等。这是需要开发人员仔细研读和理解的。本文试图归纳整理出内核的常用架构层,这些架构层具有举一反三的作用,它们构成了linux内核的骨架。
发展到今天,内核已经太庞大,太复杂。即使linus本人,也不可能阅读全部的代码。本文希望通过一些架构层次代码的分析,结合简单的例子,帮助读者理解内核的整体框架。当碰到内核问题或者需要加入某些内核功能或者写一个驱动的时候,可以迅速流畅的阅读相关代码,确定自己的方案,而不至于茫然无措。
而细节的实现,则需要程序员根据自己的需求来设计。
关于内核版本。我用的版本是2.6.18。内核有一套自己的不兼容策略,不同内核版本之间经常不能编译。
至于函数没了,数据结构变了更是家常便饭。所以我们只能选择一个版本作为基础。关于内核的不兼容设计思路的讨论,有一篇很有意思的文章,大家可以在内核文档中找到。
阅读内核代码前的准备:下载一份完整的内核。http://www.kernel.org/是linux内核的官方网站。可以下载到各个版本的内核。再准备一个好的代码阅读软件。因为内核代码经常要前后关联来阅读,所以要具有代码工程管理的软件。强烈推荐source insight。这也是国内应用很广的一个软件。另外,本文已经假设读者能编译和安装模块,具有计算机基本结构的知识。一台安装了Linux系统的计算机或者虚拟机,以及经常地练习。
由于作者水平有限,而从架构层次分析内核代码,确实是很少参考的一个工作,希望广大读者能多提意见,共同推进中国的技术水平。