您的位置首页>企业动态>

Linux内核学习的经验总结

导读大家好,我是极客范的本期栏目编辑小友,现在为大家讲解Linux内核学习的经验总结问题。学习核心,每个人都有自己的学习方法,不同的人有不

大家好,我是极客范的本期栏目编辑小友,现在为大家讲解Linux内核学习的经验总结问题。

学习核心,每个人都有自己的学习方法,不同的人有不同的看法。以下是我在学习过程中总结出来的东西。对我自己来说,我认为它更有效率。让我们分享给你。

内核学习,片面观;不可避免的疏漏,请指正。

为什么写这个博客?

在学习内核之初,不要拘泥于一个方面,不要只专注于一个子系统而跳入实际的代码行,因为在这种情况下,会涉及到很多方面,很多困难和挫折,一个函数体(假设你一开始学习的是某个方面的特定函数函数)很可能会混入其他子系统的设计思路(大多是大量相关的数据结构或全局变量)。用来支持这个子系统的管理),此时看到这些东西,没有头绪,也不是很了解,会有很多疑问。(如果你此时纠结于这些问题,其实你在学习当前子系统的过程中,也在频繁地涉及其他子系统,此时你的注意力会被分散。)其实了解了各个子系统之后再回头看这些东西,就简单了。因此,为了避免“只看到树,看不到林”,不要急于进入底层代码,也不要过早研究底层代码。

大二第一次接触内核就犯了这个错误。我一头扎进内存管理,查看了非常低级的实现代码。虽然也是基于内存管理的设计思想,但是相对来说比较孤立。因为这次没有学习其他子系统,应该说视野和思想都比较狭窄,所以直接跳过了代码涉及的其他子系统的实现。这是明智的,当然也是必要的。

我的学习方法

一开始我觉得主要问题在于你知不知道,而不是你懂不懂。一个子系统的实现采用一定的策略和方法,你在学习中需要做的就是知道有这样的事情,然后你就可以理解所描述的策略或方法。

根据自己的学习经验,刚开始学习内核的时候,我觉得应该做的就是在脑海中建立内核的总体框架,了解各个子系统的设计思路和构建思路。这些思路和想法会从宏观的角度给你呈现一个清晰的脉络,就像一棵大树的树干去掉了枝叶,一目了然。当然,具体的实现方法和函数肯定会涉及到,但我们此时接触到的函数或方法都是在内核实现的更高层次,是主要(重要)函数。我们已经了解了这些功能,他们针对的是什么设计思路,实现了什么功能,达到了什么目的,在这里他们彼此熟悉也是事实。至于这个主函数调用的其他辅助函数,就相当于枝叶,不需要太早深入。此时,内核子系统框架与代码实现之间的关联已经初步建立。关联其实很简单。例如,当你看到某个函数的名字时,你会记住这个函数针对的是哪个子系统,它实现了什么功能。

我想这个时候我想看的是LKD3。这本书是概括性的,主要从概念、设计、大的实现方法来描述各个子系统,而对具体相关功能实现的代码讲解很少涉及(相比ULK3,这本书主要是对具体功能代码实现的深入分析,当然你也可以读一读,但是过早读这本书会觉得很痛苦和枯燥,基本上就是功能的实现。此外,本书还给出了一些编写程序时的重要注意点,可以作为指导建议。主要子系统包括:内存管理、进程管理和调度、系统调用、中断和异常、内核同步、时间和定时器管理、虚拟文件系统、块I/O层、设备和模块。(这里的顺序其实就是LKD3目录的顺序)。

在学习的时候,我交替读三本书。首先,我读了LKD3,它是专用于一个子系统的。主要是了解设计原则和思路。当然我也会遇到一些主要功能的介绍,但大部分都是功能基于前面介绍的思路和原理已经完成的功能。本书没有深入分析功能本身的实现。然后看ULK3和PLKA看同一个子系统,但是不要仔细分析底层具体功能的代码,只是粗略的看一下,了解的很少甚至不看。因为,有时候,在某本书的某个地方,它卡住了,你不太明白。在其他书中,你可能会遇到不同角度对同一个问题的描述,说不出哪一句话会让你豁然开朗,比如醍醐灌顶。这种事经常发生在我身上。

并不是说有些函数体的实现在学习过程中被完全忽略了,只要你想彻底理解它们的代码实现,没有人会阻止你。我在反复阅读的过程中走得很深。比如在VFS打开文件需要分析路径,需要考虑的细节很多(././等。),但是它的代码实现很好理解。例如,在CFS调度中,分配给进程的时间片是根据shedule延迟、队列中的进程数及其nice值(使用动态优先级)计算的。没有理由不看它。这太重要太有趣了。

ULK3也将有一个设计原创。

理与思想之类的概括性介绍,基本上都位于某个主题的开篇段落。但是更多的是对支持该原理和思想的主要函数实现的具体分析,同样在首段,一句话综述函数的功能,然后对函数的实现以 1、2、3,或者 a、b、c 步骤的形式进行讲解。我只是有选择性的看,有时候对照着用 source insight 打开的源码,确认一下代码大体上确实是按书中所描述的步骤实现的,就当是增加感性认识。由于步骤中掺杂着各种针对不同实现目的安全性、有效性检查,如果不理解就先跳过。这并不妨碍你对函数体功能实现的整体把握。

PLKA 介于 LKD3 和 ULK3 之间。我觉得 PLKA 的作者(看照片,真一德国帅小伙,技术如此了得)肯定看过 ULK,无论他的本意还是有意,总之 PLKA 还是跟 ULK 有所不同,对函数的仔细讲解都做补充说明,去掉函数体中边边角角的情况,比如一些特殊情况的处理,有效性检查等,而不妨碍对整个函数体功能的理解,这些他都有所交代,做了声明;而且,就像 LKD3 一样,在某些点上也给出了指导性编程建议。作者们甚至对同一个主要函数的讲解的着重点都不一样。这样的话,对我们学习的人而言,有助于加深理解。另外,我认为很重要的一点就是 PLKA 针对的 2.6.24 的内核版本,而 ULK 是 2.6.11,LKD3 是 2.6.34。在某些方面 PLKA 比较接近现代的实现。其实作者们之所以分别选择 11 或者 24,都是因为在版本发行树中,这两个版本在某些方面都做了不小的变动,或者说是具有标志性的转折点(这些信息大多是在书中的引言部分介绍的,具体的细节我想不起来了)。

Intel V3,针对 X86 的 CPU,本书自然是系统编程的权威。内核部分实现都可以在本书找到其根源。所以,在读以上三本书某个子系统的时候,不要忘记可以在 V3 中相应章节找到一些基础性支撑信息。

在读书过程中,会产生相当多的疑问,这一点是确信无疑的。 大到搞不明白一个设计思想,小到不理解某行代码的用途。各个方面,各种疑问,你完全可以把不理解的地方都记录下来 (不过,我并没有这么做,没有把疑问全部记下来,只标记了很少一部分我认为很关键的几个问题),专门写到一张纸上,不对,一个本上,我确信会产生这么多的疑问,不然内核相关的论坛早就可以关闭了。其实,大部分的问题(其中很多问题都是你知道不知道有这么一回事的问题)都可以迎刃而解,只要你肯回头再看,书读百遍,其义自现。多看几遍,前前后后的联系明白个七七八八是没有问题的。我也这么做了,针对某些子系统也看了好几遍,切身体会。

当你按顺序学习这些子系统的时候,前面的章节很可能会引用后面的章节,就像 PLKA 的作者说的那样,完全没有向后引用是不可能的,他能做的只是尽量减少这种引用而又不损害你对当前问题的理解。不理解,没关系,跳过就行了。后面的章节同样会有向前章节的引用,不过这个问题就简单一些了 ,你可以再回头去看相应的介绍,当时你不太理解的东西,很可能这个时候就知道了它的设计的目的以及具体的应用。不求甚解只是暂时的。比如说,内核各个子系统之间的交互和引用在代码中的体现就是实现函数穿插调用,比如你在内存管理章节学习了的内存分配和释放的函数,而你是了解内存在先的,在学习驱动或者模块的时候就会碰到这些函数的调用,这样也就比较容易接受,不至于太过茫然;再比如,你了解了系统时间和定时器的管理,再回头看中断和异常中 bottom half 的调度实现,你对它的理解就会加深一层。

子系统进行管理工作需要大量的数据结构。子系统之间交互的一种方式就是各个子系统各自的主要数据结构通过指针成员相互引用。学习过程中,参考书上在讲解某个子系统的时候会对数据结构中主要成员的用途解释一下,但肯定不会覆盖全部(成员比较多的情况,例如 task_struct),对其它子系统基于某个功能实现的引用可能解释了,也可能没做解释,还可能说这个变量在何处会做进一步说明。所以,不要纠结于一个不理解的点上,暂且放过,回头还可以看的。之间的联系可以在对各个子系统都有所了解之后再建立起来。其实,我仍然在强调先理解概念和框架的重要性。

等我们完成了建立框架这一步,就可以选择一个比较感兴趣的子系统,比如驱动、网络,或者文件系统之类的。这个时候你再去深入了解底层代码实现,相较于一开始就钻研代码,更容易一些,而且碰到了不解之处,或者忘记了某个方面的实现,此时你完全可以找到相应的子系统,因为你知道在哪去找,查漏补缺,不仅完成了对当前函数的钻研,而且可以回顾、温习以前的内容,融会贯通的时机就在这里了。

《深入理解 linux 虚拟内存》(2.4 内核版本),LDD3,《深入理解 linux 网络技术内幕》,几乎每一个子系统都需要一本书的容量去讲解,所以说,刚开始学习不宜对某个模块太过深入,等对各个子系统都有所了解了,再有针对性的去学习一个特定的子系统。这时候对其它系统的援引都可以让我们不再感到茫然、复杂,不知所云。

比如,LDD3 中的以下所列章节:构造和运行模块,并发和竞态,时间、延迟及延缓操作, 分配内存,中断处理等,都属于驱动开发的支撑性子系统,虽说本书对这些子系统都专门开辟一个章节进行讲解,但是详细程度怎么能比得上 PLKA,ULK3,LKD3 这三本书,看完这三本书,你会发现读 LDD3 这些章节的时候简直跟喝白开水一样,太随意了,因为 LDD3 的讲解比之 LKD3 更粗略。打好了基础,PCI、USB、TTY 驱动,块设备驱动,网卡驱动,需要了解和学习的东西就比较有针对性了。这些子系统就属于通用子系统,了解之后,基于这些子系统的子系统的开发—驱动 (需进一步针对硬件特性) 和网络 (需进一步理解各种协议)—相对而言,其学习难度大大降低,学习进度大大加快,学习效率大大提升。说着容易做来难。达到这样一种效果的前提就是:必须得静下心来,认真读书,要看得进去,PLKA,ULK3 厚得都跟砖头块儿一样,令人望之生畏,如果没有兴趣,没有热情,没有毅力,无论如何都是不行,因为需要时间,需要很长时间。我并不是说必须打好了基础才可以进行驱动开发,只是说打好了基础的情况下进行开发会更轻松,更有效率,而且自己对内核代码的驾驭能力会更强大。这只是我个人见解,我自己的学习方式,仅供参考。

语言

PLKA 是个德国人用德语写的,后来翻译成英文,又从英文翻译成中文,我在网上书店里没有找到它的纸质英文版,所以就买了中文版的。ULK3 和 LKD3 都是英文版的。大牛们写的书,遣词造句真的是简洁,易懂,看原版对我们学习计算机编程的程序员来说完全不成问题,最好原汁原味。如果一本书确实翻译地很好,我们当然可以看中文版的,用母语进行学习,理解速度和学习进度当然是很快的,不作他想。看英文的时候不要脑子里想着把他翻译成中文,没必要。

API 感想 

“比起知道你所用技术的重要性,成为某一个特别领域的专家是不重要的。知道某一个具体 API 调用一点好处都没有,当你需要他的时候只要查询下就好了。”这句话源于我看到的一篇翻译过来的博客。我想强调的就是,这句话针应用型编程再合适不过,但是内核 API 就不完全如此。

内核相当复杂,学习起来很不容易,但是当你学习到一定程度,你会发现,如果自己打算写内核代码,到最后要关注的仍然是 API 接口,只不过这些 API 绝大部分是跨平台的,满足可移植性。内核黑客基本上已经标准化、文档化了这些接口,你所要做的只是调用而已。当然,在使用的时候,最好对可移植性这一话题在内核中的编码约定烂熟于心,这样才会写出可移植性的代码。就像应用程序一样,可以使用开发商提供的动态库 API,或者使用开源 API。同样是调用 API,不同点在于使用内核 API 要比使用应用 API 了解的东西要多出许多。

当你了解了操作系统的实现—这些实现可都是对应用程序的基础性支撑啊—你再去写应用程序的时候,应用程序中用到的多线程,定时器,同步锁机制等等等等,使用共享库 API 的时候,联系到操作系统,从而把对该 API 的文档描述同自己所了解到的这些方面在内核中的相应支撑性实现结合起来进行考虑,这会指导你选择使用哪一个 API 接口,选出效率最高的实现方式。对系统编程颇有了解的话,对应用编程不无益处,甚至可以说是大有好处。

设计实现的本质,知道还是理解 

操作系统是介于底层硬件和应用软件之间的接口,其各个子系统的实现很大程度上依赖于硬件特性。书上介绍这些子系统的设计和实现的时候,我们读过了,也就知道了,如果再深入考虑一下,为什么整体架构要按照这种方式组织,为什么局部函数要遵循这样的步骤处理,知其然,知其所以然,如果你知道了某个功能的实现是因为芯片就是这么设计的,CPU 就是这么做的,那么你的疑问也就基本上到此为止了。再深究,就是芯片架构方面的设计与实现,对于程序员来讲,无论是系统还是应用程序员,足迹探究到这里,已经解决了很多疑问,因为我们的工作性质偏软,而这些东西实在是够硬。

比如,ULK3 中讲解的中断和异常的实现,究其根源,那是因为 Intel x86 系列就是这么设计的,去看看 Intel V3 手册中相应章节介绍,都可以为 ULK3 中描述的代码实现方式找到注解。还有时间和定时器管理,同样可以在 Intel V3 对 APIC 的介绍中获取足够的信息,操作系统就是依据这些硬件特性来实现软件方法定义的。

又是那句话,不是理解不理解的问题,而是知道不知道的问题。有时候,知道了,就理解了。在整个学习过程中,知道,理解,知道,理解,知道……,交叉反复。为什么开始和结尾都是知道,而理解只是中间步骤呢?世界上万事万物自有其规律,人类只是发现而已,实践是第一位的,实践就是知道的过程,实践产生经验,经验的总结就是理论,理论源于实践,理论才需要理解。我们学习内核,深入研究,搞来搞去,又回到了芯片上,芯片是物质的,芯片的功用基于自然界中物质本有的物理和电子特性。追本溯源,此之谓也。

动手写代码

纸上得来终觉浅,绝知此事要躬行。只看书是绝对不行的,一定要结合课本给出的编程建议自己敲代码。刚开始就以模块形式测试好了,或者自己编译一个开发版本的内核。一台机器的话,使用 UML 方式调试,内核控制路走到哪一步,单步调试看看程序执行过程,比书上的讲解更直观明了。一定要动手实际操作。

参考书

LDD3          Linux Device Driver 3rd

LKD3          Linux Kernel Development 3rd

ULK3          Understanding the Linux Kernel 3rd

PLKA          Professional Linux Kernel ArchitectureUML            User Mode Linux

Intel V3       Intel? 64 and IA-32 Architectures Software Developer’s Manual Volume 3 (3A, 3B & 3C): System Programming Guide

作者在写书的时候,都是以自己的理解组织内容,从自己的观点看待一个主题,关注点跟作者自身有很大的关系。出书的时间有先后,后来人针对同一个主题想要出书而又不落入窠臼,最好有自己的切入方式,从自己的角度讲解相关问题,这才值得出这本书,千篇一律是个掉价的行为,书就不值钱了。

尽信书不如无书。

http://lwn.net/Articles/419855/ 此处是一篇关于 LKD3 的书评,指出了其中的错误,当你读完的时候,不妨去找找,看一下自己在其中所描述的地方有什么特别的印象。

http://lwn.net/ArTIcles/161190/ 此处是一篇对 ULK3 的介绍,我认为其中很关键的几句话就可以给本书定位:

Many of the key control paths in the kernel are described, step by step;

一步一步地讲述内核控制路径的实现。

The level of detail someTImes makes it hard to get a sense for the big picture, but it does help somebody trying to figure out how a parTIcular funcTIon works.

对代码讲解的详细程度有时候很难让读者把握住它的主旨大意,但是确实有助于读者理解一个特定的函数到底是如何工作的。

Indeed, that is perhaps the key feature which differentiates this book. It is very much a “how it works” book, designed to help people understand the code.

事实上,这也正是本书与众不同的地方。更像一个“如何工作”的书,帮助读者理解代码实现。

It presents kernel functions and data structures, steps the reader through them, but does not, for example, emphasize the rules for using them. UTLK is a study guide, not a programming manual.

本书描述了内核函数和数据结构,引导读者穿行于其间,但是,并没有着重强调使用它们的法则。UTLK 是一本学习指南,而不是编程手册。

这几句话对本书的描述非常到位。基于此,作为指导性原则,我们就可以很有效率地使用它了。

看一本技术书籍,书中的序言部分绝对是首先应该翻阅的,其次就是目录。我发现在阅读过程中我会频繁的查看目录,甚至是喜欢看目录。

结尾

兴趣的力量是无穷的。兴趣能带来激情,如果工作可以和兴趣结合到一起,工作起来才会有热情,那么工作就不只是工作了,更是一种享受。

Linux,我的兴趣,我的动力,我的方向,我的未来!

转自 http://blog.jobbole.com/113076/

 

郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。