Linux内核非常庞大,里面包含了上万个文件,非常的复杂。要想移植或者是修改内核里面的东西,首先需要了解内核的整体架构,知道文件存放的位置以及编译关系,内核源码结构:
- arch:存放体系结构相关的代码,里面存放着各个架构CPU的代码。如arch/arm/、 arch/i386/等。
- block:存放块设备的通用函数。
- crypto:存放常用加密和散列算法,还有一些压缩和CRC校验算法。
- drivers:所有的设备驱动程序,里面每一个子目录对应一类驱动程序。
- fs:linux支持的文件系统的代码,每个子目录对应一种文件系统。如fs/jffs2、fs/ext2等。
- init:内核的初始化代码,其main.c文件中的start_kerner是内核引导后运行的第一个函数。
- ipc:进程间通信代码
- include:内核头文件,有基本文件(存放在include/linux目录下)、各种驱动的头文件(如include/mtd/)以及各种体系相关的头文件(include/asm-arm)。配置内核后,include/asm/是个链接文件,指向配制的体系头文件(如include/asm-arm)。
- kernel:内核管理的核心代码,与处理器相关的代码位于arch/*/kernel/目录下。
- lib:内核使用到的一些库函数代码。
- mm:内存管理代码,与处理器相关的内存管理代码位于arch/*/mm/目录下。
- net:网络支持代码,每个子目录对应于网络的一个方面。
- security:安全、密钥相关的代码。
- sound:音频设备的驱动程序。
- usr:用来制作一个压缩的cpio归档文件
- documentation
- scripts
制作内核的流程:修改内核源码——>make menuconfig 配置内核——>make uImage编译生成镜像。make clean清除编译生成的一些中间文件,make disclean 彻底清除配置文件。
要分析内核,首先需要分析的是顶层目录下的Makefile(因为内核是根据Makefile规则来编译生成的)。Makefile会指定哪些文件被编译以及如何编译(是编译进内核还是编译成模块)、指定链接时的顺序。
在顶层的Makefile中发现 和可以知道编译生成的内核文件依赖于:再次跟踪可以发现vmlinux-init依赖的head-y在arch/arm/Makefile中规定了,使用的是kernel中的head-nommu.S和init_task.c文件。,而vmlinux-main所依赖的$(core-y)、$(libs-y)、$(drivers-y) 、$(net-y),这些依赖
,上述中的所以依赖都会执行相应目录下的Makefile,每个子目录都会生成一个built-in.o文件。编译后链接使用的链接脚本为arch/$(ARCH)/kernel/vmlinux.lds。生成的Image、uImage文件存放在arch/$(ARCH)/boot 中。
首先是哪些文件会被编译以及如何编译:
顶层目录Makefile决定内核根目录下哪些子目录将被编译进内核。从上面可以知道根目录中的很多文件(绝大部分)都会被编译进内核,而和架构相关的Makefile也会被包含进来(需要使用架构相关的head.S 以及init_task.c),所以在编译内核时需要指定使用的架构和交叉编译工具(如ARCH = arm CROSS_COMPILE = arm-Linux-)。
arch/$(ARCH)/Makefile决定架构目录下的哪些文件将编译进内核。在架构相关的Makefile中扩展了lib和core的内容,把和架构相关的库文件以及和架构相关的core文件都编译进去。
各级子目录下的Makefile(存放这样的条目)决定所在的目录中哪些文件会将编译进内核,哪些文件编译生成驱动模块。在配置内核后,会在顶层目录下生成配置文件.config。.config会生成include/config/auto.conf和include/linux/autoconf.h文件。auto.conf文件基本上和.config相同(把一些注释给去除了),这个文件的作用主要是给各级子目录使用,用来确定(使用宏来代替具体的值y或者是m)子目录中的文件是否要编译以及如何编译。
子目录中的Makefile有着以下的规定:
①obj-y用来定义哪些文件被编译进内核,obj-y定义的.o文件由当前目录下的.c或.S文件编译生成,连同下级子目录的built-in.o文件一起被组合成built-in.o文件供上一层的Makefile使用。
②obj-m是用来定义哪些文件被编译成可加载模块。一个模块可以由一个或多个.o文件文件组成。对于有多个源文件的模块时,除了在obj-m中增加一个.o文件外,还需要定义一个<module_name>-objs变量来告诉Makefile这个.o文件由哪些文件组成。
③lib-用来定义哪些文件被编译成库文件。lib-y中定义的文件编译后会打包成当前目录下的一个库文件lib.a。同时出现在obj-y、lib-y中的.o文件,不会被包含进lib.a中。要把这个lib.a编译进内核,需要在顶层Makefile中libs-y变量中列出当前目录。
④obj-y、obj-m还可以用来指定要进入的下一层子目录,Linux中一个Makefile只负责生成当前目录下的目标文件,子目录下的目标文件由子目录的Makefile生成。obj-$(CONFIG_XX) +=subdir/ 编译时会进入subdir中编译。
note:Makefile中经常可以看到$(patsubst %/,%/built-in.o,$(net-y))这种类型的文件,patsubst是个字符串处理函数,$(patsubst pattern, repalcement,text)表示寻找text中符合pattern的字符,用replacement替换他们。
所以Makefile的作用如下|:
- 根据配置文件.config中定义的一系列变量,决定哪些文件被编译进内核、哪些文件被编译成模块以及涉及哪些子目录。
- 顶层Makefile和arch/$(ARCH)/Makefile决定根目录下哪些子目录、arch/$(ARCH)/目录下哪些文件和目录将被编译进内核。
- 各级子目录下的Makefile决定所在目录下哪些文件被编译以及如何编译,进入哪些子目录继续编译他们的Makefile。
- 最后根据链接脚本arch/$(ARCH)/kernel/vmlinux.lds来生成内核镜像文件vmlinux。
内核配置分析
无论是执行make menuconfig ARCH = arm CROSS_COMPILE=arm-linux还是执行make config ARCH = arm CROSS_COMPILE=arm-linux来配置内核。所有的配置工具都是根据arch/$(ARCH)/Kconfig文件来生成配置界面,这个文件是所有配置文件的总入口,它会包含其他目录的Kconfig文件。内核源码每个子目录中都会有一个Makefile和Kconfig文件,Kconfig文件用于配置内核,就是各种配置界面的源文件。内核配置工具读取各个Kconfig文件,生成配置界面供开发人员配置内核,最后生成配置文件.config。
内核的配置界面以树状的菜单形式组织,主菜单下有若干个子菜单,子菜单下又有子菜单或配置选项。每个子菜单或选项可以有依赖关系。依赖关系用来确定它们是否显示。
Kconfig的语法如下:Kconfig文件的基本要素:config条目。config用来配置一个选项。基本的格式如下。下面黑色加粗的是必须存在的:
- config是关键字,表示一个配置选项的开始,后面的是配置选项的名称。
- bool表示变量的类型,即CONFIG_SMDK2440_CPU2440的类型。类型有bool、tristate、string、hex、int五种,其中bool只能取y和n两种,tristate变量可以取值有y、n、m三种。变量后面的字符串是提示信息,完整的格式是<prompt>“prompt” [ "if" <expr> ]。
- depends on ARCH_S3C2440表示依赖关系。只有ARCH_S3C2440选项选中时才会显示当前的配置选项的提示信息。完整的格式是“depends on”/"requires"<expr>,如果依赖条件不满足则使用默认的值。
- default y if ARCH_S3C2440,表示默认值是y。完整的格式:“default” <expr> 【if <expr>】。
- select CPU_S3C2440表示当前配置选项被选中时,配置选项CPU_S3C2440也会被自动选中。
- help和“---help---”都是帮助信息。help帮助信息结束的条件是,遇到一行的缩进距离比第一行帮助信息的缩进距离小时。
- menu条目:是以menu开始,endmenu结束。menu后面接的是菜单名。menu和endmenu之间存放着config条目。这些条目就是进入这个菜单后显示出来的信息。
- choice条目,choice将多个类似的配置选项组合在一起,供用户单选或多选。格式是choice .......endchoice。choice条目中定义的变量类型只能有两种:bool和tristate,不能同时有这两种变量类型。bool类型只能在多个选项中选择一个y。对于tristate类型,要么把多个选项都设置为m,要么就选一个y。
- comment条目。用来定义一些帮助信息,在配置过程中出现在界面的第一行。格式“comment” <prompt>
- source条目。此条目用于读入另一个Kconfig文件。完整格式“source” <prompt> 如source “net/Kconfig”把net/Kconfig配置文件读入。
Linux内核的配置选项多达上千个,一个个地进行选择浪费时间也对开发人员的要求比较高。普通的做法是在某个默认配置文件的基础上进行修改。如先加载arch/arm/donfigs/目录中的默认配置文件,然后在微调整下就可以了。
配置界面主菜单的类别:
- code maturity level options :代码成熟度选项,用于包含一些正在开发的或者不成熟的代码、驱动程序,一般不设置。
- general setup:常规设置,如增加附加的内核版本号、支持内存页交换功能、system V进程间通信等。一般使用默认配置。
- loadable module support:可加载模块支持,通常会选择里面的enable loadable module support、module UNloading、automatic kernel module loading。
- block layer:块设备层,用来设置块设备的一些总体参数。一般使用默认设置。
- system type:系统类型,选择CPU的架构、开发板类型等与开发板相关的配置选项。
- bus support:PCMCIA/CardBus总线的支持。
- kernel features:用于设置内核的一些参数,比如是否支持内核抢占,是否支持动态修改系统时钟等。
- boot options:启动参数,比如设置默认的命令行参数等。一般很少修改。
- floating point emulation:浮点运算仿真功能,一般使用“NWFPE math emulation”。
- userspace binary formats:可执行文件格式:一般选择支持ELF、a.out格式。
- power management options:电源管理选项。
- networking:网络协议选项,一般选择“Networking support”支持网络、“packet socket”支持socket接口功能,“TCP/IP networking”支持TCP/IP网络协议。
- device driver:设备驱动程序,几乎包含了Linux的所有驱动程序。
- file system:文件系统,选择要支持的文件系统(如EXT2、JFFS2)。
- profiling support:对系统的活动进行分析,仅供内核开发者使用。
- kernel hacking:调试内核时的各种选项。
- security options:安全选项。
- crypt options:加密选项。
- library routines:库子程序。不需要操作。
最后总结下:虽让内核中的文件有着上万个,数量及其的庞大。但是分类归纳之后不算是太复杂。内核中的Makefile是用来指定编译的规则,Kconfig文件是用来提供给用户配置选项的文件,客户编译后会生成客户的配置文件.config。.config决定了哪些目录下的哪些文件将会别编译,以及如何编译。同时在C代码中还会根据.config配置文件决定代码段是否被编译(条件编译)。