在上一篇中,我记录了如何通过源码直接编译并安装 Linux 内核,这一篇将介绍如何将内核编译为 deb 包,使我们可以方便地利用系统包管理工具对编译好的内核进行安装和删除等管理操作,以及如何利用 GitHub Actions 帮我们自动化编译。

本地编译

主要参考 debian 的官方文档:编译内核源代码:Debian 内核团队推荐

相比于前篇介绍的步骤,主要区别如下:

  1. 使用 sudo apt-get build-dep linux 的方式即可自动安装编译内核所需的全部依赖
  2. 可以用 wget 直接从 http://www.kernel.org/pub/linux/kernel/ 这个位置下载所需源码包
  3. 使用 make deb-pkg 命令编译,即可生成内核的 deb 安装包。

利用 KVM 虚拟机隔离编译环境

为了不污染工作机的系统环境,我编译软件一般都是先用 KVM 开一个虚拟机进行编译,然后将产物拷贝到物理机使用,下面记录完整流程

  1. 下载系统镜像 iso 文件,我下载的是 UOS 系统的安装镜像,放置于 ~/Downloads/uniontechos-desktop-21.0-home-beta6-amd64.iso

  2. 创建编译所需的虚拟硬盘,完整编译大约需要 40G 左右空间

    1
    2
    cd ~/Qemu/uos/
    qemu-img create -f raw disk 64G
  3. 用 KVM 引导安装镜像,进入 Live-CD 系统,参考:建议Linux用户尝试下kvm虚拟机: 最简单的使用场景——试用-Linux-LiveCD

    1
    2
    # 这里配置了端口转发,用于内核编译好之后可以方便地拷贝出来
    kvm -cpu host -smp 4 -m 16G -cdrom ~/Downloads/uniontechos-desktop-21.0-home-beta6-amd64.iso -hda ~/Qemu/uos/disk -nic user,hostfwd=tcp::8000-:8000

    ps.UOS 系统的安装镜像默认是没有进入 Live-CD 模式的选项的,需要通过如下方式进入:

     1. 在启动项选择界面,按 `Tab` 键进入编辑模式
     2. 将下图所示原本启动参数中的 `livecd-installer` 改错或者删除,然后回车启动

    live-cd

  4. 进入系统后,开启终端,首先用 fdisk 命令为虚拟磁盘创建分区
    fdisk
    a. 输入命令 sudo fdisk /dev/sda
    b. 按 n 新建分区,然后一直默认值,输入四次回车
    c. 输入 w,将变更写入硬盘

  5. 格式化硬盘分区并挂载

    1
    2
    sudo mkfs.ext4 /dev/sda1
    sudo mount /dev/sda1 /mnt/

    df

之后就可以在 /mnt/ 路径下进行编译操作了

编译步骤

  1. 为 apt 开启源码仓库
    为了可以使用 apt build-dep linux 自动安装编译所需的依赖,需要先为 apt 配置源码仓库
    参考 移植一个软件包到 stable 系统,编辑 /etc/apt/sources.list,有些发行版(如 debian)默认是将 deb-src 开头的源码仓库注释掉的,只要取消注释即可;而 UOS 是没有的,所以如下方式添加:

    1
    echo "deb-src https://home-packages.chinauos.com/home plum main contrib non-free" >> /etc/apt/sources.list
  2. 安装所需依赖

    1
    2
    3
    4
    5
    6
    # 编辑 /etc/apt/sources.list 后首先需要更新 apt
    sudo apt update
    # 安装 wget 用于下载内核源码包
    sudo apt install -y wget
    # 自动下载编译所需的所有依赖
    sudo apt build-dep -y linux
  3. 下载源码、应用 .config、编译 deb 包

    1
    2
    3
    4
    5
    6
    # 下载需要的源码,可以用 uname -r 查看系统内核版本号
    wget http://www.kernel.org/pub/linux/kernel/v5.x/linux-5.10.41.tar.xz
    tar -xf linux-5.10.41.tar.xz
    cd linux-5.10.41/
    cp /boot/config-"$(uname -r)" .config
    make deb-pkg -j4

编译结束后,会在 解压的源码包的上级目录下 生成几个 deb 包:
build_done

  1. 拷贝出需要的 deb 包,在物理机安装测试
    比较方便的从 kvm 虚拟机中拷贝文件到物理机的方法是,在虚拟机中利用 python 开一个文件下载服务器。
    由于我们开启虚拟机的时候已经设置了虚拟机 8000 端口到物理机 8000 端口的映射,所以直接在虚拟机的 /mnt/ 目录下执行:
    1
    python3 -m http.server
    然后在物理机打开浏览器,访问 http://127.0.0.1:8000/ 即可访问下载虚拟机中的文件。

我们只需要下载 linux-headerslinux-image 开头的两个 deb 文件即可,不用下载名字中带有 dbg,大小 1G 左右的包,那个是调试内核使用的。

download

下载完成后,双击 deb 文件安装,或用 sudo dpkg -i *.deb 安装即可。

自动编译脚本

针对上面的操作,我写了一个自动化的脚本,可以在这里得到:
https://github.com/debuggerx01/kernel_deb_builder/blob/main/build_on_uos.sh

注意,只能在 UOS 系统上使用该脚本。

利用 GitHub Actions 自动编译

GitHub Actions 是 GitHub 的持续集成服务,于2018年10月推出。
简单入门可以参考:GitHub Actions 入门教程—— 阮一峰

编译脚本

由于 GitHub Actions 提供的 Linux 运行环境是 Ubuntu,所以需要针对这个系统来编写编译脚本
build_action.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#!/usr/bin/env bash

# 这一步用于从脚本同目录下的 config 文件中获取要编译的内核版本号
VERSION=$(grep 'Kernel Configuration' < config | awk '{print $3}')

# add deb-src to sources.list Ubuntu系统只需要把系统 apt 配置中的源码仓库注释取消掉即可
sed -i "/deb-src/s/# //g" /etc/apt/sources.list

# install dep
sudo apt update
sudo apt install -y wget
sudo apt build-dep -y linux

# change dir to workplace
cd "${GITHUB_WORKSPACE}" || exit

# download kernel source
wget http://www.kernel.org/pub/linux/kernel/v5.x/linux-"$VERSION".tar.xz
tar -xf linux-"$VERSION".tar.xz
cd linux-"$VERSION" || exit

# copy config file
cp ../config .config

# 应用 patch.d/ 目录下的脚本,用于自定义对系统源码的修改
# apply patches
# shellcheck source=src/util.sh
source ../patch.d/*.sh

# build deb packages
# 获取系统的 CPU 核心数,将核心数X2设置为编译时开启的进程数,以加快编译速度
CPU_CORES=$(($(grep -c processor < /proc/cpuinfo)*2))
make deb-pkg -j"$CPU_CORES"

# move deb packages to artifact dir
cd ..
mkdir "artifact"
# 删除无用且巨大的调试包
rm ./*dbg*.deb
mv ./*.deb artifact/

创建 action

.github/workflows/build.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
name: Build kernel deb packages
# 在代码推送的时候自动触发 action
on: push
jobs:
build:
name: Build kernel
runs-on: ubuntu-latest
steps:
# 由于默认的 Ubuntu 环境只有 17G 左右的剩余空间,所以我们需要先利用如下 action 对编译环境进行扩容
- name: Maximize build space
uses: easimon/[email protected]
with:
# 这个值是保留给系统的空间大小,之前设置太小,总会在安装依赖或者编译过程中报设备空间不足的错误而导致编译失败
root-reserve-mb: 4096
swap-size-mb: 512
remove-dotnet: 'true'
remove-android: 'true'

# 不要忘记先 checkout 代码
- name: Checkout
uses: actions/[email protected]

# 调用自动编译脚本,需要 sudo 权限
- name: Build
run: sudo bash build_action.sh

# 将编译后的产物保留,以供直接通过网页下载
- name: Artifact
uses: actions/[email protected]
with:
name: artifact
path: ${{ github.workspace }}/artifact/

如下图,经过 2 小时 40 分钟的编译,最终生成了 70 MB左右的内核 deb 包,点击 artifact 下载解压安装即可 🐕
action_done

如何使用

如果您想要利用我的这个自动化脚本根据自己的需求编译内核,请参考如下步骤:

  1. Fork 仓库
    访问 https://github.com/debuggerx01/kernel_deb_builder ,点击右上角的 Fork 按钮,并 clone 到本地

  2. 更新 config 文件
    在本地将您获取的 config 文件替换根目录下的 config,可以从您系统的 /boot/config* 文件复制,或者手动编辑

  3. 编写自定义修改脚本
    当前 /patch.d/ 目录下的修改脚本是只针对我自己的需求编写的,建议您先将其删掉,然后编写自己的脚本放在这个目录下,在脚本执行过程中会自动应用该目录下的所有脚本

  4. 推送修改
    推送后,action 自动触发,可以在您的仓库页面的 Actions 选项卡查看进度详情。

Enjoy~ 😁

更新

卧槽,偶然发现原来可以通过禁用 .configDEBUG_INFO 选项来大幅减少编译时所需的磁盘空间,同时耗时也会减少很多!!😯

只要在解压后的源码根目录,确保 .config 已存在,执行:

1
scripts/config --disable DEBUG_INFO

这样一来,编译所需的空间从接近 40 G 直线减少到大约 4 G,actions 脚本中的扩容系统空间的部分就完全不需要了,而且生成的 deb 包也没有那个 1 G 多的 dbg 包,编译时间更是减少了一个多小时 😆

而如果是在 kvm 虚拟机中编译,如果内存分配得足够大,甚至可以完全在内存中编译,而不用额外挂载一个虚拟磁盘了~

kvm

如上图,我给 kvm 分配了 16 G内存,那么其一半 8 G 会自动以 tmpfs 挂载到 \tmp,所以只要切换到这个目录下执行编译脚本即可!