Bootstrap

Linux内核开发——编译Ubuntu 20.04内核代码

1. 完整编译

1.1. 查看内核版本

可以使用下列

uname -r
5.13.0

1.2. 下载内核代码

可以直接下载一个比当前内核版本新的代码,也可以去https://mirrors.edge.kernel.org/pub/linux/kernel下载自己内核使用的版本。此文下载的是更新的版本 5.17.15,去官网https://www.kernel.org/,下载linux-5.17.12.tar.xz。

1.3. 解压

tar -xavf linux-5.17.12.tar.xz

1.4. 配置环境

1.4.1. 更换源

不同版本的Ubuntu,需要对应不同版本的源。可以通过。https://mirror.tuna.tsinghua.edu.cn/help/ubuntu/上可以选择不同版本的Ubuntu对应的源。

# 默认注释了源码镜像以提高 apt update 速度,如有需要可自行取消注释
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy main restricted universe multiverse
# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-updates main restricted universe multiverse
# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-updates main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-backports main restricted universe multiverse
# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-backports main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-security main restricted universe multiverse
# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-security main restricted universe multiverse

# 预发布软件源,不建议启用
# deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-proposed main restricted universe multiverse
# deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-proposed main restricted universe multiverse

将上述内核拷贝进下面打开的文件。

sudo gedit /etc/apt/sources.list

拷贝完了之后,更新源

sudo apt-get update

1.4.2. 安装依赖

sudo apt-get install gcc g++
sudo apt-get install libncurses5-dev 
sudo apt-get install build-essential
sudo apt-get install kernel-package 
sudo apt-get install libssl-dev
sudo apt-get install libc6-dev 
sudo apt-get install bin86
sudo apt-get install flex 
sudo apt-get install bison  
sudo apt-get install qttools5-dev  
sudo apt-get install libelf-dev

1.5. 编译

1.5.1. 清理

首次编译可以不用清理,编译之后如果想完整重新编译,可以使用清理命令。

# make mrproper会删除配置的.config以及其他备份
sudo make mrproper 
# make clean会删除编译过程中生成的中间文件和内核镜像文件
sudo make clean

1.5.2. 配置内核

内核的编译配置放在.config文件中,内核arch目录中有提供不同架构的推荐.config供参考。也可以从当前内核的源usr/src/xxx目录中拷贝.config文件到当前目录。如果使用内核自带的参考.config。
可以使用命令:

make xconfig

会启动一个Qt的配置窗口,默认保存即可。

1.5.3. 编译

  1. 全部编译
    # 编译内核镜像及其他所有模块,-j6指定6个线程进行编程,提升编译效果
    sudo make -j6
    
  2. 单独编译
    # 编译内核镜像
    sudo make bzImage -j6
    # 编译其他所有模块
    sudo make modules -j6
    

1.5.4. 安装

安装其实就是将生成的ko库文件拷贝到指定目录lib\modules。

sudo make modules_install -j6  

1.5.5. 安装启动文件

分别生成initrd文件,将镜像文件和map文件拷贝到boot目录。

sudo mkinitramfs /lib/modules/5.17.12 -o /boot/initrd.img-5.17.12
sudo cp arch/x86/boot/bzImage /boot/vmlinuz-5.17.12
sudo cp System.map /boot/System.map-5.17.12

1.5.6. 更新grub

更新grub文件,命令会自动检测boot目录中的initrd文件,将最新的initrd文件设置为默认启动版本。

sudo update-grub

1.6. 重启验证

重启电脑,在终端查看版本号

uname -r 
5.17.12

1.7. 异常处理

  1. 提示相关库缺失,自行安装相关库依赖。
  2. 提示“make[1]: *** No rule to make target ‘debian/certs/[email protected]’, needed by ”,将下面的内存修改为空
    CONFIG_SYSTEM_TRUSTED_KEYS=“debian/canonical-certs.pem”
    CONFIG_SYSTEM_REVOCATION_KEYS=“debian/canonical-revoked-certs.pem”
    新值
    CONFIG_SYSTEM_TRUSTED_KEYS=“”
    CONFIG_SYSTEM_REVOCATION_KEYS=“”
  3. 提示“BTF: .tmp_vmlinux.btf: pahole (pahole) is not available”时,修改
    CONFIG_DEBUG_INFO_BTF=y

    CONFIG_DEBUG_INFO_BTF=n

1.8. 配置ccache

为了加速gcc编译内核,编译过的文件可以缓存起来,再次编译时就不用编译了。

1.8.1. 安装

sudo apt install ccache

1.8.2. 配置

sudo gedit ~/.bashrc 
#在末尾回车,添加如下语句,注意ubuntu要改成你的用户名。
export CCACHE_DIR="/home/ubuntu/.ccache"  
export CC="ccache gcc" export CXX="ccache g++" 
export PATH="$PATH:/usr/lib/ccache" 
#Ctrl+S或点击“保存”按钮保存,然后使其生效。
source ~/.bashrc     
#20G是cache大小,可以自行指定
ccache -M 20G

2. 编译指定模块

整个内核是由一个初始化模块程序initrd.img,内核可执行程序vmlinuz和这么多动态库ko文件组成。Linux在启动时可以动态选择需要的库来进行加载,这样可以提升内核的启动效率。另外,将模块编译成动态库(ko文件),在进行自定义模块功能时,也可以只用编译单个模块的代码,也更便利。模块是否编译,是编译进内核镜像还是单独生成内核模块文件ko文件,这些都是在.config文件中配置的。

2.1. 配置文件介绍

2.1.1. .config文件

哪些模块编译进主内核模块,哪些模块单独作为动态库存在。这些都是通过一个配置文件.config来描述的。.config文件中有记录一些配置名,一些配置的大小,剩余的主要是描述是描述是否编译进内核文件,是否编译为单独的模块。如:

# m表示编译成单独的动态库ko文件
CONFIG_ACPI_SBS=m
# y表示编译进内核文件
CONFIG_ACPI_HED=y
# 如果没有配置则表示不编译或者配置为n
CONFIG_ACPI_HED=n

2.1.2. Kconfig文件

哪些字段可以配置,可以匹配什么样的值,默认配置的值是什么等,这些准备信息都是记录kconfig文件中。
Kconfig文件有基本的语法要求:

  config TMPFS_POSIX_ACL
    bool "Tmpfs POSIX Access Control Lists"
    depends on TMPFS
    select GENERIC_ACL
    help
      POSIX Access Control Lists (ACLs) support permissions for users and
      groups beyond the owner/group/world scheme.
      To learn more about Access Control Lists, visit the POSIX ACLs for
      Linux website <http://acl.bestbits.at/>.
     If you don't know what Access Control Lists are, say N.

config是关键字,指定后面的内容为.config文件中的可配置字段名,.config文件中实际使用时会加上CFONFIG_。
bool是描述此字段的类型。Kconfig支持的类型有:bool、tristate、string、hex和int。
● bool类型取值:y或n
● trisate类型取值:y,n或m(三态值,y表示安装进内核,n表示不安装,m表示以模块安装)
● string类型取值:字符串
● hex类型取值:十六进制数字
● int类型取值:十进制数字
bool后面的字符串表示提示信息,在配置界面中显示的。
depends on表示此模块依赖于TMPFS模块,只有TMPFS模块被选中时,此配置信息才可以设置。
select表示此模块被选中时,GENERIC_ACL也会被选中。
help下面的是帮助信息。
详细说明见:Documentation/kbuild/kconfig-language.rst

2.1.3. menuconfig

参考Kconfig文件来配置.config文件,可以完成Kernel的编译配置,但是此方法不够直观,效率不高。make menuconfig可以根据arch/目录下不同架构下的Kconfig和.config文件生成一个菜单窗口,更直观的配置有关信息。
在这里插入图片描述

Kconfig中的bool类型在menucofig中对应[],有[]表示选中,[]表示不选中。
Kconfig中的tristate类型在menuconfig中应对<>,<
>表示选中编译进内核,表示以模块化编译,<>表示不编译。
配置完成之后,可以保存为.config文件。

2.1.4. xconfig

menuconfig是字符终端,依然不够直观,xconfig是基于xWindow的UI窗口终端,更清晰直观。 其功能和menuconfig一样的。

2.1.5. oldconfig

当.config有修改时,调用make oldconfig来验证相关修改是否匹配正确,会自动修改。

2.1.6. defcofig

内核源代码目录中的arch/下有不同架构cpu下的defconfig文件仅参考配置。

2.2. 配置指定模块

有些模块已经配置为编译为ko文件,有些模块没有配置。查看kconfig文件,找出该模块是否可以编译为模块,是否有依赖模块,有则选中,然后在.config中将指定模块配置为CONFIG_XX=m。

2.3. 完整编译

首次使用时,需要完整编译。

2.4. 单独编译

内核模块的makefile中有提供相关方法:

$(MAKE) -C ( K D I R ) M = (KDIR) M= (KDIR)M=(PWD) modules

KDIR为内核顶层目录。
PWD为指定编译模块的目录。
例如:

cd /home/michael/work/Kernel/linux-5.17.12
sudo make -C CONFIG_JBD2=m ./ M=fs/jbd2 modules

2.5. 拷贝

拷贝编译的模块到指定目录

sudo cp jbd2.ko /lib/modules/5.17.12/kernel/fs/jbd2

2.6. 更新驱动信息

sudo depmod-a
# 将变化的驱动信息更新到相关文件中.

2.7. 加载驱动

modprobe jbd2,动态加载驱动,如果jbd2有依赖驱动,会先加载依赖驱动再加载此驱动。
insmod jbd2,只加载此驱动,如果有依赖驱动会加载失败。

3. 制作deb安装文件

3.1. deb-pkg

上面的编译安装方便比较麻烦,如果想部署多台电脑时更不方便。如果能够制作安装包进行部署升级会更方便。
make deb-pkg
上面的命令即可以完成编译并生成deb安装文件存放在上级目录,有三个安装文件,分别为头文件,镜像文件以及libc开发文件,头文件和libc安装包都是用来进行应用程序开发的,可以不用安装。

linux-headers-5.17.12_5.17.12-1_amd64.deb
linux-image-5.17.12_5.17.12-1_amd64.deb
linux-libc-dev_5.17.12-1_amd64.deb

然后可以使用如下命令安装升级重启。

sudo dpkg -i *.deb
sudo update-grub 
sudo reboot

3.2. make-kpkg

make-kpkg也是编译并生成deb安装文件,并且可以修改版本号等操作。

  1. 首先要安装make-kpkg
sudo apt install kernel-package
  1. 开始
sudo make-kpkg  --initrd --append-to-version -20220228  --revision 001 kernel_image kernel_headers -j4

参数说明:
–initrd: 生成initramfs
–append-to-version:生成内核版本附加信息
–revision:deb文件的版本信息,只影响文件名
kernel_image:内核和模块的安装包
kernel_headers:生成内核头文件的安装包。
-j:指定多少个线程编译内核
3. 安装

sudo dpkg -i *.deb
sudo update-grub 
sudo reboot
;