学习内核的益处很多,在学习过程中不仅能够夯实大量实践根底,还能够学习到很多编码习用法,晋升学习能力和剖析能力。
一、确立高度,明确指标
高度决定视角,视角决定口头
在学习内核过程中最容易犯的谬误,也是十分难把握的其实是你站在一个什么样的高度下来学习。站什么样的高度去学习也与本身的能力相干,所以这个问题其实更多是在新开始学习的学习者感到十分苦楚的一件事。一方面又心愿本人能学懂,然而又不晓得如何开始动手。
我列举几个常见的例子:
(1) 一开始就看源码,最开始我也做过这种事,内核有什么都不晓得,后果就想着啃 0.11 的内核,后果很显然,2 天立马放弃,齐全看不懂。
(2) 翻开书从第一页开始往下啃,如果这本书比拟薄还好,如果比拟厚,比方《深刻 Linux 内核架构》,那看 2 天也得放弃。
(3) 不喜爱看目录,不喜爱疾速浏览,就想着一个一个字眼的往下抠。如果自身有肯定根底,看的时候还不会感觉腻,然而很快就发现,看了半天,什么都没有记住。
还有很多相似的问题,这些都是咱们平时学习的时候特地容易呈现的一些误区。这些其实都是没有正确扫视本人的能力,胡乱筛选高度导致。
高度是什么?
高度越高,也就是越偏差于了解各种抽象概念,偏向于构建对整体构造的一个认知,疏忽一些不必要细节,不关怀技术实现伎俩。高度越低,也就是越偏差于对应用技术的抉择,偏向于代码实现的各个细节,然而前提个别会在某个形象的概念畛域内进行各种细节性的探讨。
咱们的大脑更偏向于了解形象的内容,然而在口头时,咱们却更偏向于去把握细节性的内容。后果导致的内容就是,咱们总是心愿通过学习细节来结构对抽象概念的了解,最初被细节性内容中各种噪声烦扰思路,产生一种“这货色好难”的错觉。在了解了这点,那高度对咱们的行为有和指导意义也就跃然纸上了?
以读书为例。站的越高,意味着本人看的内容越毛糙,也就是看书的时候不会去逐字逐句的看,而是一个章节一个章节的看,极其的状况就是只看目录,在这个过程中次要集中精力构建整体构造,对外围的概念进行形象。这时候学的内容都绝对外表,然而益处就是对当前的学习有很强的指导意义,毛病很显著,会让人底气不足,而且在达到肯定水平后,很容易达到瓶颈,察觉怎么看都看不懂了。站的越低,意味着本人看的内容很粗疏,看书的时候就是一个个字眼的扣,极其状况就是开始浏览源码,去看开源社区的各种问题。然而就像诗句说的,站的越低,也就常有一种“不识庐山真面目,只缘身在此山中”的感觉。这种状况下特地容易被各种细节烦扰,例如为什么要有这些参数,为什么这里须要判断这个条件等等这些细枝末节的问题。
如何使用高度
以前对一篇博文印象很粗浅,作者了解的学习曲线划分成了两个比拟大的过程,回升的过程就是一个一直学习积攒的过程,而平缓没有增长的过程则是对之前积攒到饱和的常识进行消化的过程。我将这个学习过程进行进一步的划分,我感觉在学习积攒的 前半部分应该以 偏差学习抽象概念为主,而 后半局部应该 偏差学习实现细节。
所以集体的心得是从高到低的学习,在一个新的学习阶段,应该先多花点工夫学习一些概念化的内容,这时候切忌去看具体的实现,而是多思考如何在大脑中构建各种形象模型
对整体的架构有所概念了,而后开始学习一些细节性的内容,比方开始看些源码,抠写书上的字眼,读读一些具体的博客什么的。
二、学习小 Tips
1. 如何看书
不要从第一页开始翻 不要一页一页的翻
- 花些工夫看看前言,在很多书的前言局部,作者会通知你,整本书的构造应该是什么样,应该要以什么样的程序去浏览,在浏览的时候应该站在什么样的角度去浏览,这是作者的倡议,有什么比作者的倡议更值得咱们听取呢!?
- 不要寄心愿与一次看懂一本书,越是好的书越是要重复的看,然而很多人对这个重复了解有问题,认为重复的看就是一页页翻,反复看几遍。其实不是这样,每次重复应该让本人换一个高度,第一次翻的时候能够站在很高的高度,看一本书甚至只须要 1 天的工夫,反复几次后,站的高度应该越低,很可能看一个章节须要 1 天工夫,甚至有时候看一页就须要 1 天的工夫。
- 一本书的目录就像你在沙漠中的指南针,不要疏忽目录的作用。每次翻开书,在决定本人看什么之前,花点工夫浏览下目录,让本人回顾 (理解) 要看的章节的架构,带着这个构造去学习事倍功半。
- 带着问题去看书,这点很难,因为提什么样的问题和你抉择的高度密切相关,站的高度越高,那就越不要给本人提一些细节性的问题,反之则反之。
2. 如何看代码
如果开始看代码,肯定要记住,本人曾经站在一个十分底层的高度度了,可能有能力浏览代码,就意味着你必须对整体的构造有比拟清晰的意识,如果你都不晓得这个构造,那看代码为时太早。
无论是什么样的代码,其实思路都很相似,即便 Linux 内核是用 C 这种面相过程的语言编写,然而这么多年倒退下来,Linux 内核曾经带有了大量面对对象编程的特点。
在看代码的时候也是有两种不同的高度能够抉择,我先解释其中最粗疏的一种:
(1)如何浏览函数
一个函数写下来常常上百行,然而你须要一行一行的看么? 必定不能,那清晰意识一个函数的构造就很重要。
一个函数就是为了解决一个问题,函数名根本都能阐明其性能,函数参数是输出,返回值就是输入,函数体就是整体的执行逻辑。在函数体外部,也根本都是相似的逻辑,先是对各种输出参数进行查看,而后书写性能逻辑,而后结构输入的后果。所以一个函数写下来总是这样的一种构造。
输入后果 函数名 (输出)
{
if (输出的参数有问题)
{
异样解决,跳出
}
筹备参数
性能逻辑
结构输入后果
返回输入后果
}
一个函数其实就是一个办法,浏览的难度比书写的难度要低,书写代码须要思考的问题十分多,然而在浏览代码的时候问题就简略很多,很多书写代码过程中须要思考的问题在浏览代码的过程中就不须要思考。
- 函数名:在书写代码过程中须要思考一个函数的函数名须要可能准确表白出这个函数所具备的性能,所以常常存在各种名目标准。而浏览代码过程则能够通过浏览函数名大抵理解这个函数的性能。
- 正文:在编写代码的时候,都会倡议增加对应的函数正文,解释函数体的性能和一些注意事项;在浏览过程中能够选择性的浏览这些正文(留神:是选择性浏览,千万不要每个正文都读)
- 输出参数:在书写代码的时候,这部分的内容也是很头疼的内容,不仅须要确定须要哪些输出,还须要输出的模式,而且还须要准确定义每个输出参数的语义;然而在浏览代码的过程中,这部分内容根本能够疏忽,咱们很少会关系所看到的函数须要哪些参数输出。
- 输入后果:在书写代码的时候,这部分也是很头疼的一件事,因为准确定义输入后果也是十分艰难和麻烦的一件事;在浏览代码过程中,也须要留神输入后果,不然一个函数执行了老半天,后果连输入后果是什么都没概念,也太失败了点。
- 参数检测:在编写代码过着中十分烦的一件事,每个人都心愿调用函数的人会传入正确的参数,然而基本做不到,后果每次都要花费肯定精力对输出参数的边界、非空等进行查看;在浏览代码过程中,基本不须要浏览这部分的代码,恰好这部分内容在每个函数体中占据了相当一部分的地位;
- 参数筹备:编写代码的过程中,因为函数体外部的逻辑须要进行很多筹备,所以经常须要有一个参数筹备的过程;而浏览代码的过程根本能够疏忽这部分的逻辑,或者疾速浏览这部分逻辑,这里恰好是很多老手破费大量精力纠缠的内容,其实没必要在这里纠结,跳过就好。
- 性能逻辑:这部分是函数体中最为精髓的局部,而且代码编写起来也是相当的麻烦,被各种逻辑弄的死去火来,最初还须要重构等等伎俩;在浏览代码过程中,这部分其实很难把握,因为性能逻辑可能被封装在另外一个函数外部,这时候大家会习惯性的持续深刻看,后果弄的本人更加凌乱,又比方有的时候几个性能逻辑点组成了一套逻辑,然而大家却将这部分逻辑割裂来看,后果总感觉读的很顺当。这部分内容须要一些教训,然而有一个领导,就是在看这部分代码的时候要留神本人所站的高度,抉择采纳何种策略。
- 结构输入后果:函数体外部还会破费大量的代码进行对最初返回后果的结构工作,就像搭积木一样;不过在浏览代码的时候,咱们并不需要破费太多精力在这些逻辑上,多留神留神一些返回后果的语义。
浏览代码还有很多技巧,例如如何在带有 goto 语句的代码中疾速了解逻辑,如何界定那些正文是能够疏忽的,如何将一些代码逻辑看成一块整体内容,何时应该跳到更深的一层函数浏览等等。这些都须要平时的教训积攒。
(2)如何在大量的代码中熟能生巧
看代码有一个粒度问题,咱们不能一行一行的看,也不能一个一个函数的看,我之前提到了,Linux 内核有大量面向对象编程的影子,所以在看大量代码的时候,必须学会面向对象编程的思维模式。这样对本人在大量代码浏览中提供大量参考意见。
或者有人会通知你,面向对象编程就是弄明确什么是对象、如何写一个 class 就能够了。的确,学习面向对象编程,弄明确对象是根底,不过我感觉能够再拔高一点,了解一些更形象的概念,在这些抽象概念的领导上来学习,能够有更多的指导意义。
- 层:层并不是面向对象编程特有,然而了解层是很重要的,咱们遇到的典型的层就是网络协议栈,为什么咱们网络协议会有那么多层,就因为须要解决的事件太多,咱们不得不将内容一块块的宰割,宰割的时候,发现用层进行组织,能够让构造更加清晰,所以你当前会发现,大量的零碎都会带有层的滋味。linux 内核中带有大量的层设计,如网络协议栈有层,内存治理与寻址有层,文件 I / O 也有层。
- 畛域模型:畛域模型就是一个零碎中最为外围的几个形象实体,一个零碎,根本就是围绕着畛域模型开展,在学习内核不同的子系统的时候,肯定要花大量的精力在畛域模型上,切记!!! 在 Linux 内核上也有大量的畛域模型,例如在虚构文件系统局部存在 4 大形象 inode,dentry,file 等。在过程调度零碎的最外围形象是 task_struct。在过程地址空间则有 mm_struct,address_space 等这些外围的畛域模型。我感觉能够破费 80% 的工夫在了解这些畛域模型上。
- 畛域驱动类:畛域模型外部其实是大量的属性组成,然而如果只有属性,没有一个执行的办法,那这个畛域模型也不能发挥作用,面向对象编程的做法就是将这些办法编程畛域驱动类,说的直白一些就是接口。在 Linux 中就是那些函数指针和对应的回调函数。平时看代码,大家会破费大量的工夫去看各个回调函数,这个其实是吃力不讨好的方法,与其花大量的心理去看各个回调函数的实现,不如多思考下,为什么会有这些操作方法,它们是如何形象进去的。
如果可能了解上述的这几个形象,那在大量代码中如何熟能生巧就绝对容易了,有一个简略的套路:
(1) 在较高的角度,弄明确一个零碎为了解决什么问题,应该有哪些形象
(2) 在对整体构造有所理解当前,花心思看看这些形象对应的畛域模型,因为个别状况畛域模型很宏大,所以看的时候也须要有步骤的进行拆解学习。
(3) 在对畛域模型有所理解后,开始看畛域驱动类,想明确为什么会有这些操作。
(4) 在上述筹备好后,就能够破费一些工夫去看各个函数的具体实现,并且在看的过程中多思考畛域模型为什么这么设计。