目录
背景知识
Linus Torvalds编写了最初的Linux的原始版本,当时他还只是芬兰赫尔辛基大学的学生。从初始版本开始,Linux已经成长为一个成熟的操作系统,它稳定、可靠,并且包含了一些高端特性,足以与最好的商业操作系统抗衡。
内核版本
Linux内核的官方网站是www.kernel.org。你会常常听到主线(mainline)代码或主线内核这样的词,它们指的就是网站kernel.org上提供下载的源码树。
在开发的早期阶段,开发人员选择了一种编号系统来区分两种内核代码树,一种是针对开发和实验的非稳定版本,另一种是可供生产和使用的稳定版本。这种编号命名方式包含主版本号、次版本号和序列号。在2.6版本之前,次版本号是偶数,表示稳定版本;次版本号是奇数,则表示它是一个开发版本。例如:
-
Linux 2.4.x——稳定内核版本
-
Linux 2.5.x——实验(开发)版本
-
Linux 2.6.x——稳定内核版本。
很容易可以判断出你使用的内核版本。内核源码树的顶层目录中包含一个makefile文件,这个文件的开始几行详细说明了内核的版本号。对于2.6.30的内核发布版本,这几行的内容如下:
VERSION=2
PATCHLEVEL=6
SUBLEVEL=30
EXTRAVERSION=
NAME=Man-Eating Seals of Antiquity
在这个makefile文件的后面,以上宏会用来定义一个描述内核版本号的宏,就像这样:
KERNELVERSION=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
这样,从控制台就可以查询它的值了。在一个运行Linux的系统中,在命令行提示符后输入以下命令,就可以查询到内核的发布信息:
$ cat /proc/version
Linux version 2.6.13 (chris@pluto) (gcc version 4.0.0 (DENX ELDK 4.04.0.0)) #2 Thu Feb 16 19:30:13 EST 2006
如果你正在开发一些新的内核特性,可以像以下这样设EXTRAVERSION:
EXTRAVERSION=-foo
随后,当你使用命令cat/proc/version时,你会看到Linux version 2.6.13-foo,而这会帮助你区分出自己内核的开发版本。
内核源代码库
Linux内核源码的官方站点是www.kernel.org。在这个网站上,你可以找到Linux内核的当前和历史版本以及数量众多的代码补丁。
如果你想知道如何找到适用于特定应用的内核代码,最佳途径就是获取最新的稳定版Linux源码树。检查一下代码是否支持你使用的处理器,并搜索Linux内核邮件列表(mailing list),从中寻找与你的应用相关的所有代码补丁和问题。同样也找一下你最感兴趣的邮件列表,并在其中搜索一番。
使用git下载内核代码
大多数主流Linux桌面发行版(windows可以使用scoop安装)都带有git。例如,在Ubuntu中,可以输入以下命令,将git安装到你的桌面PC或笔记本上:
$ sudo apt-get install git #每个发行版名字可能不同
Ubuntu – Ubuntu Packages Search可以从这个网站找到软件包。
当你的系统中安装好git后,可以使用命令git clone来克隆一个git源码树:
$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git linux-2.6
这条命令的执行结果是创建一个名为linux-2.6的子目录,位于你所输入命令的当前目录中。这个子目录中包含了一个克隆自kernel.org的内核源码树。
linux内核的构造
顶层源码目录
顶层源码目录包含以下列出的子目录。(为清晰和简洁起见,我们省略了非目录项和那些用于源码控制的目录。)
图4-1 顶层源码目录
这些目录中的大多数都另外包含了几层子目录,在其中存放源码、makefile和配置文件。到目前为止,Linux内核源码树的最大分支位于../drivers目录。在这个目录中,你可以找到Linux支持众多硬件设备的驱动代码,支持的设备包括各种以太网卡,USB控制器等。你可能已经猜到了,.….arch子目录是第二大分支,其中包含的代码文件支持20多种处理器架构。 顶层Linux子目录中还有一些其他文件,包括顶层makefile,一个隐藏的配置文件(.config文件),以及与内核构建无关的信息性文件。最后,介绍两个重要的构建目标(build target)文件:System.map和vmlinux,内核构建成功后,在顶层源码目录中会生成这两个文件,其中vmlinux是内核主体(kernel proper)。
编译内核
内核构建系统在成功构建内核后,会生成一些公共文件以及一个或多个与具体架构相关的二进制模块。无论采用什么架构,构建内核后总是会生成公共文件。有两个重要的公共文件是System.map和vmlinux。前一个文件有助于调试内核,它包含了一个人类可读的内核符号的列表以及它们各自的地址。后一个文件是一个与具体架构相关的可执行文件,而且符合ELF格式。这个文件是由顶层内核makefile文件针对具体架构生成的。如果在编译内核时包含了用于调试的符号信息,这些信息会保存在vmlinux镜像中。实际上,虽然它是一个ELF格式的可执行文件,这个文件几乎从不直接用于引导系统。我们将内核代码的目标架构配置为ARM XScale,并执行make命令进行构建。内核代码是针对ADI Engineering公司的Coyote参考板进行配置的,这块参考板上使用了基于ARM核心的英特尔IXP425网络处理器,配置内核代码的命令为:
$ make ARCH=arm CROSS_COMPILE=xscale_be- ixp4xx_defconfig
这个命令并没有构建内核;它只是将内核源码树的架构配置为XScale,为后面的内核构建做准备。这个命令基于文件ixp4xx_defconfig中的默认值生成一个默认配置文件(.config文件),用于内核构建。
图4-2 内核构建输出信息
命令行中同时指定了目标架构(ARCH=arm)和工具链(CROSS_COMPILE=xscale_be-)。这就强制make使用XScale工具链来构建内核镜像,并使用内核源码树的arm分支来编译生成镜像中与架构相关的部分。我们同时也指定了一个目标文件,称为zlmage。很多架构都使用zlmage来命名构建出的目标文件。如今,主流内核在构建时会自动生成合适的默认目标文件,因此,你不需要在命令行中指定zImage或其他目标了。接下来,你可能会注意到,构建过程中每个步骤所使用的实际命令都被隐藏了,取而代之的是一些简写的符号。这样做的目的是使构建输出信息更加清晰,从而使开发人员将更多注意力集中在构建过程中出现的问题上,特别是编译器的告警信息。如果你希望或是需要查看完整的构建步骤,可以在make命令行中定义v=1而使其输出详细的信息:
$ make ARCH=arm CROSS_COMPILE=xscale_be- V=1 zImage
当构建和编译生成所有的中间文件及程序库之后,它们被组合到一个大的ELF文件中,这个文件就是我们的构建目标,称为vmlinux。虽然它与具体的架构有关,但vmlinux是一个公共目标。所有Linux支持的架构在构建时都会生成这个文件,并且它位于顶层源码目录中,很容易找到。
内核主体:vmlinux
注意这一行:
LD vmlinux
vmlinux文件就是实际的内核主体(kernel proper)。它是一个完全独立的单一ELF镜像。也就是说,vmlinux这个二进制文件不包含任何未解析的外部引用。当有合适的环境(通过一个用于引导Linux内核的引导加载程序)可以执行它时,它会引导硬件单板并在上面运行,生成功能完备的内核。(实际上,我们很少直接使用ELF目标文件vmlinux,而是使用它的压缩形式。)
图4-3 链接阶段:vmlinux
内核镜像的组成部分
vmlinux镜像是由多个二进制镜像组合生成的。链接命令第一行指定了输出文件(-o vmlinux)。第二行指定了链接器脚本文件(-T vmlinux.Ids),这个文件详细规定了如何链接生成内核二进制镜像。
从第三行开始指定了多个对象模块,是它们组成了最终的二进制镜像。注意其中指定的第一个对象为head.o。此对象是由../arch/arm/kernel/head.S文件汇编生成的,这是一个与具体架构相关的,用汇编语言编写的源文件,用于完成非常底层的内核初始化。如果你想寻找内核执行的第一行代码,从这个文件开始搜索是很明智的,因为,经过这个链接阶段,head.o会最终成为内核镜像的第一部分。
接下来的对象init_task.o,用于建立内核所需的初始的线程和进程结构。在这个对象之后,是很多对象模块的集合,而且每个模块都有个共同的名字:built-in.o。然而,你会注意到这些built-in.o对象是由内核源码树的不同部分编译生成的,built-in.o对象名之前的不同文件路径说明了这一点。内核镜像中会包含这些二进制对象。 也许你并不会感到惊讶,3个最大的二进制成员分别是由文件系统代码、网络代码和内置设备驱动程序代码编译生成的。如果你将内核代码和架构相关内核代码编译出的对象放在一起,它们合起来就成为了第二大二进制组件。这个成员中包含了调度器、进程及线程管理、定时器管理和其他核心的内核功能。当然,内核也会包含一些与具体架构相关的功能,比如底层的上下文切换、硬件层中断和定时器的处理、处理器异常的处理等。这些内容在../arch/arm/kernel中可以找到。 需要牢记的是,我们是在分析一个具体的内核构建的例子。在这个特定的例子中,我们构建了一个具体针对ARM XScale架构的内核,更确切地说,是针对ADI Engineering公司参考板上的英特尔IXP425网络处理器。我们可以看到,在图4-4中,与具体机器相关的二进制成员是arch/arm/mach-ixp4xx。内核镜像中包含了与具体架构相关的部分,这个部分是由架构和机器类型(处理器/参考板)决定的,类型不同,生成的二进制成员就不同,从而最终的vmlinux镜像的组成也会略有差别。明白了一个例子后,其他架构的情况就很容易理解了。
图4-4 vmlinux组成成员
表4-1列出了图4-4中的每个成员,并描述了组成vmlinux镜像的每个二进制组件。
表4-1 vmlinux镜像的成员
成员 | 描述 |
---|---|
arch/arm/kernel/head.o | 内核中与具体架构相关的启动代码 |
arch/arm/kernel/init_task.o | 内核所需的初始的线程和任务结构体 |
init/built-in.o | 主要的内核初始化代码 |
usr/built-in.o | 内置的initramfs镜像 |
arch/arm/kernel/built-in.o | 与具体架构相关的内核代码 |
arch/arm/mm/built-in.o | 与具体架构相关的内存管理代码 |
arch/arm/common/built-in.o | 与具体架构相关的通用代码,因架构而异 |
arch/arm/mach-ixp4xx/built-in.o | 与具体机器相关的代码,主要用于初始化 |
arch/arm/nwfpe/built-in.o | 与具体架构相关的浮点运算模拟(floating-point emulation)代码 |
kernel/built-in.o | 内核自身的通用部分 |
mm/built-in.o | 内存管理代码的通用部分 |
fs/built-in.o | 文件系统代码 |
ipc/built-in.o | 进程间通信,比如SysV IPC |
security/built-in.o | Linux安全组件 |
crypto/built-in.o | 加密API |
block/built-in.o | 内核块设备层的核心代码 |
arch/arm/lib/lib.a | 与具体架构相关的通用程序库,因架构而异 |
lib/lib.a | 通用的内核辅助函数 |
arch/arm/lib/built-in.o | 与具体架构相关的辅助函数 |
lib/built-in.o | 通用的程序库函数 |
drivers/built-in.o | 所有的内置驱动,不包含可加载的模块 |
sound/built-in.o | 声音驱动 |
firmware/built-in.o | 驱动固件对象 |
net/built-in.o | Linux网络 |
.tmp_kallsyms2.o | 内核符号表 |
当我们谈论内核主体时,指的就是vmlinux镜像文件(处于顶层内核源码目录中)。很少有平台直接使用它来引导系统。主要是因为我们引导系统所使用的镜像一般都是经过压缩的,引导加载程序至少要能够解压这个镜像。很多平台都需要不同类型的接人点以便实现代码解压。对于不同的架构、机器类型、引导加载程序和引导需求,这个镜像文件会有不同的组成方式。
子目录的布局
有两个文件很常见,可以在很多内核子目录中看到,它们是Makefile和Kconfig。这两个文件推动内核的配置和构建过程。
内核构建系统
config文件
.config文件用于构建Linux内核镜像的配置蓝图。在一个Linux项目初期,你很可能会花费大量精力来创建适于你的嵌入式平台的配置。有几种编辑器,既有基于文本的,也有基于图形界面的,专门用于编辑内核配置文件。使用这些编辑器生成的配置会被写入到一个名为.config的配置文件中,这个文件位于顶层Linux源码目录,并用于内核构建。
有几个make命令会删除配置文件而不给出任何警告。最常见的就是make distclean。这个make目标的设计初衷是让内核源码树回到原始的、未配置的状态。这包括删除源码树中所有的配置数据,当然也会删除原先的.config文件。
如果你在没有备份.config文件的情况下就执行make distclean或make mrproper命令,你就会体会到我们的痛苦。(已经提醒过你了——记得备份.config文件!)
要理解.config文件,你需要理解Linux内核的一个重要特征。Linux采用单体(monolithic)内核结构。也就是说,整个内核是由代码编译并静态链接生成的,是一个单一的可执行文件。然而,也可以编译一组源码文件,并通过增量链接的方式生成一个对象模块,它可以动态加载到运行的内核中。内核一般通过这种方式来支持大多数常见的设备驱动程序。在Linux中,它们被称为可加载模块,也常被统称为设备驱动程序。当内核启动完成后,可以使用特定应用程序将可加载模块动态安装到运行的内核中。
CONFIG_USB=m,说明这个内核配置中包含了USB子系统,并且它会被编译成一个可动态加载的模块(=m)。当内核启动完成后,我们可以使用工具加载它。这个选项的另一个值是=y,在这种情况下,USB模块会被编译和静态链接到内核镜像中,成为它的一部分。在这种情况下,USB子系统会被最终编译到.../drivers/built-in.o中,这是个复合二进制对象。如果一个设备驱动程序被配置成可加载的模块,它的代码不会被编译到内核主体中,而是被编译成一个独立的可加载的模块,并在内核启动完成后被安装到运行的内核中。
注意CONFIG_USB_DEVICEFS=y声明。这个配置选项所表示的含义稍有不同。在这种情况下,USB_DEVICEFS(配置选项一般采用这种缩写方式)并不代表一个独立的模块,而是指一个可以在USB驱动中启用或禁止的特性。这个选项并不一定会合成被编译到内核主体中(=y)的模块。相反,它能启用一个或多个特性,这些特性作为额外的对象模块被编译到USB设备驱动程序这个总的模块中。通常,通过配置编辑器中的帮助文本,或是配置编辑器中显示的配置选项之间的层次结构,我们可以更清楚地看出这种差别。
配置编辑器
内核配置子系统包含几个配置目标,用于make命令。我们在这里一一列出,并概述其作用,其中的文本说明来自命令 help的输出信息:
-
config——使用基于命令行的程序来更新当前配置;
-
menuconfig——使用基于菜单的程序来更新当前配置;
-
xconfig——使用基于QT的前端更新当前配置;
-
gconfig——使用基于GTK的前端更新当前配置;
-
oldconfig——以现有的.config文件为基础来更新当前配置;
-
silentoldconfig——与oldconfig相同,但不输出任何信息;
-
randconfig——创建新的配置文件,其中包括所有配置选项的随机答案;
-
defconfig——创建新的配置文件,其中包括所有配置选项的默认答案;
-
allmodconfig——创建新的配置文件,尽可能地将选项配置成模块;
-
allyesconfig——创建新的配置文件,将所有选项配置成yes;
-
allnoconfig——创建新的配置文件,将所有选项配置成no,即最小化的配置。
在命令行中,进入顶层内核目录,并输入命令make gconfig,你就可以启动这个配置编辑器。
当你退出配置编辑器时,它会提示你是否保存修改。如果选择保存修改,全局配置文件.config就会被更新(如果不存在,则会被创建)。我们在前面介绍过,顶层makefile会使用这个.config文件来构建内核。
大多数内核软件模块也通过.config文件间接地读取配置内容,原理如下。在构建过程中,构建系统会处理这个.config文件,并生成一个名为autoconf.h的C语言头文件,放在目录../include/linux中,这个文件是自动生成的。尽量不要直接修改这个文件,因为当配置有变动并且新的构建开始后,你所做的修改就丢失了。很多内核源文件直接使用预处理指令#include来包含这个文件。内核构建文件在每条内核编译命令行中都包含了这个autoconf.h文件,具体是使用了编译命令gcc的-include选项,如下所示:
gcc ... -include include/linux/autoconf.h ... <somefile.c>
各个内核模块就是通过这种方式来访问内核配置的。
Makefile目标
在顶层Linux源码目录中输入命令make help,它会显示一长串从源码树中生成的目标列表。最常见的使用make的方式是不指定目标。在这种情况下,它会生成内核ELF文件vmlinux和针对所选架构的默认二进制镜像(例如,x86架构的bzImage)。使用make时,如果不指定目标,它也会编译所有由配置文件指定的设备驱动程序模块(内核可加载模块)。
很多架构和机器类型都需要一个二进制镜像目标,而这个目标与具体使用的架构和引导加载程序有关。比较常见的这类目标是zImage。对于很多架构来说,这就是默认的二进制镜像目标,可以被加载到目标嵌入式系统中并运行。新手常犯的一个错误就是将bzImage指定为make的目标。然而,bzImage目标是针对x86/PC架构的。有一个常见的错误观点,认为bzImage是指经过压缩工具bzip2压缩过的镜像,其实不然,bzImage是指大(big)的zImage。bzImage只适用于PC兼容机就足够了,这些机器中包含了工业标准的PC型BIOS。
图4-5中执行make help时的输出信息。可以看出,有很多目标可以使用。列出了每个目标,并简要介绍了作用。需要特别注意的是,即使是help目标(在执行命令make help时,我们指定了help为make的目标)也是与具体架构相关的。如果你在执行make help时指定了不同的架构,其输出的也会不同。
图4-5 部分Makefile目标
目标前有个星号(*),表示此目标会默认构建。很多以defconfig结尾的目标(*defconfig),它们都代表了默认配置。
内核配置
差不多有300个内核子目录都包含了名为Kconfig(或者是个带扩展名的类似名称,比如Kconfig.ext)的文件。这个文件用于配置其所在目录的源码的特性。Kconfig中的每个配置参数都有附带的帮助文本,配置子系统会解析Kconifg的内容,并提示用户做出配置选择。
配置工具(比如前面介绍的gconf)会读取各个子目录中的Kconfig文件,首先读取的是arch子目录中的Kconfig文件。它是在Kconfig的makfile中读取的,这个makefile包含了与下列内容类似的相关条目:
ifdef KBUILD_KCONFIG
Kconfig := $(KBUILD_KCONFIG)
else
Kconfig : = arch/$(SRCARCH)/Kconfig
endif
...
gconfig: $(obj)/gconf
$< $ (Kconfig)
根据你选择的具体架构,gconf会读取该架构对应的Kconfig文件,并将其内容作为顶层配置定义。Kconfig文件中包含了很多类似这样的指令行:
source "drivers/pci/Kconfig”
这条指令告诉配置编辑器,从内核源码树的其他位置读取另一个Kconfig文件。每种架构都包含很多这样的Kconfig文件,这些Kconfig组合起来成为一个完整的配置集合,当用户配置内核时,配置集合会以菜单的形式展现在用户面前。每个Kconfig文件都可以随意指定处于源码树其他位置的Kconfig文件。配置工具——这里是gconf——会递归读取所有这些链接在一起的Kconfig文件,并相应地构造出配置时所用的菜单结构。
图4-6是一个树状结构视图,其中列出了与ARM架构相关的部分Kconfig文件。这个例子其内核配置是由473个不同的Kconfig文件定义的。为了节省篇幅和表达清晰,省略了其中大多数文件,目的是显示出整体的结构。如果将它们全部列出,需要占用好几页纸。
图4-6 arm部分Kconfig文件
正如我们在前面所提到的,这些Kconfig文件组合在一起决定了配置的菜单结构和配置选项,当用户配置内核时会看到它们。
图4-7 gconf配置内核时的屏幕显示
定制配置选项
在这个Kconfig文件片段中,可以看到这里定义了一个名为System Type的菜单项。在ARM system type提示之后有一系列与ARM架构相关的选择项。文件的最后通过source指令包含了具体与IXP4xx相关的一些Kconfig定义。你可以在arch/arm/mach-ixp4xx/Kconfig中添加定制的配置开关。
图4-8 添加Kconfig内容
在配置编辑器(在这个例子中是gconf)运行起来后,如果你选择支持两个定制硬件平台中的一个,那么.config文件中就会包含对应这些新选项的宏定义。同所有的内核配置选项一样,每个宏定义都是以CONFIG_开头,以表示这是个内核配置选项。结果是.config文件中定义了两个新的配置选项,并记录了它们的状态。
图4-9 生成的.config文件
注意一下与Vega和Constellation硬件平台相关的两个新配置选项。如图4-4所示,选择支持Vega,在.config文件中,可以看到一个新的CONFIG选项(CONFIG_ARCH_VEGA)被选中,它代表Vega板,并且它的值被设置为y。同样也注意到,文件中还有一个与Constellation相关的CONFIG选项,但它未被选中。
内核Makefile
在构建内核时,makefile会扫描配置文件,并根据其内容决定需要进入哪些目录和编译哪些源文件。在前面的例子中,我们想对两个定制硬件平台(Vega和Constellation)添加支持,为了完成这个任务,我们来看一下相关的makefile,它会读取配置文件并根据其中的定制选项而采取行动。
因为在这个例子中你是在处理与硬件相关的选项,现假设有两个硬件设置模块,分别称为vega_setup.c和constellation_setup.c,代表了两块定制硬件板。我们已经将这两个C源文件放在了内核源码树的.…./arch/arm/mach-ixp4xx子目录中。
而仅仅为此,内核构建系统的开发人员就已经做了大量的工作。对于只是想在内核中为定制硬件添加支持的普通开发人员,内核构建系统的良好设计让这类定制工作很容易。
我们的目的是基于配置选项有条件地引入新的硬件设置函数,只需将下面两行加到makefile(.../arch/arm/mach-ixp4xx/Makefile)的末尾就大功告成了:
obj-$(CONFIG_ARCH_VEGA) +=vega_setup.o
obj-$(CONFIG_ARCH_CONSTELLATION) +=costellation_setup.o
完成这些步骤后,我们的工作就完成了。我们在内核中简单地添加了一些设置函数,以支持具体的定制的硬件。采用类似方法,你应该能够自行修改内核的配置/构建系统了。
内核文档
Linux源码树中包含了很多信息。然而,将它们都阅读一遍有些难度,因为.../Documentation目录包含了大约1300个文档,分布在118个子目录中。阅读这些资料时你需要注意一点:因为内核开发和发布的速度很快,这些文档可能很快就过时了。尽管如此,它们常常会为你提供一个良好的起点,你可以从中了解有关某个内核子系统或概念的基本知识。
另外,不要忽视了Linux文档项目(Linux Documentation Project),其主页是www.tldp.org,从这个网站上你可以找到某个文档或man page的最新版本。对于我们前面讨论的内核构建系统,内核源码树中也有相关文档,位于子目录.../Documentation/kbuild中。 如果不提一下Google,任何关于内核文档的讨论都是不完整的。在学习过程中,你会碰到不少困难,并且想提问一些问题,这时你应当使用Google进行搜索,因为这些困难和问题很有可能已经被别人解决或回答了。花一些时间让自己熟练从因特网中搜索问题的答案。你会发现很多邮件列表以及资讯中心,其中包含了有关你的具体项目或问题的有价值的信息。
获取定制的Linux内核
一般来说,你可以从3个途径获得针对你的硬件平台的嵌入式Linux内核:购买一个合适的商业嵌入式Linux发行版;下载一个免费的嵌入式发行版,而它适于你的架构和处理器并支持你的特定硬件平台;找一个和你的应用最相近的开源Linux内核,并自行移植。
虽然将一个开源内核移植到你的定制硬件板卡上并不一定很困难,但需要投入大量的管理和开发资源。采用这种方式,你可以获取免费软件,但是要在开发项目中部署Linux根本不会是免费的。即使对于一个功能很少的小型系统,你也需要很多其他工具和软件,而不仅仅是Linux内核。
还需要做些什么
本章将重点放在了Linux内核本身的布局和构造上。你可能已经发现了,对于一个基于Linux的嵌人式系统来说,Linux内核只是其中一个很小的组件。除了Linux内核之外,你还需要以下工具和软件,用来开发、测试和发布嵌入式Linux产品:
-
引导加载程序,你需要将它移植到特定硬件平台上,并做相应的配置
-
适合于你所选架构的交叉编译器和相关的工具链
-
文件系统,其中包含很多软件包——主要是二进制可执行文件和程序库,而且它们是针对本地硬件架构和处理器而编译的
-
设备驱动程序,内核通过它们访问硬件板卡上的定制设备;开发环境,包括主机上的工具和软件
-
Linux内核源码树,并且适合于特定的处理器和硬件板卡
扩展资料
-
.../Documentation/kbuild/*
-
Tool Interface Standard (TIS) Executable and Linking Format (ELF) Specification
-
Linux内核设计与实现(原书第3版),Robert Love
-
Just for Fun,Linus Torvalds/David Diamond
参考
-
Christopher Hallinan. 嵌入式Linux基础教程