谈谈如何学习Linux内核

学习内核的好处不少,在学习过程当中不只能够夯实大量理论基础,还能够学习到不少编码惯用法,提高学习能力和分析能力。node

1、确立高度,明确目标linux

高度决定视角,视角决定行动

在学习内核过程当中最容易犯的错误,也是很是难掌握的实际上是你站在一个什么样的高度上去学习。站什么样的高度去学习也与自身的能力相关,因此这个问题其实更可能是在新开始学习的学习者感到很是痛苦的一件事。一方面又但愿本身能学懂,可是又不知道如何开始入手。编程

我列举几个常见的例子:网络

(1) 一开始就看源码,最开始我也作过这种事,内核有什么都不知道,结果就想着啃0.11的内核,结果很显然,2天立马放弃,彻底看不懂。架构

(2) 翻开书从第一页开始往下啃,若是这本书比较薄还好,若是比较厚,好比《深刻Linux内核架构》,那看2天也得放弃。函数

(3) 不喜欢看目录,不喜欢快速浏览,就想着一个一个字眼的往下抠。若是自己有必定基础,看的时候还不会以为腻,可是很快就发现,看了半天,什么都没有记住。学习

还有不少相似的问题,这些都是咱们平时学习的时候特别容易出现的一些误区。这些其实都是没有正确审视本身的能力,胡乱挑选高度致使。编码

高度是什么?

高度越高,也就是越偏向于理解各类抽象概念,倾向于构建对总体结构的一个认知,忽略一些没必要要细节,不关心技术实现手段。高度越低,也就是越偏向于对使用技术的选择,倾向于代码实现的各个细节,可是前提通常会在某个抽象的概念领域内进行各类细节性的讨论。spa

咱们的大脑更倾向于理解抽象的内容,可是在行动时,咱们却更倾向于去把握细节性的内容。结果致使的内容就是,咱们老是但愿经过学习细节来构造对抽象概念的理解,最后被细节性内容中各类噪声干扰思绪,产生一种“这东西好难”的错觉。在理解了这点,那高度对咱们的行为有和指导意义也就呼之欲出了?
以读书为例。站的越高,意味着本身看的内容越粗糙,也就是看书的时候不会去逐字逐句的看,而是一个章节一个章节的看,极端的状况就是只看目录,在这个过程当中主要集中精力构建总体结构,对核心的概念进行抽象。这时候学的内容都相对表面,可是好处就是对之后的学习有很强的指导意义,缺点很明显,会让人底气不足,并且在达到必定程度后,很容易到达瓶颈,发觉怎么看都看不懂了。站的越低,意味着本身看的内容很细致,看书的时候就是一个个字眼的扣,极端状况就是开始阅读源码,去看开源社区的各类问题。可是就像诗句说的,站的越低,也就常有一种“不识庐山真面目,只缘身在此山中”的感受。这种状况下特别容易被各类细节干扰,例如为何要有这些参数,为何这里须要判断这个条件等等这些细枝末节的问题。设计

如何运用高度

之前对一篇博文印象很深入,做者理解的学习曲线划分红了两个比较大的过程,上升的过程就是一个不断学习积累的过程,而平缓没有增加的过程则是对以前积累到饱和的知识进行消化的过程。我将这个学习过程进行进一步的划分,我以为在学习积累的前半部分应该以偏向学习抽象概念为主,而后半部分应该偏向学习实现细节。

因此我的的心得是从高到低的学习,在一个新的学习阶段,应该先多花点时间学习一些概念化的内容,这时候切忌去看具体的实现,而是多考虑如何在大脑中构建各类抽象模型

对总体的架构有所概念了,而后开始学习一些细节性的内容,好比开始看些源码,抠写书上的字眼,读读一些具体的博客什么的。

2、学习小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) 在上述准备好后,就能够花费一些时间去看各个函数的具体实现,而且在看的过程当中多思考领域模型为何这么设计。

相关文章
相关标签/搜索