# Linux Kernel Build
这是操作系统系统的分类图,也是目前市场上存在的集中不同的操作系统设计思想,分别是:
- 宏内核:操作系统提供较为完备的运行组件
- 目的:通用性,完备性
- 微内核:操作系统仅提供基础系统服务的组件
- 目的:精简性,移植性
- 混合内核:鉴于前两种之间
- 目的:获取前两者的优点的平衡点
关于不同设计思想和好坏,这里不做具体比较,后面我们会专门分析这个内容,这里我们主要是有这个概念,并了解到我们的 Linux 使用的是宏内核的设计方法,下图为 Linux 内核的基本组件内容,对应后面的各个部分,我们能在宏观上对整个 Linux 内核的构成有了解。
# Get Source Code
Linux Code 阅读器能够在线查阅源码,The Linux Kernel documention 能够查阅到 Linux 版本的改动和一些开发的示例,源码下载可以在官网,如果网络太慢的话,可以使用中国的镜像站,例如清华镜像站等,下载的格式一般是 tar.gz
,然后执行以下的命令进行解压
#bash | |
$ tar -zxvf ./xxx.tar.gz #解压源码 |
然后我们可以使用 tree
这个工具对整个内核的文件目录架构有个直观的理解,执行以下的命令
#bash | |
$ tree -L 1 #查看树状的目录内容 深度为 1 |
如果没有该工具,可以使用
sudo apt install tree
进行下载
然后我们能看到这样的内容结构:
文件夹部分:
doucumentation
:说明文档,对每个目录的具体作用进行说明。arch
: 存放不同架构下的硬件的初始化代码和相关的配置文件arch/xxx/config
:该架构下的一般配置文件
block
:部分块设备驱动程序certs
:和证书相关crypto
:加密,压缩,CRC
校验算法drivers
: 驱动源码,这里包含所有提交到 Linux 社区的驱动,可以在配置文件勾选是否编译进去fs
:文件系统源码,这里包含了所有文件系统支持的源码,也可以在配置文件中进行勾选include
: 通用头文件包括对通用硬件,软件等的支持init
:这里包含的init
进程的动作,也就是跳转到内核后开始执行的ipc
:这里包含了进程间通信的机制和逻辑实现kernel
: 这里包含了内核的核心通用代码的实现kernel/config
: 一般的配置文件/boot/config-x.xx.x-xx-xxxxxx
当前正在运行内核的配置文件
lib
:程序的共享库的源文件,库文件代码 (与平台相关的)mm
:内存管理的源码net
:网络相关的源码usr
:统一系统资源的源码security
:Selinux 的模块sound
:音频设备的驱动程序Scripts
:编译过程中会执行的一些脚本程序
文件部分:
文件名 | 作用 |
---|---|
COPYING | 许可和授权信息。 |
CREDITS | 贡献者列表。 |
Kbuild | 内核设定脚本,可以对内核中的变量进行设定。 |
Kconfig | 配置哪些文件编译,那些文件不用编译。 |
Makefile | 该文件将编译参数、编译所需的文件和必要的信息传给编译器。 |
# Prepare The Environment
在环境准备这里我们介绍了两种不同的内核准备环境,然后我们根据自己的需求进行配置即可
- 通用 PC 的内核环境准备
- 实时 PC 的内核环境准备
需要根据不同的架构下载相应的工具链,具体可以参考以下命令:
# ARM
- 对于 ARM 32 位工具链
$ sudo apt-get install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf |
- 对于 ARM 64 位工具链
$ sudo apt-get install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu |
- 安装程序包依赖项
$ sudo apt-get install build-essential autoconf libtool cmake pkg-config git python-dev swig3.0 libpcre3-dev nodejs-dev |
# RISC-V
Toolchain:https://github.com/riscv/riscv-gnu-toolchain
- 安装必要的编译工具依赖
$ sudo apt-get install autoconf automake autotools-dev curl python3 libmpc-dev libmpfr-dev libgmp-dev gawk build-essential bison flex texinfo gperf libtool patchutils bc zlib1g-dev libexpat-dev |
- 编译工具的安装
$ ./configure --prefix=/opt/riscv | |
$ make |
# Normal Kernel
依赖包的安装,因为在编译过程中会使用一些软件和依赖库,以下为一般依赖安装的要求
#bash | |
$ sudo apt update #更新源 | |
$ sudo apt-get install build-essential # 常用构建工具集 包含(g++ gcc make dpkg lic6) | |
$ sudo apt install ncurses-base ncurses-bin #依赖包:是一个程序库,它提供了 API: 文字终端 | |
$ sudo apt install libncurses5-dev #lib 版本的依赖包 | |
$ sudo apt install flex bison #flex 弹性布局 bison 通用解析器生成器 | |
$ sudo apt install libssl-dev # 安全套接字协议 | |
$ sudo apt install libelf-dev elfutils #ELF 目标文件访问库 elfutils 用于读取,创建和修改 ELF 二进制文件 |
# Real-Time Kernel
Linux 源码没有实现硬实时,为了实现抢占式的内核环境,我们需要使用上游 Linux 社区提供的实时补丁,来使得内核编译提供硬实时的环境,因此我们首先需要从网站获取补丁,我们需要找到对应版本的补丁。下载文件的格式为 tar.gz
,然后放在 Linux Kernel 源码文件夹同级的目录。需要注意的是:如果你使用版本过新,有可能该版本社区仍未提供补丁。亦或是你的版本并没有在社区提供支持的计划内。因此有实时需求的使用者,建议先到网站查阅提供的内核版本补丁的列表,然后再根据项目需求到选择内核版本。
硬实时:不仅要求任务响应要实时,而且要求在规定的时间内完成事件的处理
下载后,执行以下命令
#bash | |
$ tar -xzvf linux-5.6.19.tar.gz #解压内核 | |
$ gunzip patch-5.6.19-rt12.patch.gz #解压补丁 | |
$ cd linux-5.6.19/ | |
$ patch -p1 < ../patch-5.6.19-rt12.patch #打补丁 |
接下来就是依赖包的安装
#bash | |
$ sudo apt update #更新源 | |
$ sudo apt-get install build-essential # 常用构建工具集 包含(g++ gcc make dpkg lic6) | |
$ sudo apt install ncurses-base ncurses-bin #依赖包:是一个程序库,它提供了 API: 文字终端 | |
$ sudo apt install libncurses5-dev #lib 版本的依赖包 | |
$ sudo apt install flex bison #flex 弹性布局 bison 通用解析器生成器 | |
$ sudo apt install libssl-dev # 安全套接字协议 | |
$ sudo apt install libelf-dev elfutils #ELF 目标文件访问库 elfutils 用于读取,创建和修改 ELF 二进制文件 |
# Configure Kernel Build
#bash | |
$ cd linux-x.x.xx | |
# 拿到你想要配置的 config 文件 | |
$ cp /arch/arm64/config/defconfig .config #将配置文件复制到一级目录 | |
# 以下六选一!!!只需要执行一次 | |
$ make config #老的 make 方式,使用的时每一次弹一句问 y/n | |
$ make oldconfig # 与上面这种类似,默认一样的配置不会弹出,新配置才会弹出提问 | |
$ make menuconfig # 文字终端的图形化,推荐使用 | |
$ make xconfig #图形化的配置 (需要安装图形化系统) | |
$ make kconfig #(KDE 桌面环境下,并且安装了 qt 开发环境) | |
$ make gconfig #(Gnome 桌面环境,并且安装 gtk 开发环境) |
这里我们演示了
make menuconfig
一般情况下,我们需要改动的主要是通用设置(也就是 General Setup
),除非需要进行内核定制或者裁剪的时候,我们才会去修改其他具体的配置项。
界面的介绍:
[]
:编译可选项,有两种状态,[*]
和[]
,表示编译进内核和不编译进内核。<>
:编译可选项,有三种状态,<*>
和<M>
和<>
,表示编译进内核,编译成模块化作为加载模块,不编译进内核。{}
:编译形式(一定要编译的),有两种{*}
和{M}
,表示编译进内核和编译成模块化作为加载模块。--
:依赖编译内容,因为开启一些编译选项而需要的依赖内容
控制介绍:
方向键:控制上下左右移动
Space :选择选项的内容
操作 | 功能 |
---|---|
方向键 | 移动光标 |
Space Bar (空格键) | 改变选项 |
Esc Esc | 退回上一级 |
Enter | 进入该内容 |
h | 查看内容的帮助界面 |
# Optional
# Unnecessary
这部分往往是我们不需要开启的编译内容,能够帮我们加快编译速度和精简内核
选项 | 意义 |
---|---|
Compile also drivers which will not load | 在选项中没有作为可加载模块的驱动也进行编译 |
Automatically append version information to the version string | 在编译完成后的名称加上版本信息 |
Build ID Salt | 用于验证编译的唯一性和可行度,一般用于分发版本的编译 |
Default init path | 可以修改 init 启动程序的文件目录 |
((none)) Default hostname | 主机名修改,看个人喜好 |
General notification queue | 内核事件能够传递到用户态 |
auditing infrastructure | 审核架构支持,使用 SELinux 才需要开启 |
Expose irq internals in debugfs | 中断 debug 才需要开启 |
Make expert-level adjustments to RCU configuration | 一般开发者用不到 RCU 的专家配置 |
Checkpoint/restore support | 进程 debug 的一些特性支持 |
Kernel->user space relay support | 如果有大量从内核态到用户态才需要开启 |
Include all symbols in kallsyms | 并不需要开启,会把素有的 symbols 包含进来 |
PC/104 support | 104 bus 支持 |
Kernel Performance Events And Counters | 默认不开启 |
Enable SLUB debugging support <br /> Enable memcg SLUB sysfs support by default <br /> | 内存 debug 相关的,一般不开启 |
Disable heap randomization <br /> Harden slab freelist metadata <br /> Page allocator randomization <br /> | 不开启,这是一个安全选项,没这种需求可以不开启 |
Profiling support | 不开启,除非需要分析支持 |
# Necessary
这部分往往是我们需要开启的编译内容,因为这是系统的基本运行依赖组件,取消后会导致系统异常
选项 | 意义 |
---|---|
Support for paging of anonymous memory (swap) | 交换分区支持,扩大虚拟内存的空间,提高系统 io 访问的性能 |
System V IPC | 进程间通讯的方式 |
POSIX Message Queues | 消息队列,也是进程见通讯的方式 |
Enable process_vm_readv/writev syscalls | 开启两项系统调到用,主要用于进程间直接读写操作 |
Timer tick handling (Idle dynticks system (tickless idle)) | 设置定时器的滴答,我们选择 <br /> Idle dynticks system (tickless idle) 该模式能够再 CPU 没有任务是,动态调整以达到节能的目的 <br /> 其他两个选项一个是固定滴答,一个是全调节滴答 |
Old Idle dynticks config | 老版本的配置 |
CPU/Task time and stats accounting | 主要是进程的信息统计,保持系统默认,可用于检测状态 |
Kernel .config support | 用于 Kernel 配置文件解析支持 |
Control Group support | CGroup 支持,需要保持默认 |
Namespaces support | 命名空间支持,也要保持默认 |
Initial RAM filesystem and RAM disk (initramfs/initrd) support | 目前认识主流的初始化 ramfs 的方式,其中压缩方式 Lz4 是最快,Gzip 最通用 |
Compiler optimization level | 默认 O2 |
Configure standard kernel features | 保持默认即可,除非你是专家 |
Enable bpf() system call <br /> Enable userfaultfd() system call <br /> Enable rseq() system call | 这是系统调用的支持,看不懂你就可以关掉。 |
Choose SLAB allocator | 建议选择 slab ,具体可以了解内存管理 |
Allow slab caches to be merged | 开启,用于回收 cache 的内存碎片 |
# Consideration
选项 | 意义 |
---|---|
uselib syscall | 如果运行程序有基于 libc5 之前版本编译的,需要开启这一项支持。 |
High Resolution Timer Support | 如果计算机支持高分率的定时器,可以开启。否则没有任何意义,只会加大内核镜像的大小 |
Preemption Model | 抢占模式选择 <br /> No Forced Preemption 一般服务器使用,保证吞吐量 <br /> Voluntary Kernel Preemption 一般普通 PC 用户 <br /> Preemptible Kernel 用于特定相应的终端,像 TV 等 <br /> Full Preempt Kernel 用于需要硬实时的设备。注意!需要打补丁后才能选择。 |
Kernel compression mode | 内核的压缩方式,最快最优压缩是 LV4,最通用是 Gzip |
CPU isolation | 确保运行关键任务的 CPU 不受任何 “噪声” 源(例如未绑定的工作队列)的干扰 |
Automatic process group scheduling | 进程自动调度,主要是能够让 CPU 均衡负载,但是可能会损失任务对 CPU 的亲和性 |
Enable deprecated sysfs features to support old userspace tools | 未兼容过去工具的,具体请 help 详细阅读 |
Boot config support | 开启对启动配置文件解析的支持 |
SLUB per cpu partial cache | 开启后能够加速读取 |
# Build Kernel
编译前准备
#bash | |
$ make prepare # 编译前准备 |
然后选择编译工具,如果是交叉编译,需要安装目标系统的编译工具链
#bash | |
$ make ARCH=xx.xxx #xx.xx 是编译工具链 |
这里有两个选项:
make zImage
make bzImage
区别:在 X86 平台上,zimage 只能用于小于 512k 的内核
#Bash 完成命令 | |
$ make bzImage ARCH=xx.xxx |
编译好的内核在: arch/xxx/boot/
目录下
如果要编译并打包成 deb 可执行以下命令
#bash | |
$ make -j`nproc` && make -j`nproc` bindeb-pk | |
$ make dep-pkg |
编译好的文件包再源码目录的上一级中
编译模块并安装
#bash | |
#编译和安装模块 | |
$ make modules | |
$ sudo make modules_install INSTALL_MOD_PATH=/lib/modules #将编译好的内核模块放在 c/lib/modules 下 |
安装内核
#自动安装 | |
$ sudo make install | |
#手动安装 | |
$ sudo cp linux-x.xx.xx/arch/xxx/boot/bzImage /boot/mylinux-x.x.xx | |
$ sduo cp linux-x.xx.xx/initrd-x.xx.xx /boot/initrd-x.xx.xx | |
#非本机手动安装 | |
$ sudo dpkg -i linux-*.deb |
更新引导
#Bash | |
$ sudo update-initramfs -c -k x.xx.xx | |
$ sudo update-grub # 更新引导 | |
$ sudo update-grub2 | |
$ reboot #测试 |
# Summary
本文介绍了 Linux Kernel 的基本概念以及编译的基本流程和注意事项,已经常见选项控制,主要是让大家对内核编译有一个了解和实操的指南,是一本指南式博客。