在CentOS7环境下遇到由于某种原因需要升级内核的场景,经过一番查找,在网上找到了一个可以直接使用的RPM,大大减轻了工作量,但我开始对如何编译产生了兴趣内核。 那么让我们来实验一下。
一些内核知识
Linux内核代号分为Prepatch、Mainline、Stable、LongTerm(LongTermSupport)等,其中:
前面提到,由于一定的激励,需要升级内核来解决问题。 经过仔细的代码比对,最终选择了5.15内核版本进行版本升级。 内核升级完成后,问题解决。
内核编译计划
编译环境
注意:编译过程中的输出非常大。 确保编译目录所在的c盘有足够的可用空间。 刚开始我的测试过程中,c盘只有25G可用空间,导致由于空间不足导致编译失败。 之前的扩容是20G。 只需要45G就可以保证编译顺利。
海湾合作委员会打算
CentOS7系统自带的GCC版本为4.8.5,不再满足5.15内核的编译条件。 这里我们使用SCL软件设置来使用更高版本的GCC:
$ sudo yum install -y centos-release-scl scl-utils-build
$ sudo yum install -y devtoolset-9-toolchain
安装后,可以在当前会话中激活它:
$ scl enable devtoolset-9 bash
$ gcc --version
gcc (GCC) 9.3.1 20200408 (Red Hat 9.3.1-2)
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
可以看到GCC版本已更改为GCC9.3.1,但请注意这些方法仅在当前会话中生效,当用户切换或打开新会话时需要重新激活。
RPM包管理工具
编译之前需要安装RPM包管理工具rpmdevtools:
sudo yum -y install rpmdevtools
rpmdev-setuptree命令可以在当前用户下手动创建rpmbuild目录,用于编译rpm包。 其结构如下:
[develop@localhost rpmbuild]$ tree
.
├── BUILD
├── RPMS
├── SOURCES
├── SPECS
└── SRPMS
5 directories, 0 files
编译方法一
1、安装依赖包:
$ sudo yum install -y gcc make ncurses-devel flex bison openssl-devel elfutils-libelf-devel perl bc
2.下载代码包:
$ wget https://www.kernel.org/pub/linux/kernel/v5.x/linux-5.15.126.tar.xz
3.解压代码:
$ tar -xvf linux-5.15.126.tar.xz
4、将当前内核编译配置复制到新内核源码中,主要是设置内核编译选项。 复制当前用于启动系统的配置是最安全的形式。
$ cd linux-5.15.126
$ cp /boot/config-$(uname -r) ./.config
如果需要以文本图形的形式配置内核并调整内核加载模块,可以执行以下操作:
General setup —>Select Local version – append to kernel release
5.减小RPM包尺寸:
1)CONFIG_DEBUG_INFO:如果我们的内核中启用了CONFIG_DEBUG_INFO选项,那么我们编译出来的二进制补码代码就会带来很多调试信息。 内核镜像镜像全部被修剪和优化,而驱动则没有,所以单个KO的大小非常大。 为了减小 RPM 的大小,您需要编辑 .config 文件并注释以下行:
CONFIG_DEBUG_INFO=y
to
#CONFIG_DEBUG_INFO=y
2)INSTALL_MOD_STRIP:另一个重要的事情是编译时传递给make的参数INSTALL_MOD_STRIP。 如果设置了 INSTALL_MOD_STRIP,驱动程序将被剥离以减少输出的大小。 关于,内核kbuild文档中的描述是:
INSTALL_MOD_STRIP
If this variable is specified, it will cause modules to be stripped
after they are installed. If INSTALL_MOD_STRIP is '1', then the
default option --strip-debug will be used. Otherwise, the
INSTALL_MOD_STRIP value will be used as the option(s) to the strip
command.
这里你可能会问,什么是strip? Strip是通过删除可执行文件中ELF头的typchk段、符号表、字符串表、行号信息、调试段、注释段、重定位信息等来减少程序体积。 的目标。 在内核编译测试中,不使用INSTALL_MOD_STRIP制作的kernel-5.15.126RPM包大小近1G,设置后大小只有几十兆,非常有效。
6、这里先暂停一下,先看一下内核makefile通过makehelp提供的功能,有很多,我们看几个比较常用的:
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:
menuconfig - Update current config utilising a menu based program
Kernel packaging:
rpm-pkg - Build both source and binary RPM kernel packages
binrpm-pkg - Build only the binary kernel RPM package
deb-pkg - Build both source and binary deb kernel packages
bindeb-pkg - Build only the binary kernel deb package
在:
7、编译并执行:
$ sh -c '
yes "" | make INSTALL_MOD_STRIP=1 rpm-pkg -j 16'
make执行后会有一堆确认项,可以通过“yes”手动处理。
在我的机器上编译一次大约需要一个小时。 当看到如下内容时,说明编译成功:
Wrote: /home/develop/rpmbuild/SRPMS/kernel-5.15.126-1.src.rpm
Wrote: /home/develop/rpmbuild/RPMS/x86_64/kernel-5.15.126-1.x86_64.rpm
Wrote: /home/develop/rpmbuild/RPMS/x86_64/kernel-headers-5.15.126-1.x86_64.rpm
Wrote: /home/develop/rpmbuild/RPMS/x86_64/kernel-devel-5.15.126-1.x86_64.rpm
Executing(%clean): /bin/sh -e /var/tmp/rpm-tmp.1Vmnnr
+ umask 022
+ cd /home/develop/rpmbuild/BUILD
+ cd kernel-5.15.126
+ rm -rf /home/develop/rpmbuild/BUILDROOT/kernel-5.15.126-1.x86_64
+ exit 0
此时在rpmbuild/RPMS/x86_64目录下可以看到编译完成的三个RPM:
total 232212
-rw-rw-r-- 1 develop develop 67109724 Aug 12 01:04 kernel-5.15.126-1.x86_64.rpm
-rw-rw-r-- 1 develop develop 1464848 Aug 12 01:04 kernel-headers-5.15.126-1.x86_64.rpm
-rw-rw-r-- 1 develop develop 169205676 Aug 12 01:05 kernel-devel-5.15.126-1.x86_64.rpm
这里编译完成后的RPM可以进行升级和测试。 我们来谈谈升级后的测试。
编译方法二
ELRepo项目
常见的内核升级方案中,比较常见的方法之一是使用ELRepoProject提供的内核版本,同时ELRepoProject将内核名称更改为kernel-ml以避免与RHEL内核冲突。 其kernel-ml依然提供了最新版本内核的rpm安装包。 但新大版本发布后,将不再提供旧版本。 您可以在各种镜像站点上看到它。 目前提供kernel-ml-6.4.9和kernel-ml-6.4.8的RPM包。
需要5.15LTS版本来解决这个问题,那么如何编译5.15版本的kernel-ml包呢?
经查资料,kernel-ml分支在5.15到5.15.13之后不再更新。 幸运的是,我找到了5.15.13 SRPM包kernel-ml-5.15.13-1.el7.elrepo.nosrc.rpm。
SRPM
什么是 SRPM?
SRPM包比RPM包多了一个“S”,这是“Source”的首字母,因此SRPM可以音译为“源代码模式下的RPM包”。 也就是说,SRPM包不再是编译好的二补代码文件,而是源代码文件。 可以理解,SRPM包是软件以源代码形式发布后,直接打包成RPM包的产品。
通过 rpm2cpio 反汇编 kernel-ml-5.15.13-1.el7.elrepo.nosrc.rpm:
$ rpm2cpio kernel-ml-5.15.13-1.el7.elrepo.nosrc.rpm | cpio -idv
config-5.15.13-x86_64
cpupower.config
cpupower.service
kernel-ml-5.15.spec
744 blocks
可以看到它包含四个文件:
编制计划
1、已经获得了kernel-ml-5.15.13的文件,基于此我们可以制作5.15.126版本所需的内容:
1)将config-5.15.13-x86_64的名称更改为config-5.15.126-x86_64,可以直接使用
2)更改kernel-ml-5.15.spec中LKAver的版本号:
%define LKAver 5.15.13
to
%define LKAver 5.15.126
3)如果已经执行了编译方法1,则需要删除rpmbuild目录,然后执行rpmdev-setuptree命令生成新的rpmbuild目录。
将以下文件复制到 rpmbuild/SOURCES 目录:
config-5.15.126-x86_64
cpupower.config
cpupower.service
linux-5.15.126.tar.xz
2.安装依赖项。 ELRepo方式比编译方式需要多安装几个依赖:
$ sudo yum install asciidoc newt-devel xmlto audit-libs-devel bin
utils-devel elfutils-devel java-1.8.0-openjdk-devel libcap-devel numactl-devel perl python-devel python3 slang-devel xz-devel pciutils-devel perl-ExtUtils-Embed
3.执行编译
执行spec所在目录:
$ rpmbuild -v -bb --clean ./kernel-ml-5.15.spec
执行一次也需要一个小时。 当听到以下内容时,说明制作成功:
Wrote: /home/develop/rpmbuild/RPMS/x86_64/kernel-ml-5.15.126-1.el7.x86_64.rpm
Wrote: /home/develop/rpmbuild/RPMS/x86_64/kernel-ml-devel-5.15.126-1.el7.x86_64.rpm
Wrote: /home/develop/rpmbuild/RPMS/x86_64/kernel-ml-headers-5.15.126-1.el7.x86_64.rpm
Wrote: /home/develop/rpmbuild/RPMS/x86_64/perf-5.15.126-1.el7.x86_64.rpm
Wrote: /home/develop/rpmbuild/RPMS/x86_64/python-perf-5.15.126-1.el7.x86_64.rpm
Wrote: /home/develop/rpmbuild/RPMS/x86_64/kernel-ml-tools-5.15.126-1.el7.x86_64.rpm
Wrote: /home/develop/rpmbuild/RPMS/x86_64/kernel-ml-tools-libs-5.15.126-1.el7.x86_64.rpm
Wrote: /home/develop/rpmbuild/RPMS/x86_64/kernel-ml-tools-libs-devel-5.15.126-1.el7.x86_64.rpm
Executing(%clean): /bin/sh -e /var/tmp/rpm-tmp.vF4Ure
+ umask 022
+ cd /home/develop/rpmbuild/BUILD
+ cd kernel-ml-5.15.126
+ /usr/bin/rm -rf /home/develop/rpmbuild/BUILDROOT/kernel-ml-5.15.126-1.el7.x86_64
+ exit 0
Executing(--clean): /bin/sh -e /var/tmp/rpm-tmp.7Fu6Tu
+ umask 022
+ cd /home/develop/rpmbuild/BUILD
+ rm -rf kernel-ml-5.15.126
+ exit 0
此时源码编译内核,可以在rpmbuild/RPMS/x86_64目录下看到几个编译好的RPM:
total 78164
-rw-rw-r-- 1 develop develop 59928964 Aug 12 10:30 kernel-ml-5.15.126-1.el7.x86_64.rpm
-rw-rw-r-- 1 develop develop 14431016 Aug 12 10:30 kernel-ml-devel-5.15.126-1.el7.x86_64.rpm
-rw-rw-r-- 1 develop develop 1601944 Aug 12 10:30 kernel-ml-headers-5.15.126-1.el7.x86_64.rpm
-rw-rw-r-- 1 develop develop 2715444 Aug 12 10:30 perf-5.15.126-1.el7.x86_64.rpm
-rw-rw-r-- 1 develop develop 762024 Aug 12 10:30 python-perf-5.15.126-1.el7.x86_64.rpm
-rw-rw-r-- 1 develop develop 279844 Aug 12 10:30 kernel-ml-tools-5.15.126-1.el7.x86_64.rpm
-rw-rw-r-- 1 develop develop 161168 Aug 12 10:30 kernel-ml-tools-libs-5.15.126-1.el7.x86_64.rpm
-rw-rw-r-- 1 develop develop 138332 Aug 12 10:30 kernel-ml-tools-libs-devel-5.15.126-1.el7.x86_64.rpm
可以看到,与第一种形式相比,形成的RPM数量更多,devel-5.15.126的rpm大小也比较小。
与实际的devel-5.15.126拆包相比,内眼可见的差异是:
我也找了很久差异的原因源码编译内核,但是没有结果。 如果有大佬知道原因的话,希望能给我一些建议。 先感谢您。
两种形式都可以正常完成内核版本升级,但从占用空间和输出RPM的对比来看,更倾向于使用第二种编译方式,即基于ELRepoProject对SRPM进行修改后编译最新的kernelrpm 。
内核升级
内核升级比较简单,实际升级时只需要这两个RPM文件(针对编译方法2):
kernel-ml-5.15.126-1.el7.x86_64.rpm
kernel-ml-devel-5.15.126-1.el7.x86_64.rpm
如果是编译方法一的话就是这两种:
kernel-5.15.126-1.x86_64.rpm
kernel-devel-5.15.126-1.x86_64.rpm
其中编译好的kernel-headersrpm在很多文章中都说需要安装,而在我的实际测试中,如果带的话会遇到冲突。 elrepo 官方文档还指出,kernel-headers 不需要升级,只有在重新创建 glibc 和整个操作系统时才需要:
There is no need to install the kernel-ml-headers package. It is only necessary if you intend to rebuild glibc and, thus, the entire operating system. If there is a need to have the kernel headers installed, you should use the current distributed kernel-headers package as that is related to the current version of glibc.
1、安装内核:
yum localinstall kernel-ml-*
2、安装成功后,查看本机存在的内核列表:
awk -F' '$1=="menuentry " {print i++ " : " $2}' /boot/grub2/grub.cfg
0 : CentOS Linux (5.15.126-1.el7.x86_64) 7 (Core)
1 : CentOS Linux (3.10.0-862.el7.x86_64) 7 (Core)
2 : CentOS Linux (0-rescue-2acea53cb12b4a61b90296add99afcc1) 7 (Core)
3.设置启动最新内核(通常最新安装的内核启动顺序为0)
grub2-set-default 0
4.Reboot重启机器
参考
1.
2.@lists.elrepo.org/msg03172.html
3.
4.
5.
6.
7.%E8%B0%83%E9%A2%91
8.
9.
10.#L1481
11.
12.
13.
Momo ID:苹果尼斯
1、内核编译的基本流程,如有需要,进行修改;;;【根据发布版本生成对应的源码包】;。
步骤是必需的,[ ] 是可选步骤。
如果您不使用发行版的源包管理系统,则无需执行任何步骤
4. 如果要使用源包管理系统进行安装,可以使用每个发行版的源包构建系统。 在这些情况下,步骤 3 和 4 的操作将结合起来。
2. 准备工作 2.1 工具安装源码包名称备注
ncurses-开发
基于控制台(文本界面)设置所需
qt开发
基于窗口(图形界面)设置所需
qt3开发
基于窗口(图形界面)设置所需
gcc-c++
基于窗口(图形界面)设置所需
rpm 构建
生成rpm包需要
源码包名备注
libncurses5-dev
基于控制台(文本界面)设置所需
qt3-开发工具
基于窗口(图形界面)设置所需
克++
基于窗口(图形界面)设置所需
内核包
生成deb包时需要
假根
生成deb包时需要
dpkg-dev
生成deb包时需要
2.2 获取源码
不多说了。参考:一篇文章了解Linux系统内核升级及下载当前内核源码
这里假设源代码(linux2.6)已经下载到当前主目录~/linux-2.6/。
3. 配置
源代码树附带了帮助设置的工具。 这个工具包称为 kconfig。 设置工具有两种类型:基于控制台(文本界面)和基于窗口(图形界面)。 这里主要以控制台工具为例介绍窗口工具。
1、选择目标机的架构,默认配置
在源码树中,为每个架构准备了一个默认的.config文件,位于arch//configs/*_defconfig,执行以下命令:
$ make defconfig
2.根据需要更改内核配置
要启动基于控制台的设置工具,请在源树的根目录下执行以下命令:
$ make menuconfig
[y]键:完成左侧的选择显示。 这意味着该项目已编译并静态添加到内核中。
【m】键,显示完成。 此时,该项目将被编译为模块。 使用此功能时编译内核源码,模块会根据需要动态添加到内核中。
[n]键,将会生成,并且该函数不会被编译。
大致完成你想要的设置后,可以选择菜单顶屏的操作菜单,或者按两次【Esc】键完成设置,在“是否保存新的设置”中选择是按钮。设置?” 出现的对话框,将设置保存到 .config 文件。 至此,内核编译设置完成。
4. 编译 4.1 直线编译
当运行编译内核的目标机是本地时,只需要在源码树的根目录下执行:
$ make
编译时间与机器的性能有很大关系,少则几分钟,多则几个小时。 那么有什么办法可以节省编译时间呢?
在内核的配置中
禁用目标环境中不需要的驱动程序,仅编译必要的功能或模块。关于编译选项
-j选项用于指定make的并发度。 当机器是多处理器时,可以根据处理器的数量来指定该值,这样就可以实现快速编译。在机器性能方面
使用高性能主机进行内核编译。 4.2 交叉编译变量说明
拱
目标机框架
交叉编译
指定交叉编译器的前缀
$ make ARCH=arm CROSS_COMPILE=armv5tel-linux- uImage
$ make ARCH=arm CROSS_COMPILE=armv5tel-linux- modules
$ make ARCH=arm CROSS_COMPILE=armv5tel-linux- INSTALL_MOD_PATH=~/armroot-2.6.38 modules_install
4.3 自定义模块编译
有时,除了嵌入式模块之外,我们还需要编译可加载的模块。 它的源代码和内核源代码树不在同一目录树中,而是单独提供的。 在这些情况下,需要对驱动程序的源代码正确编译Makefile,并按照以下方法编译安装:
$ make –C /lib/modules/$(uname -r)/build M=$PWD
$ make –C /lib/modules/$(uname -r)/build M=$PWD modules_install
-C 选项:到内核源目录的符号链接。
-M 选项:告诉 Linux 内核的构建操作在源代码树之外执行构建操作。
5. 安装 5.1 本地安装
安装分三个阶段进行。
第一阶段是模块的安装,在编译后的源码树根目录下执行
命令:
$ sudo make modules_install
此时,编译好的模块安装在/lib/modules下。
第二阶段是安装内核二进制镜像文件,生成并安装引导初始化文件系统镜像文件。 同样在源码树的根目录下执行以下命令:
$ sudo make install
至此,内核镜像文件就安装在/boot下了。
下表是内核安装生成的文件和目录列表:
文件名或目录名内容
/lib/modules/<内核版本>
模块安装目录
/boot/vmlinuz-<内核版本>
内核映像文件
/boot/initramfs-<内核版本> 或 /boot/initrd.img-<内核版本>
引导初始化文件系统映像
/boot/Systemmap-<内核版本>
地址信息文件
第三阶段更改启动文件。 在某些系统配置下,可能需要自动配置 GRUB 以便能够从当前安装的内核引导。 在这些情况下,请适当编辑 /boot/grub/menu.lst 或执行 update-grub 命令。
此时请重新启动机器,确认新内核是否仍能正常运行。
注意:内核镜像的文件名和模块目录名是根据内核版本命名的。 因此,当重新编译和安装已安装版本的内核时,原来安装的内核和模块将被覆盖。 为了避免这些情况,可以在内核配置选项CONFIG_LOCALVERSION中添加字符,该字符将成为内核版本的一部分,因此可以避免被覆盖。
进行对象描述
干净的
将源树恢复到编译前状态。 obj文件被删除,.config或者编译时手动生成的一些文件不会被删除
正确先生
将源码树完全恢复到发布时的状态。发布时源码树中不存在的所有文件都会被删除,包括.config文件
帮助
显示可用的 make 对象
标签
生成标签文件。有了标签文件,就可以使用Emacs等编辑器的标签跳转功能跳转到函数定义,可以高效浏览源代码
范围
生成与 cscope 一起使用的索引文件。 cscope 是一个基于控制台(文本界面)的源浏览器
盟友配置
生成一个 .config 文件,其中启用所有设置项并静态添加到内核
全部无配置
生成.config文件,其中允许范围内的设置项被设置为无效
全部修改配置
生成一个 .config 文件,启用所有可设置为模块的项目并将其设置为模块
/.o
仅执行生成指定目标文件所需的编译。仅指定时,此目录中的所有目标文件均从 .config 文件生成
/.ko
只生成指定的模块
进行选项描述
V=0|1|2
设置编译时控制台显示的详细程度。 默认只显示摘要,设置为0。设置为1时,显示更详细的信息。 设置为2时,除了摘要之外,还会显示编译的原因(原因大多是“由于没有对象”)
奥=
将编译生成的所有文件输出到. 它在严格禁止写入源代码树的情况下非常有用。
5.2 使用包管理工具安装
要将内核镜像纳入包管理范围,需要生成相应的源码包。
$ make rpm-pkg
执行该命令后,将创建一个源码包(SRPM)和一个二进制包(RPM)。 二进制包存放在~/rpmbuild/rpms下编译内核源码,源码包存放在~/rpmbuild/SRPMS下。
如果您有 SRPM 提取的发行版内核的源代码,请使用 rpmbuild 创建源代码包。 如果内核的SRPM解压到~/rpmbuild,则执行以下命令创建源码包:
$ rpmbuild –ba ~/rpmbuild/SPECS/kernel.spec
创建的源码包存放目录与之前相同。 这些源码包与普通源码包相同,可以使用rpm命令进行安装和卸载。
$ make deb-pkg
创建的源码包存放在源码树的根目录下。 生成了几个源码包,其中包含内核镜像和模块的就是linux-image-<内核版本>.deb文件。这些源码包的操作与普通deb源码包文件的操作相同
同样可以使用dpkg来安装。
另外,Ubuntu还在kernel-package包中包含了一个辅助创建内核包的工具——makekpkg命令。 该工具可以通过命令选项来设置创建操作,您也可以根据自己的需要使用该工具。
6. 卸载
注意:卸载内核时请谨慎。 另外,请务必做好修改GRUB设置等工作。