本文主要介绍linux内核源代码所在文件的相关知识。内容详实易懂,操作简单快捷,具有一定的参考价值。相信大家看完这篇放置linux内核源代码的文章都会有所收获。让我们一起来看看吧。
linux内核的源代码放在/usr/src/linux目录中。内核源代码的组成:1 .arch目录,包含了这个内核源代码支持的硬件架构相关的核心代码;2、包含目录,包含大部分核心包含文件;3.init目录,包含核心启动代码;4.mm目录,包含所有内存管理代码;5.drivers目录,包含系统中的所有设备驱动程序;6.Ipc目录,其中包含核心进程间通信代码。
linux内核的源代码在哪里?
Linux内核源代码可以从多种途径获得。一般来说,在安装的linux系统下,/usr/src/linux目录下的就是内核源代码。
对于源代码的阅读,如果想顺利阅读,最好事先对源代码的知识背景有一定的了解。
linux内核源代码的组成如下(假设相对于Linux目录):
拱门
该子目录包含与该核心源代码支持的硬件架构相关的核心代码。比如X86平台的i386。
包括
该目录包含大多数核心包含文件。此外,每个受支持的体系结构都有一个子目录。
初始化
该目录包含核心启动代码。
毫米
这个目录包含所有的内存管理代码。与具体硬件架构相关的内存管理代码位于arch/*/mm目录下,比如X86对应的arch/i386/mm/fault.c。
司机
系统中的所有设备驱动程序都位于该目录中。它又分为几种类型的设备驱动程序,每种驱动程序也有相应的子目录。比如声卡的驱动对应驱动/声音。
工业程序控制( industrial process control的缩写)
该目录包含核心进程间通信代码。
模块
该目录包含可以动态加载的内置模块。
fs Linux
支持的文件系统代码。不同的文件系统有不同的子目录。例如,ext2文件系统对应于ext2子目录。
核心
主核心代码。同时,与处理器结构相关的代码放在arch/*/kernel目录中。
网
核心网络部分代码。其中的每个子目录都对应于网络的一个方面。
解放运动
这个目录包含核心库代码。与处理器结构相关的库代码放在arch/*/lib/目录中。
剧本
该目录包含用于配置内核的脚本文件。
证明文件
这个目录是一些文件,作为参考。
Linux内核源代码分析方法1。内核源代码的清晰性如果你想分析Linux,深入操作系统的本质,阅读内核源代码是最有效的途径。众所周知,成为一名优秀的程序员需要大量的练习和代码编写。虽然编程很重要,但是只会编程的人很容易把自己局限在自己的知识里。我们要想扩大知识面,就需要接触别人写的代码,尤其是比我们水平高的人写的代码。通过这种方式,我们可以跳出自己知识圈的束缚,进入别人的知识圈,去了解更多我们短时间内连自己都无法了解的信息。Linux内核是开源社区无数“大神”精心维护的,这些人都可以称得上是顶尖的代码高手。通过阅读Linux内核代码,我们不仅学到了内核相关的知识,还学到了他们的编程技巧和对计算机的理解。
我也是通过一个项目接触到Linux内核的源代码分析,从源代码分析中受益匪浅。除了获取相关内核知识,也改变了我之前对内核代码的认知:
1.内核源代码的分析并不是“高不可攀”。内核源代码分析的难点不在于源代码本身,而在于如何用更合适的方式方法来分析代码。由于内核庞大,我们无法像分析一般演示程序那样从主函数开始一步步分析。我们需要一种方法从中间“分而治之”内核源代码。这种“随需应变”的方法可以让我们抓住源代码的主线,而不是纠结于具体的细节。
2.内核的设计很漂亮。内核的特殊地位决定了内核的执行效率必须足够高,才能响应当前计算机应用的实时性要求,所以Linux内核采用C语言和汇编混合编程。但是我们都知道,软件执行的效率和软件的可维护性在很多情况下是背道而驰的。如何在保证其高效率的前提下提高内核的可维护性,取决于内核中的“漂亮”设计。
3.神奇的编程技巧。在一般的应用软件设计领域,编码的地位可能不会被太过重视,因为开发者更看重的是软件的好设计,编码只是实现手段的问题——就像用斧头劈柴一样,不用太多思考。但是,在内核中却不是这样。好的编码设计带来的不仅仅是可维护性的提升,还有代码性能的提升。
每个人对内核的理解都会不一样。随着我们对内核理解的深入,对它的设计和实现会有更多的思考和体会。因此,本文希望引导更多徘徊在Linux内核之门外的人走进Linux的世界,亲身体验内核的神奇和伟大。而且我也不是内核源代码方面的专家,所以只想分享一下自己分析源代码的经验和心得,为有需要的人提供参考和帮助。说白了,也算是对计算机行业的一点微薄贡献,尤其是在操作系统内核方面。闲话不多说(已经啰嗦了,尴尬~),我来分享一下自己的Linix内核源代码分析方法。
2内核源代码难不难?本质上,分析Linux内核代码和看别人的代码没什么区别,因为你面前的一般不是自己写的代码。先举个简单的例子。一个陌生人随便给你一个程序,让你看完源代码后解释一下程序的功能设计。我想很多觉得自己编程能力还可以的人一定觉得这没什么。只要我耐心的把他的代码从头到尾看一遍,就一定能找到答案,也确实如此。现在我们假设,如果这个人是Linus,给你一个Linux内核的模块的代码,你还会觉得那么轻松吗?很多人可能会犹豫。同一个陌生人给你的代码(当然Linus认识你不算,呵呵~),为什么给我们的感觉不一样?我认为有以下原因:
1.1号。Linux内核代码对“外界”来说有些神秘,而且庞大,可能感觉不可能一下子就下手。比如可能来自一个很小的原因——找不到主函数。对于一个简单的演示程序,我们可以从头到尾分析代码的含义,但是对内核代码的分析是完全无效的,因为没有人能从头到尾看完Linux代码(因为实在没必要,用的时候看看就行了)。
2.很多人都接触过大型软件的代码,但大部分都属于面向应用的项目,代码的形式和含义都与他们经常接触的业务逻辑有关。与内核代码不同,它处理的大部分信息都与计算机底层息息相关。比如操作系统、编译器、汇编、架构等相关知识的缺乏也会造成内核代码的阅读困难。
3.分析内核代码的方法不够合理。面对大量复杂的内核代码,如果不从全局角度出发,很容易陷入代码细节的泥潭。内核代码虽然庞大,但也有它的设计原则和架构,否则维护它对任何人来说都是噩梦!如果梳理出代码模块的整体设计思路,然后分析代码的实现,那么分析源代码可能是一件轻松愉快的事情。
针对这些问题,我个人是这么理解的。如果你还没有接触过大型软件项目,通过分析Linux内核代码,可能是积累大型项目经验的好机会(的确,Linux代码是我目前接触过的最大的项目!)。如果你对计算机的底层了解不够透彻,那么我们可以选择边分析边学习的方式来积累底层知识。刚开始分析代码可能有点慢,但是随着知识的积累,我们对Linux内核的“业务逻辑”会逐渐清晰。最后,如何从全局角度把握分析的源代码,也是我想和大家分享的经验。
3内核源代码分析方法3.1数据收集从人们对新事物的认识来看,在探索事物的本质之前,必然有一个认识新事物的过程。这个过程让我们对新事物有了初步的概念。比如我们想学钢琴,那么我们需要明白,弹钢琴需要我们学习乐理、记谱法、五线谱等基础知识,然后学习钢琴演奏技巧和指法,最后才能真正开始练琴。
分析内核代码也是如此。首先,我们需要定位要分析的代码的内容。它是进程同步和调度的代码,内存管理的代码,设备管理的代码,系统启动的代码等等。庞大的内核决定了我们不可能一下子分析完所有的内核代码,所以需要给自己一个合理的分工。正如算法设计告诉我们的,要解决一个大问题,首先要解决其中涉及的子问题。
通过定位要分析的代码的范围,我们可以利用手头的所有资源,尽可能全面地了解这部分代码的整体结构和一般功能。
这里所说的所有资源都是指百度、Google的大型网络搜索引擎、他人提供的操作系统原理、经验、资料的教科书和专业书籍,甚至是Linux源代码提供的文档、注释、源标识符的名称(不要小看代码中标识符的名称,有时它们可以提供关键信息)。简而言之,这里的所有资源指的是所有你能想到的可用资源。当然,我们不可能通过这种形式的信息收集获得我们想要的所有信息。我们只是想尽可能的全面。因为信息越全面,在分析代码的过程中可以利用的信息就越多,分析过程的难度也会越小。
这里举个简单的例子,假设我们要分析Linux的变频机制实现的代码。到目前为止,我们只知道这个术语,通过字面意思,可以大致猜测它应该和CPU的频率调整有关。通过信息收集,我们应该能够获得以下相关信息:
1.cpufreq机制。
2.性能、节能、用户空间、按需和保守频率调制策略。
3./driver/cpufreq/.
4./documentation/cpufreq .
5.pstate和C状态。
如果你能通过分析Linux内核代码来收集这些信息,你应该是非常“幸运”的。毕竟关于Linux内核的信息没有。NET和JQuery,但比起十几年前,在没有强大的搜索引擎和相关研究资料的时候,应该称得上是一个“丰收”的时代!我们甚至通过简单的“搜索”(可能需要一两天的时间)就找到了这部分代码所在的源文件目录,不得不说这样的信息简直“无价”!
3.2源代码位置从数据收集中,我们“幸运”地找到了与源代码相关的源代码目录。但这并不意味着我们真的在分析这个目录中的源代码。有时候我们找到的目录可能是零散的,有时候我们找到的目录中有很多与特定机器相关的代码,我们更关心要分析的代码的主要机制,而不是与机器相关的专门代码(这有助于我们理解内核的本质)。因此,我们需要仔细选择与代码文件相关的材料。当然,这一步不太可能一次完成,谁也不能保证所有要分析的源文件都能一次选完,不漏一个。但是我们也不用担心,只要我们能把握住与大部分模块相关的核心源文件,通过后期对代码的详细分析,自然就能全部找到。
回到上面的例子,我们仔细阅读了/documentation/cpufreq下的文档。目前,Linux源代码会将模块相关的文档保存在源代码目录的documentation文件夹中。如果要分析的模块没有文档,会增加定位关键源文件的难度,但不会引导我们找到想要分析的源代码。通过阅读文档,我们至少可以关注源文件/driver/driver/cpufreq/cpufreq . c通过这个对源文件的文档描述,结合之前收集的FM攻略,我们可以很容易的关注五个源文件:cpufreq_performance.c、cpufreq_powersave.c、cpufreq_userspace.c、cpufreq_ondemand、cpufreq_conservative.c是否找到了所有涉及的文档?别急,从它们开始,迟早会找到其他源文件。如果我们在windows下使用sourceinsight读取内核源代码,通过函数调用、符号引用搜索等功能,结合代码分析,很容易找到其他文件,如freq_table.c、cpufreq_stats.c和/include/linux/cpufreq.h。
根据搜索到的信息的流向,我们可以定位到需要分析的源文件。源位置不是很关键,因为我们不需要找到所有的源文件,我们可以推迟一些工作,直到分析代码的过程。源码位置也很关键,找到一些源文件是源码分析的基础。
3.3简单笔记简单笔记
在找到的源文件中,分析每个代码元素(如变量、宏、函数和结构)的一般含义和功能。之所以称之为简单注释,并不是说这部分的注释工作很简单,而是说这部分的注释不必过分详细,只要大致描述一下相关代码元素的含义即可。相反,这里的工作实际上是整个分析过程中最困难的一步。因为这是第一次深入内核代码,尤其是对于第一次分析内核源代码的人来说,大量陌生的GNU C语法和铺天盖地的宏定义会让人绝望。这时候你只要静下心来,找出每一个重点难点,就能保证以后遇到类似的困难时不被困住。而且,关于内核的其他知识也会像树一样不断扩展。
比如cpufreq.c文件的开头会出现“DEFINE_PER_CPU”这个宏,通过查阅资料我们基本可以了解这个宏的含义和作用。这里使用的方法和之前收集数据时使用的方法基本相同。另外,我们也可以使用sourceinsight提供的函数来查看它的定义,或者使用LKML(Linux内核邮件列表)来查找。总之,我们总是可以通过一切可能的手段得到这个宏的含义——为每个CPU定义一个独立变量。
我们不需要一次准确的描述注释(我们甚至不需要了解每个功能的具体实现过程,只需要了解大致的功能意义)。我们把收集到的数据和对后面代码的分析结合起来,不断完善注释的含义(源代码中的原始注释和标识符在这里非常有用)。通过不断的标注,不断的获取信息,不断的修改标注的含义。
当我们简单地注释所有涉及的源文件时,我们可以实现以下效果:
1.基本理解源代码中代码元素的含义。
2.找出本模块涉及的所有关键源文件。
结合之前收集的信息和数据来描述要分析的代码的整体或架构,我们可以将分析结果与数据进行比较,以确定和纠正我们对代码的理解。这样,通过一个简单的注释,就可以从整体上把握源模块的主要结构。这也实现了我们简单标注的基本目的。
3.4详细注释代码的简单注释完成后,可以认为模块的分析完成了一半,剩下的就是对代码的深入分析和透彻理解。简单的注释永远无法非常准确地描述代码元素的具体含义,所以详细的注释是非常必要的。在这一步中,我们需要理解以下内容:
1.当使用变量定义时。
2.当使用宏定义代码时。
3.函数的参数和返回值的含义。
4.函数的执行流程和调用关系。
5.结构字段的具体含义和使用条件。
我们甚至可以把这个step函数叫做detailed comment,因为在简单的注释中,函数之外的代码元素的含义基本上是清楚的。函数本身的执行流程和算法是这部分注释和分析的主要任务。
比如如何实现cpufreq_ondemand策略的算法(在函数dbs_check_cpu中)。我们需要分析这个函数使用的变量和一步一步调用的函数,找出算法的来龙去脉。最好的结果是我们需要这些复杂函数的执行流程图和函数调用关系图,这是最直观的表达。
通过这一步的注释,我们基本上可以全面掌握整个待分析代码的实现机制。并且所有的分析工作可以被认为完成了80%。这一步尤为关键。我们必须使注释中的信息尽可能准确,以便更好地理解要分析的代码内部模块的划分。虽然在Linux内核中使用了宏语法“module_init”和“module_exit”来声明模块文件,但是模块内部子功能的划分是基于对模块功能的充分理解。只有正确划分模块,才能搞清楚模块提供了哪些外部函数和变量(使用EXPORT_SYMBOL_GPL或EXPORT_SYMBOL导出的符号)。以便继续模块内标识符相关性分析的下一步。
3.5模块内部标识符的依赖性通过第四步对代码模块的划分,我们可以很容易地对模块进行逐一分析。一般我们可以从文件底部的模块入口和出口函数入手(由“module_init”和“module_exit”声明的函数一般在文件的末尾),根据它们调用的函数(自己定义的函数或者其他模块的函数)和使用的关键变量(本文件中的全局变量或者其他模块的外部变量)——我们称之为标识符,绘制一个“函数-变量-函数”依赖图。
当然,一个模块中标识符的依赖不是简单的树形结构,很多情况下是复杂的网络关系。这时,我们对代码的详细注释的作用就体现出来了。根据函数本身的含义,我们将模块划分为子函数,并提取每个子函数的标识符依赖树。
通过对标识符依赖关系的分析,我们可以清楚地看到模块定义的函数调用了哪些函数,使用了哪些变量,模块的子函数之间的依赖关系——哪些函数和变量是共享的。
3.6模块间的相互依赖模块间的相互依赖
一旦模块的内部标识符依赖图全部整理出来,就可以根据模块所使用的其他模块的变量或函数很容易地得到模块之间的依赖关系。
1cpufreq代码的模块依赖关系可以表示如下。
13.7模块架构图通过模块之间的依赖关系图,可以清晰地表达模块在整个待分析代码中的位置和作用。以此为基础,可以对模块进行分类,梳理出代码的架构关系。
1如cpufreq的模块依赖图所示,我们可以清楚地看到所有的FM策略模块都依赖于核心模块cpufreq、cpufreq_stats和freq_table。如果我们把三个依赖模块抽象成代码的核心框架,这些FM策略模块都是建立在这个框架上的,它们负责和用户层交互。核心模块cpufreq提供驱动和其他相关接口与系统底层交互。因此,我们可以得到下面的模块架构图。
1当然,架构图并不是模块的无机拼接,我们需要结合自己查阅过的资料来丰富架构图的含义。所以这里架构图的细节会随着不同人的理解而有所不同。但是架构图主体的意思基本是一样的。至此,我们已经完成了所有待分析内核代码的分析工作。
评论前必须登录!
注册