3.3 配置内核
《深度探索Linux操作系统》章节:3.3 配置内核,宠文网网友提供全文无弹窗免费在线阅读。!
内核提供了make menuconfig、make xconfig、make gconfig等具有图形界面的配置方式。make menuconfig是图形界面配置方式中最简陋的一种,但是却非常方便易用,依赖也最小。其他如make xconfig、make gconfig需要QT、GTK+等库的支持。在本书中,我们使用make menuconfig配置内核,其简单地基于终端的图形界面是使用ncurses编写的,因此需要安装libncurses5-dev,安装方法如下:
3.3.1 交叉编译内核设置
在默认情况下,内核构建系统默认内核是本地编译,即编译的内核是运行在与宿主系统相同的体系架构上。如果是为其他的架构编译内核,即交叉编译,我们需要设置两个变量:ARCH和CROSS_COMPILE。其中:
◆ ARCH指明目标体系架构,即编译好的内核运行在什么平台上,如x86、arm或mips等。
◆ CROSS_COMPILE指定使用的交叉编译器的前缀。对于我们的交叉工具链来说,其前缀是i686-none-linux-gnu-。
在顶层的Makefile中,我们可以看到工具链中的编译器、链接器等均以$(CROSS_COMPILE)作为前缀:
可以使用多种方式定义这两个变量,比如通过在环境变量中定义ARCH、CROSS_COMPILE;或者每次执行make时,通过命名行为这两个变量的赋值,如:
也可以直接更改顶层Makefile。这种方法比较方便,但是要小心,以免破坏Makefile文件。本书中我们采用这种方式,将顶层Makefile中的如下脚本:
更改为:
3.3.2 基本内核配置
编译内核的第一步是配置内核,但是在我们使用的这一版的内核中,有成千上万的配置项,并且很多配置项彼此之间存在着非常紧密的依赖关系,如果从零开始一项一项地配置,显然不是一个好办法。
幸运的是,在很多情况下,我们都会有一个目标系统的老版本内核配置文件,而不必每次都从零开始。在此种情况下,首先将已有的内核配置文件复制到顶层目录下,并命名为.config;然后运行make oldconfig,其将会询问用户如何处理变动的内核配置;最后用户可以使用make menuconfig进行微调。虽然内核提供make oldconfig的方法,但是这些方法并不是完美的,读者需要小心处理新内核中新增或改变的配置项。
但是也有很多情况,已有配置并不理想,我们需要进行更彻底定制,或者我们根本找不到一个合适的已有配置。难道我们就别无选择,只能从零开始了吗?当然不是,内核构建系统已经为开发者考虑了这些。
一方面内核为很多平台附带了默认配置文件,保存在arch/<arch>/configs目录下,其中<arch>对应具体的架构,如x86、arm或者mips等。比如,对于x86架构,内核分别提供了32位和64位的配置文件,即i386_defconfig和x86_64_defconfig;对于arm架构,内核提供了如NVIDA的Tegra平台的默认配置tegra_defconfig,Samsung的S5PV210平台的默认配置s5pv210_defconfig等。
如果我们打算使用x86的32位的默认配置,执行下面命令即可:
如果想使用Samsung的S5PV210平台的默认配置,则使用如下命令:
如果对这些内核内置的默认配置依然不满意,kbuild还提供了创建一个最小配置的方法,从某种意义上讲,这是最彻底的定制方式了,命令如下:
执行该命令后,内核除了选中必选项外,其余全部不选。我们举个例子来展示这个配置方式,例如某Kconfig文件中有如下配置:
如果我们在IA32上执行"make allnoconfig",则内核构建系统基本按照如下规则处理上述各配置项。
◆ config A:无条件选中。
◆ config B:不会被选中,因为平台不是X86_64架构。
◆ config C:无条件选中。另外,因为该选项明确要求选中D,所以选项D也会被选中。
◆ config E:不会被选中。
◆ config F:不会被选中。虽然该选项指出默认值"default y",但是注意"default y"和"def_bool y"是有本质区别的,"def_bool y"是无条件选中,"default y"只是建议。
执行make allnoconfig后,生成的配置文件.config如下:
在本书中,我们基于make allnoconfig的结果开始配置内核,命令如下:
接下来各节中,我们以这个基本配置为基础,按照需要进行具体的配置。希望读者可以通过这个过程的学习,能够做到举一反三,在具体的项目中进行最优的配置。
3.3.3 配置处理器
1.选择处理器型号
对于x86架构来说,其具有向后兼容性,较新的处理器都支持较早的处理器的指令。因此,为较早的处理器开发的程序都可以在较新的处理器上运行,但是反过来则不一定了,因为较早的处理器当然不会支持较新的处理器中的一些指令。
因此,选择支持越早的处理器,则内核就可以在更多的机器上运行。对于很多Linux发行版,通常就是依照这个原则。比如Ubuntu12.10的内核配置支持的处理器型号为Pentium Pro(686),这样理论上可以确保Ubuntu12.10可以运行在Pentium Pro以后的所有系列机器上。
如此选择虽然带来了兼容性的好处,但是付出的代价可能就是丧失了速度。比如,为Pentium Pro编译的内核,只针对Pentium Pro进行了优化,显然不能使用最新处理器中的更高级的指令。
对于其他架构亦如此,甚至有过之而无不及,比如都是ARM处理器,但是如果目标平台是Freescale iMX系列,处理器型号显然不能选择Samsung的S3C系列。
在Linux操作系统中,可以使用如下命令查看处理器的具体型号:
下面是配置内核支持的处理器型号的步骤。
1)执行make menuconfig,出现如图3-4所示界面。
图 3-4 配置处理器型号(1)
2)在图3-4中,选择菜单项"Processor type and features",出现如图3-5所示界面。
图 3-5 配置处理器型号(2)
3)在图3-5中,选择菜单项"Processor family",出现如图3-6所示的界面。
图 3-6 配置处理器型号(3)
4)以笔者的机器为例,根据前面察看的CPU信息,显然选择图3-6中的"Core 2/newerXeon"是最适合的。如果在列表中没有与实际CPU型号完全吻合的,可选择与它最接近的一项。
2.配置内核支持SMP
如果机器有多颗CPU(包括多核),为了更好地发挥多颗CPU的性能,需要配置内核支持SMP。下面是配置内核支持SMP的步骤。
1)执行make menuconfig,出现如图3-7所示的界面。
图 3-7 配置SMP(1)
2)在图3-7种,选择菜单项"Processor type and features",出现如图3-8所示的界面。
图 3-8 配置SMP(2)
3)在图3-8中,选中"Symmetric multi-processing support"。
3.3.4 配置内核支持模块
在嵌入式系统中,由于外围设备相对比较固定,因此,在编译内核时,基本可以确定内核需要支持哪些特性,例如支持哪些硬件、支持哪些文件系统等。而对于用在PC系统上的内核,因为个人计算机中包含的硬件千差万别,为了提供更好的兼容性,各家Linux发行版的内核都尽可能地包含更多的功能,支持更多的硬件。但是,如果所有的功能模块和驱动全部编译进内核映像,势必造成内核极其庞大。以作者使用的Ubuntu12.10发行版为例,其内核映像大小为5MB,而该发行版中包含的内核模块的尺寸约为100MB左右。也就是说,如果把全部的模块都编译进内核映像,内核映像的尺寸大约要增加100MB,而其中绝大部分模块在特定的一台机器上是根本不会用到的。
除了尺寸上的考虑外,更大的灵活性也是一方面。比如,开发人员在开发某个驱动时,如果使用模块机制,只需单独编译驱动,然后动态加载,即可进行调试;而不必重新编译整个内核,甚至重启系统。
因此,在我们编译的内核中,启用内核的动态加载模块特性。下面是配置内核支持模块机制的步骤。
1)执行make menuconfig,出现如图3-9所示的界面。
图 3-9 配置内核支持模块(1)
2)在图3-9中,选中菜单项"Enable loadable module support",允许内核动态加载模块,出现如图3-10所示的界面。
图 3-10 配置内核支持模块(2)
3)在图3-10中,选中"Module unloading",允许内核动态卸载模块。
3.3.5 配置硬盘控制器驱动
一般而言,PC的根文件系统都保存在硬盘上,因此,我们需要配置内核的硬盘驱动。在笔者写作这本书时,大多数现代PC都使用SATA接口的硬盘,SATA硬盘基本已经全面取代了IDE硬盘。因此,我们以SATA硬盘为例,讨论内核中硬盘驱动的配置。关于SATA控制器驱动的配置,需要从三个方面考虑。
(1)硬盘控制器的接口
SATA控制器使用的是PCI接口,挂在PCI总线上,所以首先需要配置内核支持PCI总线。
可以使用lspci命令查看SATA硬盘的相关信息。下面以笔者机器为例,执行lspci命令输出的关于SATA控制器的相关信息:
显然,在0号PCI总线上,有一个SATA控制器,工作模式为AHCI,使用的内核驱动是ahci。
(2)与SCSI层之间的关系
在内核中,SATA设备被实现为一个SCSI设备,如图3-11所示。
图 3-11 SATA子系统结构
因此,虽然目标机器上可能没有SCSI设备,但是如果要支持SATA控制器,内核也要配置支持SCSI。
(3)底层设备驱动
在图3-11中,内核将SATA驱动从逻辑上划分为两层:
◆ SATA Translation,这一层负责SCSI和SATA协议之间的翻译,在SATA驱动中被封装为libata模块。
◆ Low Level Device Driver,在SATA Translation层下,是直接面对设备的底层驱动。很多SATA控制器均提供两种可选模式:一种是模拟传统的IDE,通常称为Compatibility模式,这种模式是为了向后兼容那些较早的不支持SATA的操作系统;另外一种是AHCI模式,这种模式可以提供更好的性能及传输速度。对于Intel的SATA控制器来说,内核为这两种不同的模式分别实现了驱动:ata_piix和ahci。ata_piix驱动用于Compatibility模式,ahci驱动用于AHCI模式。
从kbuild中有关SATA的Kconfig中,我们也可以清楚地看到SATA控制器对PCI和SCSI的依赖:
先看配置项ATA,正如配置中的描述"Serial ATA and Parallel ATA drivers",这一项对应于SATA和PATA设备。注意其中用黑体标识的部分,这一配置项是依赖SCSI的而且ATA是选择(select)依赖SCSI。也就是说,一旦配置内核支持ATA设备,kbuild将自动选中内核的SCSI支持。
/>再来看配置项SATA_AHCI和ATA_PIIX,这两项对应的就是前面所说的SATA控制器的驱动,SATA_AHCI用于驱动AHCI模式,ATA_PIIX用于驱动Compatibility模式。根据上面我们用黑体标示的部分,可以清楚地看到,这两项均要求内核支持PCI总线。
下面我们就具体配置这三个部分。
1.配置PCI总线
因为SATA控制器使用的是PCI接口,所以我们首先来配置内核支持PCI总线。
1)执行make menuconfig,出现如图3-12所示的界面。
图 3-12 配置PCI总线(1)
2)在图3-12中,选择菜单项"Bus options",出现如图3-13所示的界面。
图 3-13 配置PCI总线(2)
3)在图3-13中,选中菜单项"PCI support"。PCI总线配置完毕。
2.配置SCSI
接下来配置SCSI。
1)执行make menuconfig,出现如图3-14所示的界面。
图 3-14 配置SCSI(1)
2)在图3-14中,选择菜单项"Device Drivers",出现如图3-15所示的界面。
图 3-15 配置SCSI(2)
3)在图3-15中,选择菜单项"SCSI device support",出现如图3-16所示的界面。
图 3-16 配置SCSI(3)
4)在图3-16中,选中"SCSI device support"和"SCSI disk support",注意将它们都编译进内核,而不是编译为模块。SCSI配置完毕。
3.配置SATA控制器驱动
下面来配置SATA控制器驱动。
1)执行make menuconfig,出现如图3-17所示的界面。
图 3-17 配置SATA控制器驱动(1)
2)在图3-17中,选择菜单项"Device Drivers",出现如图3-18所示的界面。
图 3-18 配置SATA控制器驱动(2)
3)在图3-18中,选择"Serial ATA and Parallel ATA drivers"(注意将它编译进内核,而不是编译为模块),出现如图3-19所示的界面。
图 3-19 配置SATA控制器驱动(3)
4)笔者的机器使用的是Intel SATA控制器,所以选择图3-19中的"AHCI SATA support"和"Intel ESB,ICH,PIIX3,PIIX4 PATA/SATA support"。前者是工作在AHCI模式的Intel SATA控制器的驱动,后者是工作在Compatibility模式的Intel SATA控制器的驱动。注意将它们也都编译进内核。
至此,SATA控制器的驱动配置完成。接下来我们编译内核,并将编译好的内核保存在目标系统的根文件系统的boot目录下。
下面测试新编译的内核。首先在虚拟机的sda2分区上创建boot目录,用来存放内核映像:
将新编译的内核复制到虚拟机:
并在虚拟机GRUB的配置文件中添加如下启动项:
注意将虚拟机的GRUB的配置文件grub.cfg中的timeout都设置为一个正值,比如5s,这样GRUB才会给我们机会选择引导哪个系统。
然后重新启动并进入vita系统,运行结果如图3-20所示。
图 3-20 配置SATA控制器驱动后内核运行情况
根据内核的输出信息可见,内核已经正确识别了SATA硬盘。但是因为没有找到合适的文件系统挂载sda2分区,所以在“抱怨”"No filesystem could mount root"后出现了"panic"。因此,在下一小节,我们配置内核对文件系统的支持。
3.3.6 配置文件系统
内核支持多种文件系统,但是由于我们仅使用Ext4文件系统,所以这里仅配置内核包含Ext4文件系统驱动模块。Ext4驱动是向后兼容的,也就是说它也可以驱动Ext3和Ext2文件系统。下面是配置文件系统的步骤。
1)执行make menuconfig,出现如图3-21所示的界面。
图 3-21 配置文件系统(1)
2)在图3-21中,选择菜单项"File Systems",出现如图3-22所示的界面。
图 3-22 配置文件系统(2)
3)在图3-22中,选中配置项"The Extended 4(ext4)filesystem",并将其直接编译进内核。
在格式化Ext4文件系统时,工具mke2fs.ext4会默认支持"huge_file"特性,而该特性要求内核支持大于2TB的块设备或文件,因此,我们配置内核支持这一特性。
1)执行make menuconfig,出现如图3-23所示的界面。
图 3-23 配置支持大于2TB的块设备和文件(1)
2)在图3-23中,选择菜单项"Enable the block layer",出现如图3-24所示的界面。
图 3-24 配置支持大于2TB的块设备和文件(2)
3)在图3-24中,选中配置项"Support for large(2TB+)block devices and files"。
3.3.7 配置内核支持ELF文件格式
在上一节,我们配置了内核支持Ext4文件系统。但是内核从文件系统加载文件,仅支持文件系统还是不够的,内核还要支持具体的文件格式,当前Linux系统使用的标准二进制格式是ELF,因此需要配置Linux支持ELF文件格式。以下是配置内核支持ELF文件格式的步骤。
1)执行make menuconfig,出现如图3-25所示的界面。
图 3-25 配置内核支持ELF文件格式(1)
2)在图3-25中,选择菜单项"Executable file formats/Emulations",出现如图3-26所示的界面。
图 3-26 配置内核支持ELF文件格式(2)
3)在图3-26中,选中配置项"Kernel support for ELF binaries"。ELF格式支持配置完毕。
在配置内核支持ELF文件格式后,我们重新编译内核并使用新编译的内核引导vita系统后,系统的输出如图3-27所示。
图 3-27 配置文件系统和文件格式后内核运行情况
根据内核输出的信息可见,内核已经识别出硬盘的分区,也识别出了sda2分区使用的文件系统,并使用Ext4文件系统驱动成功挂载了该分区。但是内核在“报怨”"No init found…"后,依然出现了"panic"。那么,这里的"init"指的是什么呢?我们看一下内核进入用户空间的过程:
函数kernel_init首先尝试执行initramfs中的字符串ramdisk_execute_command代表的命令。目前没有使用initramfs,所以这个字符串为空,于是其继续尝试到根文件系统中寻找程序,首先其将尝试寻找字符串execute_command代表的命令。
根据下面代码:
字符串execute_command代表用户通过内核命令行参数"init"明确指定的程序,形如:
在上面grub的配置文件中,使用黑体标识的部分就是明确告诉内核,第一个进程直接运行根文件系统中目录/bin下的bash。如果用户没有通过命令行参数"init"指定第一个进程执行的程序,这个进程将依次尝试执行/sbin、/etc、/bin下的init,最后尝试执行/bin/sh。
由于目前根文件系统中除了内核的映像外没有任何文件,因此,内核找不到任何程序,所以在报告"No init found…"后出现"panic"了。为了辅助验证内核的构建,在下一节我们将构建一个基本的根文件系统。