宠文网

深度探索Linux操作系统

宠文网 > 科普学习 > 深度探索Linux操作系统

3.3 配置内核

书籍名:《深度探索Linux操作系统》    作者:王柏生
    《深度探索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"了。为了辅助验证内核的构建,在下一节我们将构建一个基本的根文件系统。