Linux 内核编译

内核编译介绍

通常只有在以下情况下才需要重新编译内核:

  • 新功能需求:需要的新功能只有在新内核中才能使用,为了获得这些功能,必须重新编译内核;
  • 内核过于臃肿:如果内核中包含很多不需要的功能,可以重新编译内核并去除这些功能;
  • 更换硬件驱动:如果当前内核编译的驱动导致系统不稳定,可以替换为稳定版的驱动;
  • 其他需求:对于特殊硬件系统,可能需要自行设计内核。

内核及内核模块的路径如下:

  • 内核:/boot/vmlinuz
  • 虚拟文件系统(RAM Disk):/boot/initramfs
  • 内核模块:/lib/modules/version/kernel
  • 内核源代码:/usr/src/linux

在 CentOS 上,可以在以下网站下载原始 SRPM:http://vault.centos.org/

Linux 官方内核源码可以在以下网站下载:https://www.kernel.org/

保持源码干净

下载内核并解压:

[root@234c8 ~]$ wget ftp://ftp.twaren.net/pub/Unix/Kernel/linux/kernel/v3.x/linux-3.10.89.tar.xz
[root@234c8 ~]$ tar -Jxvf linux-3.10.89.tar.xz -C /usr/src/kernels/

为了确保内核源代码干净,没有保留目标文件(*.o)及相关配置,可以使用make mrproper来处理:

[root@234c8 linux-3.10.89]$ make mrproper

一般只有第一次执行内核编译前采用这一命令,其他时候用make clean就可以。

挑选内核功能

/boot目录下存在config-*的内核功能列表文件,可以通过多个方法创建:

  • make menuconfig:最常用的方法,在命令行模式下进行配置。
  • make oldconfig:通过已存在的./.config文件内容设置默认值,只将新版内核内功能列出来供选择。
  • make xconfig:通过 KDE 图形界面来设置。
  • make gconfig:通过 GNOME 图形界面来设置。
  • make config:过去的设置方式。

下面以make menuconfig为例进行操作:

[root@234c8 linux-3.10.89]$ cp /boot/config-3.10.0-1160.41.1.el7.x86_64 .config
[root@234c8 linux-3.10.89]$ make menuconfig
 .config - Linux/x86 3.10.89 Kernel Configuration
 ----------------------------------------------------------------------------------------
  +--------------------- Linux/x86 3.10.89 Kernel Configuration -----------------------+
  |  Arrow keys navigate the menu.  <Enter> selects submenus -- letters are hotkeys.   |
  |  Pressing <Y> includes, <N> excludes, <M> modu.  Press <Esc><Esc> to exit, <?> for |
  |  Help, </> for Search.  Legend: [*] built-in  [ ] excluded  <M> module  < > mod    |
  |                                                                                    |
  | +--------------------------------------------------------------------------------+ |
  | |             [*] 64-bit kernel                                                  | |
  | |                 General setup  --->                                            | |
  | |             [*] Enable loadable module support  --->                           | |
  | |             -*- Enable the block layer  --->                                   | |
  | |                 Processor type and features  --->                              | |
  | |                 Power management and ACPI options  --->                        | |
  | |                 Bus options (PCI etc.)  --->                                   | |
  | |                 Executable file formats / Emulations  --->                     | |
  | |             -*- Networking support  --->                                       | |
  | |                 Device Drivers  --->                                           | |
  | |                 Firmware Drivers  --->                                         | |
  | |                 File systems  --->                                             | |
  | |                 Kernel hacking  --->                                           | |
  | +-------------↓(+)---------------------------------------------------------------+ |
  +------------------------------------------------------------------------------------+
  |               <Select>    < Exit >    < Help >    < Save >    < Load >             |
  +------------------------------------------------------------------------------------+

关于整个内核功能选择,一般来说,要选择必需的功能编译进内核,可能需要的功能编译成模块,不清楚的功能则保持默认。功能选择好后,选择Save退出。

内核编译

在编译内核之前,需要清除无用文件,然后分别编译内核和模块。使用 -j 参数指定编译时使用的线程数:

[root@234c8 linux-3.10.89]$ make clean
[root@234c8 linux-3.10.89]$ make -j 16 bzImage
  BUILD   arch/x86/boot/bzImage
Setup is 16704 bytes (padded to 16896 bytes).
System is 4674 kB
CRC e48489cb
Kernel: arch/x86/boot/bzImage is ready  (#1)
[root@234c8 linux-3.10.89]$ make -j 16 modules
  IHEX2FW firmware/whiteheat_loader.fw
  IHEX2FW firmware/keyspan_pda/keyspan_pda.fw
  IHEX2FW firmware/keyspan_pda/xircom_pgs.fw
[root@234c8 linux-3.10.89]$ make -j 16 clean bzImage modules
  LD      drivers/scsi/scsi_mod.o
  LD [M]  drivers/scsi/scsi_tgt.o
  LD      drivers/scsi/built-in.o
  LD      drivers/built-in.o

编译完成后,文件应该存放在当前目录下。

安装模块

如果模块有修改,可以在内核功能选择的 General setup 中的 Local version 中修改成新的名称。安装模块:

[root@234c8 linux-3.10.89]$ make modules_install

安装内核

在无法确认新内核是否工作情况下,保留旧内核:

[root@234c8 linux-3.10.89]$ cp arch/x86/boot/bzImage /boot/vmliunz-3.10.89m
[root@234c8 linux-3.10.89]$ cp .config /boot/config-3.10.89m
[root@234c8 linux-3.10.89]$ chmod a+x /boot/vmlinuz-3.10.89m
[root@234c8 linux-3.10.89]$ cp System.map /boot/System.map-3.10.89m
[root@234c8 linux-3.10.89]$ gzip -c Module.symvers > /boot/symvers-3.10.89m.gz
[root@234c8 linux-3.10.89]$ restorecon -Rv /boot
[root@234c8 linux-3.10.89]$ dracut -v /oot/initramfs-3.10.89m.img 3.10.89m

接着使用grub2-mkconfig来处理grub2开机菜单:

[root@234c8 linux-3.10.89]$ grub2-mkconfig -o /boot/grub2/grub.cfg

之后就可以重启测试新内核了。

内核模块依赖

在内核模块目录中通常分为以下几个子目录:

  • arch:与硬件平台有关的项目,如 CPU 的等级。
  • crypto:内核所支持的加密技术,例如 MD5、DES 等。
  • drivers:一些硬件的驱动程序,如网卡、显卡驱动等。
  • fs:内核支持的文件系统,如 VFAT、NFS 等。
  • lib:一些函数库。
  • net:与网络有关的各项协定数据,包括防火墙。
  • sound:与声音有关的模块。

内核模块之间的依赖性检查通过 /lib/modules/version/modules.dep 来记录,可以使用 depmod 命令来加入已编译好的模块。例如要加入名为 tp-413.ko 网卡驱动到内核模块:

[root@101c7 ~]$ cp tp-413.ko /lib/modules/3.10.0-1160.41.1.el7.x86_64/kernel/drivers/net/
[root@101c7 ~]$ depmod

执行 depmod 命令后,程序会跑到内核模块目录,将全部模块分析一遍,将结果写入到 modules.dep 文件中。

内核模块查询

要查询内核以及加载的模块列表,可以使用 lsmod 命令:

[root@101c7 ~]$ lsmod
Module                  Size  Used by
nf_conntrack_ftp       18478  0 
nf_conntrack          139264  1 nf_conntrack_ftp
snd_seq_midi           13565  0 
snd_seq_midi_event     14597  1 snd_seq_midi

结果会分别显示出模块名称,模块大小和模块被其他模块使用(Used by)的信息。

如果想要查询模块具体信息,可以使用 modinfo 命令。例如查询 nf_conntrack_ftp 模块:

[root@101c7 ~]$ modinfo nf_conntrack_ftp
filename:       /lib/modules/3.10.0-1160.41.1.el7.x86_64/kernel/net/netfilter/nf_conntrack_ftp.ko.xz
alias:          nfct-helper-ftp
alias:          ip_conntrack_ftp
description:    ftp connection tracking helper
author:         Rusty Russell <rusty@rustcorp.com.au>
license:        GPL
retpoline:      Y
rhelversion:    7.9
srcversion:     F21861D5AD43080B93CC4DD
depends:        nf_conntrack
intree:         Y
vermagic:       3.10.0-1160.41.1.el7.x86_64 SMP mod_unload modversions 
signer:         CentOS Linux kernel signing key
sig_key:        4B:E3:B8:E9:52:F4:81:B2:62:51:AC:E4:66:9B:A7:99:71:D1:F1:AF
sig_hashalgo:   sha256
parm:           ports:array of ushort
parm:           loose:bool

上面查询可以得到模块的文件位置、依赖的模块,还有模块的参数(parm)。

内核模块加载与删除

除了上面使用到的modprobe命令,还可以使用insmod来安装模块。例如载入fat模块:

[root@101c7 ~]$ insmod /lib/modules/3.10.0-1160.41.1.el7.x86_64/kernel/fs/fat/fat.ko.xz 
[root@101c7 ~]$ lsmod | grep fat
fat                    65950  0 

使用insmod命令必须接模块完整路径来安装,并且不会去分析模块的依赖性。

想要移除模块使用rmmod命令,例如移除fat模块:

[root@101c7 ~]$ rmmod fat
[root@101c7 ~]$ insmod /lib/modules/3.10.0-1160.41.1.el7.x86_64/kernel/fs/fat/vfat.ko.xz 
insmod: ERROR: could not insert module /lib/modules/3.10.0-1160.41.1.el7.x86_64/kernel/fs/fat/vfat.ko.xz: Unknown symbol in module

不过这样有个坏处就是需要手动处理依赖问题,比如上面fat模块被移除后,无法用insmod命令来载入vfat模块。

最稳妥的方式还是使用modprobe来处理。-f参数可以强制载入模块,-r参数用来移除模块:

[root@101c7 ~]$ modprobe vfat
[root@101c7 ~]$ lsmod | grep vfat
vfat                   17461  0 
fat                    65950  1 vfat
[root@101c7 ~]$ modprobe -r vfat

如果想让模块开机自动载入,例如要加入启动一个nf_conntrack_frp模块,可以在目录下新建一个 conf 文件:

[root@101c7 initrams]$ vi /etc/modules-load.d/ftp.conf
nf_conntrack_ftp
"/etc/modules-load.d/ftp.conf" [New] 1L, 17C written

一个模块(驱动)写一行,上面模块是针对默认 FTP 端口设置。如果要调整到 558 端口,可以在modprobe.d目录下面新建配置:

[root@101c7 initrams]$ vi /etc/modprobe.d/ftp.conf
options nf_conntrack_ftp ports=558
"/etc/modprobe.d/ftp.conf" [New] 1L, 35C written

系统重启后就能顺利载入模块了。也可以通过重启systemd-modules-load服务来即刻生效:

[root@101c7 initrams]$ systemctl restart systemd-modules-load
[root@101c7 initrams]$ lsmod | grep nf_conntrack_ftp
nf_conntrack_ftp       18478  0 
nf_conntrack          139264  1 nf_conntrack_ftp