图源:pexels
内核(Kernel)是整个操作系统的最底层,负责所有硬件的驱动,以及提供系统所需的内核功能。
所有需要让计算机完成的功能,都需要内核支持才行。
内核的本质是一个大型程序,可以在编译后加载到内存中执行,进而加载各种驱动和模块,驱动主机的相关硬件,然后对系统中的其它软件提供相应的功能。
为了节省空间,编译后的内核通常会被压缩成一个内核文件,使用的时候再被解压到内存中。一个Linux主机上可以有多个内核文件,可以在启动的时候选择其中一个用于加载系统。当然也可以通过grub2等boot loader进行多重引导。
前面说过了,内核需要编译后才能使用(执行),而编译自然需要相应的源代码,一般称为内核源码。
默认情况下我们安装的Linux发行版是不会附带内核源码的,如果要安装源码就要去Linux发行版的官网下载安装对应的内核SRMP文件。而如果想安装其它版本的内核,可以直接从网上下载相应的内核源码Tarball,解压后进行内核编译,然后通过grub2进行引导就可以使用了。
通常来说,我们不太需要对内核进行频繁编译,而且一般来说Linux发行版自带的编译好的内核就能满足我们日常的需要。只有当出现以下几种情况的时候你可能才需要编译内核:
新功能的需求
内核是频繁更新的,随着更新会逐渐加入一些新功能和特性,如果你想使用新版本内核加入的功能,或者是你有新的硬件要使用,但新硬件无法被旧版本的内核识别或加载,那你就可能需要进行内核编译。
需要精简内核
Linux发行版附带的默认内核是针对大部分人的使用情况来设置的,所以在极个别特殊情况下可能会显得很“臃肿”,比如你要安装的目标硬件是嵌入式设备,硬件水平有限,需要节省内存或者磁盘,那你可能需要重新编译内核,以去除不需要的功能来精简内核。
与硬件搭配的稳定性
Linux发行版附带的默认内核大多数是针对Intel的CPU设置的,如果你的CPU是其它型号,比如AMD或ARM,有可能会运行的不是很完美,此时你可以对内核源码的设置进行调整,专门对你的特异CPU进行优化后重新编译内核。
如要要查看当前使用的内核版本:
[icexmoon@xyz ~]$ uname -r 3.10.0-1160.el7.x86_64
CentOS 7使用3.10.x作为长期维护版本,所以升级内核的话最好使用3.10.x版本的内核,比如3.10.89。
当然你也可以不遵守上边的限制,直接使用最新的5.x.x版本内核,但是跨越大版本升级内核可能出现一些无法解决的问题。
如果要获取当前使用的Linux发行版的默认内核对应的源码,最好的方式是通过其官网获取:
CentOS官网提供源码的页面是:https://vault.centos.org/。
寻找对应的CentOS版本(我这里是7.9.2009),然后查找到updates目录,然后往下找.src.rpm
文件,最终我找到的是这个页面:https://vault.centos.org/7.9.2009/updates/Source/SPackages/:
有多个SRPM文件,其中kernel.xxx
就是内核的SRMP包,kernel-3.10.0-1160.el7.src.rpm应该就是我当前使用的内核对应的SRPM包,其它类似kernel-3.10.0-1160.42.2.el7.src.rpm应该是小版本更新后的SRPM包。
下载SRPM包后进行解包,就能获取相应的内核源码。
Linux内核一直都是由Linux创始人Linus所属的团队在负责开发维护,其发布内核的官网是:
具体提供所有Linux内核源码的地址是:https://mirrors.edge.kernel.org/pub/linux/kernel/
如果访问速度不快,可以使用其它镜像站,这里找到一个中科大的Linux用户协会网站:
该网站提供Linux相关资源的镜像站,内核的下载网址是:
除了内核,还有其它很多Linux资源镜像。
之前在在Linux 之旅 21:编译安装软件中介绍过怎么使用.patch
文件来升级软件。如果你已经有某个版本的内核源码了,也可以使用这种方式进行升级,不过需要注意的是需要按照版本顺序依次使用.path
文件升级,比如你当前内核版本是5.8.1
,使用patch-5.8.2
可以升级到5.8.2
内核,再使用patch-5.8.3
就可以升级到5.8.3
版本的内核,以此类推。
先下载需要的源码,这里我选择linux-3.10.99
版本的源码:
[icexmoon@xyz tmp]$ wget https://mirrors.edge.kernel.org/pub/linux/kernel/v3.x/linux-3.10.99.tar.xz --2021-09-17 16:33:01-- https://mirrors.edge.kernel.org/pub/linux/kernel/v3.x/linux-3.10.99.tar.xz 正在解析主机 mirrors.edge.kernel.org (mirrors.edge.kernel.org)... 2604:1380:3000:1500::1, 147.75.95.133 正在连接 mirrors.edge.kernel.org (mirrors.edge.kernel.org)|2604:1380:3000:1500::1|:443... 失败:连接超时。 正在连接 mirrors.edge.kernel.org (mirrors.edge.kernel.org)|147.75.95.133|:443... 已连接。 已发出 HTTP 请求,正在等待回应... 200 OK 长度:73312148 (70M) [application/x-xz] 正在保存至: “linux-3.10.99.tar.xz” 100%[===========================================================================================>] 73,312,148 153KB/s 用时 6m 51s 2021-09-17 16:42:00 (174 KB/s) - 已保存 “linux-3.10.99.tar.xz” [73312148/73312148])
- 速度比较慢,原因是
wget
会先尝试通过IPV6进行连接,如果服务器不支持就一直要等待,建议使用wget -4
指定通过IPV4进行连接,建立连接的所读会快得多。- 官方镜像下载速度会很慢,建议使用上边提供的国内镜像,能慢速下载。
一般来说,内核源码应该安装到/usr/src/kernels/
目录:
[root@xyz tmp]# tar -Jxvf linux-3.10.99.tar.xz -C /usr/src/kernels/ ...省略 linux-3.10.99/virt/kvm/iodev.h linux-3.10.99/virt/kvm/iommu.c linux-3.10.99/virt/kvm/irq_comm.c linux-3.10.99/virt/kvm/irqchip.c linux-3.10.99/virt/kvm/kvm_main.c [root@xyz tmp]# du -sh /usr/src/kernels/linux-3.10.99/ 566M /usr/src/kernels/linux-3.10.99/
居然解压出来要566M,恐怖如斯。
解压出的内核源码包含以下目录:
[root@xyz tmp]# ll /usr/src/kernels/linux-3.10.99/ | grep '^d' drwxrwxr-x. 32 root root 4096 3月 4 2016 arch drwxrwxr-x. 3 root root 4096 3月 4 2016 block drwxrwxr-x. 4 root root 4096 3月 4 2016 crypto drwxrwxr-x. 100 root root 8192 3月 4 2016 Documentation drwxrwxr-x. 112 root root 4096 3月 4 2016 drivers drwxrwxr-x. 36 root root 4096 3月 4 2016 firmware drwxrwxr-x. 73 root root 4096 3月 4 2016 fs drwxrwxr-x. 26 root root 4096 3月 4 2016 include drwxrwxr-x. 2 root root 254 3月 4 2016 init drwxrwxr-x. 2 root root 256 3月 4 2016 ipc drwxrwxr-x. 11 root root 4096 3月 4 2016 kernel drwxrwxr-x. 9 root root 8192 3月 4 2016 lib drwxrwxr-x. 2 root root 4096 3月 4 2016 mm drwxrwxr-x. 55 root root 4096 3月 4 2016 net drwxrwxr-x. 12 root root 186 3月 4 2016 samples drwxrwxr-x. 13 root root 4096 3月 4 2016 scripts drwxrwxr-x. 9 root root 268 3月 4 2016 security drwxrwxr-x. 22 root root 4096 3月 4 2016 sound drwxrwxr-x. 17 root root 215 3月 4 2016 tools drwxrwxr-x. 2 root root 102 3月 4 2016 usr drwxrwxr-x. 3 root root 17 3月 4 2016 virt
这些目录的用途为:
摘抄自鸟哥的Linux私房菜
在编译之前应当清理源码目录,将可能存在的之前编译产生配置文件和目标文件(object file)清除:
[root@xyz tmp]# cd /usr/src/kernels/linux-3.10.99/ [root@xyz linux-3.10.99]# make mrproper
需要注意的是,make mrproper
命令会将内核功能选择文件一并删除,如果仅需要清理之前编译残留的文件,可以:
[root@xyz linux-3.10.99]# make clean
make clean
仅会删除编译产生的中间文件,不会删除相关配置文件。
在开始编译内核前,需要先选择内核功能,即为内核设定好哪些功能直接编译进内核,哪些功能是编译成模块外挂,哪些功能是完全不需要的。
具体可以通过以下几种方式进行设置:
make menuconfig
命令行下以纯文本的方式进行设置。
make oldconfig
使用已存在的./.config
文件作为默认设置,在此基础上仅将新功能列出让用户选择。
make xconfig
通过Qt
图形接口实现的界面进行设置,比如支持Qt
图形接口的KDE下就可以使用此功能进行设置。
make gconfig
通过Gtk
图形接口实现的界面进行设置,比如支持Gtk
图形接口的GNOME就可以使用此功能进行设置。
make config
最原始的设置方式,选择错误后将无法修改。
这里以默认内核的配置文件作为参考来设置新内核的功能:
[root@xyz linux-3.10.99]# ll /boot/config-3.10.0-1160.el7.x86_64 -rw-r--r--. 1 root root 153591 10月 20 2020 /boot/config-3.10.0-1160.el7.x86_64 [root@xyz linux-3.10.99]# cp /boot/config-3.10.0-1160.el7.x86_64 .config [root@xyz linux-3.10.99]# ll .config -rw-r--r--. 1 root root 153591 9月 17 17:13 .config
下面进行内核选择:
[root@xyz linux-3.10.99]# make menuconfig
会出现这样的界面:
有点像是BIOS的设置界面。
操作方式很简单,上边是具体的设置项,下边是菜单,选中不同的菜单项有不同的功能:
Select
:当选中该菜单项时:
Enter
会进入当前设置项(有子设置项的菜单尾部会有-->
符号)Space
(空格)会修改当前配置项(头部有[]
或<>
标识的才能修改)Exit
:选中时按下Enter
会返回上一级菜单,如果已经是顶部菜单,将退出设置程序。Help
:选中时按下Enter
会显示选中设置项的帮助信息。Save
:选中时按下Enter
会保留当前的设置到配置文件。Load
:从配置文件加载内核设置。内核功能项的设置值主要分以下几种:
-*-
:默认编译到内核,无法修改。[*]
:编译到内核。[M]
:编译到内核模块。[ ]
:不使用该功能。这部分设置是与Linux最相关的程序交互、内核版本说明、是否使用开发中的程序代码等相关的。
这里仅做了上面的修改,其余均保持默认值。
其中Local version
和Automatically append version...
的用途是可以在Linux内核版本后添加上一段自定义字符,比如上边设置后最终编译出的内核版本显示的时候就会显示3.10.99icexmoon
之类的字样。
Kernel compression mode
是内核编译后的压缩模式,使用Bzip2
可以让内核拥有较高的压缩比。
Kernel .config support
可以让内核的配置文件直接写入内核文件。
其余相关配置的说明可以阅读鸟哥的Linux私房菜。
这里是内核加载模块的相关设置,如果内核要能加载模块,该功能必须写入内核。
这里仅增加了一个Forced module unloading
功能,该功能可以让内核强制卸载模块。
其下的自配置项Patition Types
涉及分区的相关功能:
这里均保持默认值。
子配置项I/O scheduler
可以设置I/O调度算法:
这里建议设置为Deadline
。
这一部分设置包括CPU的类型与功能选择。
子选项Linux guest support
提供Linux虚拟化相关功能:
这里保持默认选项。
选项Preemption Model
可以调整内核抢占模式:
具体分为三种:
如果Linux主机的用途是服务器,可以修改选项为No Forced Preemption (Server)
。
内核抢占模式的更多内容可以阅读Linux Preemption模式。
选项Timer frequency
可以调整CPU的时钟频率,时钟频率会影响CPU的调度和执行,如果Linux主机用于服务器,可以适当调低时钟频率,比如300 HZ。这里使用默认值1000HZ:
这里是电源管理的相关功能。如果硬件支持,可以通过这里的设置降低系统的能耗。
子选项CPU Frequency scaling
中可以设置CPU频率脉冲相关的功能:
其中Default CPUFreq governor
可以设置CPU频率调节模式,这里设置为ondemand
。
更多的CPU频率调节模式可以阅读cpufreq 五种模式。
这里是总线相关的功能。
这里保留默认值,其中PCI Stub driver
选项为虚拟化需要的功能。
这里是文件格式相关的功能。
虽然现在都是64位系统环境,但可以打开IA32 a.out support
和x32 ABI for 64-bit mode
,以兼容32位程序的运行。
这里是网络相关的选项,包括防火墙等。
子选项Networking options
中包含防火墙的功能:
在这里打开Transparent proxying support
功能。
其它选项均保持默认值,如果想了解更多相关功能说明,请阅读鸟哥的Linux私房菜。
这里是驱动相关的设置。这里不做修改,直接使用默认值。
这里是文件系统相关的设置。
CentOS 7默认是不支持ext2
和ext3
的,这里开启。
增加一个xfs realtime subvolume support
,该功能可以支持“事实子卷”,"实时子卷"是专门存储文件数据的卷,可以允许将日志与数据分开在不同的磁盘上。
子选项DOS/FAT/NT Filesystems
可以添加对Windows
相关文件系统的支持。
这里将default codepage for FAT
修改为936,将Default iocharset
修改为utf8
,以支持简体中文。
此外,添加NTFS write support
以支持NTFS
文件系统的写入功能。
更多关于
codepage
的相关内容请阅读内核中VFAT文件系统codepage和iocharset设置。
子选项Network File Systems
中包含网络文件系统的相关设置:
这里添加上对SMB2网络文件系统的支持。
子选项Native language support
可以设置语言支持:
简体中文以模块的方式支持。
这里是Linux核心开发者会用到的相关功能,如果你不是内核开发人员,应该是用不到的。
这里是安全相关的选项,包括SELinux的相关设置:
这里包含虚拟化相关的设置。
这里添加一个虚拟化相关的功能KVM legacy PCI device assignment support
。
到这里所有设置就都选择好了,选择下方的save
进行保存:
确认保存到.config
,点ok
。
保存好后一路选择exit
退出程序即可。
可以通过make help
命令查看内核编译的相关命令:
[root@xyz linux-3.10.99]# make help Cleaning targets: clean - Remove most generated files but keep the config and enough build support to build external modules mrproper - Remove all generated files + config + various backup files distclean - mrproper + remove editor backup and patch files Configuration targets: config - Update current config utilising a line-oriented program nconfig - Update current config utilising a ncurses menu based program menuconfig - Update current config utilising a menu based program xconfig - Update current config utilising a QT based front-end gconfig - Update current config utilising a GTK based front-end oldconfig - Update current config utilising a provided .config as base ...省略
主要的命令有:
make vmlinux
:将内核编译成未压缩的内核文件。make modules
:仅编译内核模块。make bzImage
:将内核编译成压缩后的内核文件。make all
:执行上边所有的三个操作。通常编译内核时的步骤是:
make clean
:清理上次编译的残留文件。make bzImage
:编译内核。make modules
:编译模块。或者可以使用组合命令:make clean bzImage modules
。
此外,为了更高效地编译内核,可以使用-j n
命令使用多线程编译内核,其中n
可以设置为CPU最大支持的线程数,比如双核四线程的CPU编译内核时可以使用make -j 4 bzImage
。
现在开始实际编译内核:
[root@xyz linux-3.10.99]# make clean [root@xyz linux-3.10.99]# make bzImage ...省略 OBJCOPY arch/x86/boot/vmlinux.bin HOSTCC arch/x86/boot/tools/build BUILD arch/x86/boot/bzImage Setup is 16720 bytes (padded to 16896 bytes). System is 4469 kB CRC d8aafb6f Kernel: arch/x86/boot/bzImage is ready (#1)
出现xxx is ready
字样就说明编译好了。
下面编译模块:
[root@xyz linux-3.10.99]# make modules
- 编译前请确保有足够的空闲空间,我因为空间不够编译失败了。
- 模块编译非常耗时,我几年前的小破笔记本上的虚拟机用单线程跑了1小时左右。
内核模块都安装在/lib/modules/$(uname -r)
目录下。也就是说以内核版本进行区分,一般来说不会有什么问题,但如果我们要安装的内核模块对应的版本已经存在了,就会产生冲突。此时解决的方式有:
General setup
中的Local version
中添加一个额外的版本标识。如果不存在版本冲突的问题就可以直接安装:
[root@xyz linux-3.10.99]# make modules_install ...省略 INSTALL /lib/firmware/edgeport/down2.fw INSTALL /lib/firmware/edgeport/down3.bin INSTALL /lib/firmware/whiteheat_loader.fw INSTALL /lib/firmware/whiteheat.fw INSTALL /lib/firmware/keyspan_pda/keyspan_pda.fw INSTALL /lib/firmware/keyspan_pda/xircom_pgs.fw DEPMOD 3.10.99icexmoon [root@xyz linux-3.10.99]# ll /lib/modules 总用量 8 drwxr-xr-x. 7 root root 4096 7月 24 14:46 3.10.0-1160.el7.x86_64 drwxr-xr-x. 3 root root 4096 9月 17 21:59 3.10.99icexmoon
安装内核实际上就是将编译好的内核文件部署到/boot
目录:
[root@xyz linux-3.10.99]# cp ./arch/x86/boot/bzImage /boot/vmlinuz-3.10.99icexmoon [root@xyz linux-3.10.99]# cp .config /boot/config-3.10.99icexmoon [root@xyz linux-3.10.99]# chmod a+x /boot/vmlinuz-3.10.99icexmoon [root@xyz linux-3.10.99]# cp ./System.map /boot/System.map-3.10.99icexmoon [root@xyz linux-3.10.99]# gzip -c Module.symvers > /boot/symvers-3.10.99icexmoon.gz [root@xyz linux-3.10.99]# restorecon -Rv /boot restorecon reset /boot/System.map-3.10.99icexmoon context unconfined_u:object_r:boot_t:s0->unconfined_u:object_r:system_map_t:s0
这里除了内核文件,还拷贝了其它关键文件到/boot
目录。
我们之前说过,内核在加载到内存中后,要依靠虚拟文件系统才能读取到模块,所以还要创建新内核的虚拟文件系统:
[root@xyz linux-3.10.99]# dracut -v /boot/initramfs-3.10.99icexmoon.img 3.10.99icexmoon Executing: /sbin/dracut -v /boot/initramfs-3.10.99icexmoon.img 3.10.99icexmoon dracut module 'busybox' will not be installed, because command 'busybox' could not be found! dracut module 'dmsquash-live-ntfs' will not be installed, because command 'ntfs-3g' could not be found! dracut module 'cifs' will not be installed, because command 'mount.cifs' could not be found! ...省略 *** Creating image file *** *** Creating image file done *** *** Creating initramfs image file '/boot/initramfs-3.10.99icexmoon.img' done ***
要让新内核能使用还需要让其出现在boot loader的引导菜单上才行,所以这里需要重建grub2
的配置文件,以让grub2
自动识别到新内核:
[root@xyz linux-3.10.99]# grub2-mkconfig -o /boot/grub2/grub.cfg Generating grub configuration file ... Found linux image: /boot/vmlinuz-3.10.99icexmoon Found initrd image: /boot/initramfs-3.10.99icexmoon.img Found linux image: /boot/vmlinuz-3.10.0-1160.el7.x86_64 Found initrd image: /boot/initramfs-3.10.0-1160.el7.x86_64.img Found linux image: /boot/vmlinuz-0-rescue-f5a85e92d51e4d40975fd956fd775f9c Found initrd image: /boot/initramfs-0-rescue-f5a85e92d51e4d40975fd956fd775f9c.img done
可以看到识别到了我们的新内核。
现在新内核已经就绪,重启来看看:
第一个选项就是新内核,选择新内核进入系统。
[icexmoon@xyz ~]$ uname -r 4.1.16
内核更新成功。
- 花了两天时间总算成功安装了一个官方内核,不过兼容性依然有点问题,GNOME窗口管理器就很卡。安装过程中我同样参考了CentOS7编译内核 详细步骤。
- 遇到的其它坑包括:
- 安装最新的内核
5.9.9
时,各种编译工具均版本过老,无法编译,如果强行要编译可能还需要找其它的软件源更新编译工具,我这里没有继续折腾。- 安装内核
3.10.99
后,无法正常进入系统,显示不能加载/dev/dm-0
(centos-root
),猜测应该是因为/root
所在分区使用的是LVM
,且因为空间不够我进行过扩若,导致出现了xfs
的一个BUG,该BUG影响3.10
~3.12
的所有内核版本,详情请见xfs.org。- 安装内核
3.13.1
和3.19.8
后,无法正常进入系统,VMware虚拟机直接报错:操作系统拒绝CPU。网上有说法是内存太小,但我遇到的问题并不完全一致,是完全没有内核的加载过程输出,修改虚拟机内存大小后问题也依然存在。
以上就是Linux内核编译的全部内容了,谢谢阅读。
到这里,《鸟哥的Linux私房菜》这本书就全部学习完毕了,可能还有一两篇文章作为番外以及该书的书评。