Linux|Linux内核配置和编译

一、配置前先确认Makefile来检查交叉编译工具链和SoC架构有没有设置对。
CROSS_COMPILE ?= 交叉编译工具链的安装目录
ARCH = SOC的 架构,比如arm,主要目的是为了编译时能找到arch/arm目录。
二、内核配置,分二步
(1)第一步配置 make xxx_defconfig
xxx文件和具体的开发板相对应,若是ARM架构的话,在arch/arm/configs/目录下, 这一步其实就是把arch/arm/configs/目录下的xxx文件中的内容,复制到生成的 .config文件中(arch/arm/configs目录下的这么多个xxx_defconfig哪里来的?其实这些文件都是别人手工配置好适合一定的开发板的.config文件后自己把.config文件保存过去的。针对这个SoC的开发板的最初配置肯定是SoC原厂的工程师去做的)。
若最后只要出现:configuration written to .config证明我们的配置成功。通过这一步配置ls -a命令便可以看到.config隐藏文件。配置的关键就是得到.config文件,可以说内核的整个配置过程就是围绕.config来展开的。其作用类似与uboot中的include/configs/目录下的xxx.h文件(uboot便是利用它完成配置的),内核在编译过程中会读取生成的.config文件中的配置项,并且用这些配置项去指导整个编译链接过程。
打开.config文件便可发现,它的格式类似于脚本文件,其中内容是一行一行的配置项和配置值,随便抽取一行CONFIG_ARM=y。这样的配置值有两三千行,由此可见linux内核是高度可配置的。正是因为linux内核的配置项太多太繁杂了,因此linux内核不能再像uboot那样直接手工配置,而是发明了一个图形化的配置工具menuconfig,来降低配置难度。当然你的大脑足够厉害能够记住这二三千行的话,你每次可直接在.config文件中手动书写/修改.config文件完成内核配置,最终只要.config中内容是正确的,就不影响编译过程。呵呵
(2) 第二步配置make menuconfig
这步操作一定要确保Ubuntu安装了ncurses库,因为ncurses库是linux中用来实现文字式的图形界面(执行make menuconfig后从显示的的配置信息就可以看出scripts\kconfig\lxdialog目录下的一些c文件就是用来提供menuconfig的那些程序源代码),另外还要保证中段控制台屏幕全屏,否则可能显示不出图形化界面。
menuconfig读取第一步得到的.config文件和Linux中的Kconfig文件,用.config文件中的配置选择结果来初始化menuconfig中各个菜单项的选择值最终给我们一个图形化的界面,让我们可以更加容易的找到自己想要修改的配置项,然后更改配置它。如下图所示,可以发现menuconfig中间的选择区中有很多个选择项,每个选择项对应.config文件中的一个配置项,将光标放在选择项上按Enter键可以进入子目录( —>的选项才是有子菜单的);;按键Y、N、M三个按键的作用分别是将选中模块编入、去除、模块化;双击ESC表示退出;按键?可以显示帮助信息;按下/按键可以输入搜索内容来全局搜索信息;在menuconfig中选项前面的括号里,表示编入,空白表示去除,M表示模块化( Legend: []built-in [ ] excluded module < > module capable );
注:[]表示不可以模块化,<>表示可以模块化。另外 linux内核中一个功能模块有三种编译方法:编入、去除和模块化。所谓编入就是将这个模块的代码直接编译连接到zImage中去;去除就是将这个模块不编译链接到zImage中;模块化是将这个模块仍然编译,但是不会将其链接到zImage中,会将这个模块单独链接成一个内核模块.ko文件,将来linux系统内核启动起来后可以动态的加载或卸载这个模块。
Linux|Linux内核配置和编译
文章图片

说到这里就不得不提下menuconfig和Kconfig和.config和Makefile的关系:
1)menuconfig本身的软件而并不负责提供内容菜单里的内容(目录结构,以及菜单项目的细节),它只负责提供menuconfig工作的这一套逻辑,譬如在通过上下左右箭头按键来调整光标等。
2)菜单内容是由内核源码树各个目录下的Kconfig文件来支持的,Kconfig文件中按照一定的格式包含了一个又一个的配置项,每一个配置项在make menuconfig中都会成为一个菜单项目,也就是说menuconfig中显示的菜单目录结构和源码目录中的Kconfig的目录结构是一样的(例如,我们手动改一个Kconfig中的配置项,再次make menuconfig相应的在menuconfig中也会随着改动)。即menuconfig的菜单内容来自于Kconfig文件,Kconfig文件是不变的,Kconfig文件只是决定有没有这个菜单项,并不管这个菜单项的选择结果
3)菜单项的配置选择值在.config文件里。譬如等于y的,等于m的… make menuconfig每次推出时会将我们更改过的配置重新写入.config文件中记录,下一次再次打开make menuconfig时会再次加载.config,最终去编译内核时编译连接程序会考虑.config中的配置值指导整个编译连接过程。
4)Kconfig按照一定的格式来书写,menuconfig程序可以识别这种格式,然后从中提取出有效信息组成menuconfig中的菜单项。将来在做驱动移植等工作时,有时需要自己添加Kconfig中的一个配置项来将某个设备驱动添加到内核的配置项目中,这时候就需要对Kconfig的配置项格式有所了解,否则就不会添加。 打开其中的一个Kconfig文件,例如drivers/net目录下的Kconfig文件,例如其中的一段内容:
Linux|Linux内核配置和编译
文章图片

【Linux|Linux内核配置和编译】其中:
menuconfig:
表示菜单(本身属于一个菜单中的项目,但是他又有子菜单项目),其后面的 NETDEVICES 的就是其中一个配置项的名字,这个名字前面添加CONFIG_后就构成了.config文件中的其中一个配置项CONFIG_NETDEVICES。一个menuconfig后面跟着的所有config项就是这个menuconfig的子菜单。这就是Kconfig中表示的目录关系
depends:
配置项NETDEVICES依赖于另一个配置项NET。如果NET配置项为Y或者M,则NETDEVICES配置项才有意义;如果依赖的哪个配置项NET本身被设置为N,则NETDEVICES配置项根本没有意义。所以有时候你在menuconfig中如果找不到一个选项,但是这个选项在Kconfig中却是有的,则可能的原因就是这个配置项依赖的一个配置项是不成立的。
另外 depends依赖的配置项可以是多个,而且还可以有逻辑运算。这种时候只要依赖项目运算式子的逻辑结果为真则依赖就成立。
config:
表示菜单中的一个配置项(本身并没有子菜单下的项目)。其后面IFB的就是其中一个配置项的配置项名字,这个字符串前面添加CONFIG_后就构成了.config中其中一个配置项名字CONFIG_IFB
tristate:
意思是三态,即3种状态,t就是这个配置项可以被三种选择,分别对应Y、N、M三种选择方式,也就是menuconfig页面中配置项前的<>的三种状态
bool:
bool的意思是这个配置项只能被2种选择,对应Y和N,即menuconfig页面中[]的二种状态
help:
帮助信息,告诉我们这个配置项的含义,以及如何去配置他,这里的帮助信息和在menuconfig里按问好?出现的帮助信息是的对应的。
Linux|Linux内核配置和编译
文章图片

source:
内核源码目录树中每一个Kconfig都会source引入其所有子目录下的Kconfig,从而保证了所有的Kconfig项目都被包含进menuconfig中。这个也告诉我们:如果你自己在linux内核中添加了一个文件夹,一定要在这个文件夹下创建一个Kconfig文件,然后在这个文件夹的上一层目录的Kconfig中source引入这个文件夹下的Kconfig文件。
5)从上面可以知道:配置项被配置成Y、N、M会影响.config文件中的CONFIG_XXX变量的配置值。这个.config中的配置值(=y、=m、没有)会影响最终的编译链接过程。如果=y则会被编入(built-in),如果=m会被单独连接成一个ko模块,如果没有则对应的代码不会被编译。那么这么是怎么实现的?都是通过当前目录下的makefile实现的。接着上述4)分析,打开drivers/net目录下的makefile文件,举其中一个例子:obj-$(CONFIG_IFB) += ifb.o
Linux|Linux内核配置和编译
文章图片

如果CONFIG_IFB变量值为y,则obj += ifb.o,因此 ifb.c会被编译;如果CONFIG_IFB变量未定义,则ifb.c不会被编译。如果CONFIG_IFB变量的值为m则会被连接成ko模块(这个是在linux内核的Makefile中定义的规则)
把menuconfig中的菜单项、Kconfig中的配置项、.config中的一行、 Makefile中的一行,这4个东西结合起来理解,则整个linux内核的配置体系就明了了。
三、编译
make uImage
这里需要注意的几点:
1)在内核中搜索uImage后发现,其在arch/arm/Makefile中,代码如下所示(uImage依赖于vmlinux,即真正的内核 ):
zImage Image xipImage bootpImage uImage: vmlinux
( Q ) (Q) (Q)(MAKE)( b u i l d ) = (build)= (build)=(boot) MACHINE=$(MACHINE)( b o o t ) / (boot)/ (boot)/@
因此不难猜测,该Makefile一定会被主Makefile所包含,代码如下:
include( s r c t r e e ) / a r c h / (srctree)/arch/ (srctree)/arch/(SRCARCH)/Makefile
2)vmlinux的依赖如下
Linux|Linux内核配置和编译
文章图片

对其依赖进行一步步分析:
Linux|Linux内核配置和编译
文章图片

3)生成的include/config/auto.conf文件(供子makefile来使用)和include/linux/autoconfig.h(供源代码使用)也会被包含到主makefile
4)分析makefile很关键的二点就是找到第一个启动的文件(arch/arm/kernel/head.S)和连接脚本(arch/arm/kernel/vmlinux.lds 在配置过程根据arch/arm/kernel/vmlinux.lds.S生成),当然能够分析Makefile来确定是可以的,但有的时候也可以直接在编译时输出的打印信息来确认
四、下载并启动
这里用tftp的方式下载内核。将镜像文件uImage放到,放到tftp服务器共享目录;打开开发板进入到Uboot界面(一定要将uboot的相关环境变量设置好,确保能和tftp服务器ping通);在uboot下用 tftp命令下载 如 tftp 0x30008000 uImage (0x30008000为配置时的链接地址,uImage便是下载到该地址);下载完成就可以用bootm ox30008000来启动内核了

    推荐阅读