本文地址:https://www.ebpf.top/post/ubuntu_2104_bpf_env

1. 系統安裝

1.1 Vagrant

Vagrant 是一款用於構建及配置虛擬開發環境的軟體,基於 Ruby,主要以命令列的方式執行。Vagrant 由 HashiCorp 官方出品,相信提到大名鼎鼎的 HashiCorp 公司,大家還能夠聯想到著名的安全密碼儲存 Valut、服務註冊發現 Consul、大規模排程系統 Normad 等等。

Vagrant 可基於官方提供的各種虛擬機器打包檔案(比如 VirtualBox 的 Box 檔案)快速搭建各種系統的驗證環境,也可以靈活通過配置式的檔案管理多 VM 環境,同時具有平臺適配性,是非常好的 VM 管理工具。但其自身並不提供虛擬化環境,需要配合 VirtualBox、WMware、KVM 等虛擬化技術,1.6 版本底層也支援使用容器,可以將容器替代完整的虛擬系統。

Vagrant 的架構使用 "Provisioners" 和 "Providers" 作為開發環境的構建模組。

|--vagrant
|--Providers 如:VirtualBox、Hyper-V、Docker、VMware、AWS
|--Boxex 如:Centos7。與映象類似
|--Provisioners 如:'yum intall -y python' 等自定義自動化指令碼

Vagrant 作為最外層的虛擬軟體,目的是幫助開發者更容易地與 Providers 互動。Vagrantfile 記錄 Providers 和 Provisioners 的相關資訊。

Providers 作為服務,幫助 vagrant 使用 Boxes 建立和建立虛擬環境。Vagrant 提供的內嵌的 Provider 有 VirtualBox、Hyper-V、Docker、VMware 等。

在 Mac 系統中如果使用 Brew 管理包,則可以通過 brew 命令直接安裝。其他系統的安裝方式可參考 下載 Vagrant,檢視詳情。

$ brew install vagrant

在安裝 vagrant 成功後,我們還需要安裝底層的虛擬化軟體(即上述架構中的 Providers)才能正常工作,這裡我們使用 VirtualBox。

1.2 VirtualBox

Oracle VirtualBox 是德國公司 InnoTek 出品的虛擬機器軟體,現在由 Oracle 公司管理和發行,適用於多平臺系統。使用者可以在 VirtualBox 上安裝並且執行 Solaris/Windows/Linux 等系統作為客戶端作業系統。

在 Mac 系統上可以通過下載 dmg 檔案進行安裝。其他系統的安裝參見 Oracle VM VirtualBox 基礎包

在 Vagrant 和 VirtualBox 安裝完成後,我們可以使用以下命令快速搭建一個 Ubuntu 的 VM:

$ vagrant init ubuntu/hirsute64
$ vagrant up

在啟動成功後,我們可以通過 vagrant ssh 直接登入到 VM 系統中。

1.3 系統環境搭建

2021 年 4 月 22 號,Ubuntu 釋出了 21.04 Hirsute Hippo 版本 [1],核心採用 5.11.0 版本。這裡選擇最新的 Ubuntu 發行版本,主要考慮 BPF 技術演進較快,新功能基本都需要高版本核心,採用最新發行的 Ubuntu 發行版本,方便後續的 BPF 功能學習和核心版本升級。更多 Ubuntu 版本釋出的詳情可參見官方 Wiki

這裡我們使用 Vagrant 虛擬機器的方式快速搭建環境,Hirsute Box 映象 可在官方提供的 Vagrant Boxs 市場 中查詢,ubuntu/hirsute64 Box 的映象大小為 600M 左右。

$ vagrant init ubuntu/hirsute64
$ vagrant up # 如果 box 有更新,可以使用 update 子命令更新
#$ vagrant box update

如果從國外下載映象過慢,可以通過其他方式下載 Box 檔案,使用命令: vagrant box add <name> <boxpath> 然後匯入到本地後再啟動上述命令。為方便大家快速下載我已經上傳了一份到 百度網盤,提取碼【bgfg】。

$ vagrant box add ubuntu/hirsute64 ~/Downloads/ubuntu_2104.box
$ vagrant box list
ubuntu/hirsute64 (virtualbox, 20210923.0.0)

另外,我們也可以使用以下命令將正在執行的 VM 重新打包成 Box 檔案:

$ vagrant package --output hirsute64_my.box

如果上述安裝順利,我們可以通過 ssh 的方式登入到新建立的 VM 機器中。

$ vagrant ssh
Welcome to Ubuntu 21.04 (GNU/Linux 5.11.0-36-generic x86_64) * Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage System information as of Sat Sep 25 11:22:52 UTC 2021 System load: 0.0 Processes: 105
Usage of /: 3.4% of 38.71GB Users logged in: 0
Memory usage: 18% IPv4 address for enp0s3: 10.0.2.15
Swap usage: 0% * Super-optimized for small spaces - read how we shrank the memory
footprint of MicroK8s to make it the smallest full K8s around. https://ubuntu.com/blog/microk8s-memory-optimisation 0 updates can be applied immediately. Last login: Sat Sep 25 08:13:09 2021 from 10.0.2.2
vagrant@ubuntu-hirsute:~$ uname -a
Linux ubuntu-hirsute 5.11.0-36-generic #40-Ubuntu SMP Fri Sep 17 18:15:22 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

通過檢查 CONFIG_DEBUG_INFO_BTF 核心編譯選項確認系統是否已經支援 BTF 能力,這是 BPF CO-RE (Compile Once – Run Everywhere) 能力的基礎。

$ grep CONFIG_DEBUG_INFO_BTF /boot/config-5.11.0-36-generic
CONFIG_DEBUG_INFO_BTF=y
CONFIG_DEBUG_INFO_BTF_MODULES=y

至此,我們的 BPF VM 環境已基本準備完成。

2. BPF 程式編譯

對於我們編譯 BPF 程式而言需要系統已經安裝了必備的 linux-headers 包。在 Ubuntu/Debian/Arch/Manjaro 發行版系統中需要安裝 linux-headers 包,而在 CentOS/Fedora 發行版系統中需要安裝 kernel-headers 和 kernel-devel 兩個包。

首先我們在 VM 中安裝 linux-headers 包:

$ sudo apt update
$ sudo apt install linux-headers-$(uname -r)
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
linux-headers-5.11.0-36-generic is already the newest version (5.11.0-36.40).

如果是通過核心升級版本的場景,務必確認 linux-headers 版本與當前執行的核心版本一致。

為了能夠編譯 BPF 程式,我們還需要安裝編譯所依賴的工具包:

$ sudo apt install -y bison flex build-essential git cmake make libelf-dev  clang llvm strace tar libfl-dev libssl-dev libedit-dev zlib1g-dev  python  python3-distutils

$ clang --version
Ubuntu clang version 12.0.0-3ubuntu1~21.04.2
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin $ llc --version
LLVM (http://llvm.org/):
LLVM version 12.0.0 Optimized build.
Default target: x86_64-pc-linux-gnu
Host CPU: haswell Registered Targets:
...
bpf - BPF (host endian)
bpfeb - BPF (big endian)
bpfel - BPF (little endian)
...

通過上述 clang/llvm 版本資訊檢視,我們可以知道在 Ubuntu 21.04 版本中,安裝的為 12.0 的版本,這可以滿足我們對於 CO-RE 能力的要求(>= 9.0 版本)。

我們使用 libbpf-bootstrap 來進行編譯驗證,同時使用 --recursive 引數將 libbpf-bootstrap 倉庫中的依賴子模組 libbpf 同時下載到本地。

$ git clone --recursive https://github.com/libbpf/libbpf-bootstrap.git
Cloning into 'libbpf-bootstrap'...
remote: Enumerating objects: 260, done.
remote: Counting objects: 100% (172/172), done.
remote: Compressing objects: 100% (96/96), done.
remote: Total 260 (delta 74), reused 149 (delta 64), pack-reused 88
Receiving objects: 100% (260/260), 1.65 MiB | 280.00 KiB/s, done.
Resolving deltas: 100% (118/118), done.
Submodule 'libbpf' (https://github.com/libbpf/libbpf.git) registered for path 'libbpf' # 子模組 libbpf 下載
Cloning into '/home/vagrant/libbpf-bootstrap/libbpf'...
remote: Enumerating objects: 6174, done.
remote: Counting objects: 100% (1222/1222), done.
remote: Compressing objects: 100% (336/336), done.
remote: Total 6174 (delta 965), reused 942 (delta 866), pack-reused 4952
Receiving objects: 100% (6174/6174), 3.59 MiB | 547.00 KiB/s, done.
Resolving deltas: 100% (4087/4087), done.
Submodule path 'libbpf': checked out '8bf016110e683df2727a22ed90c9c9860c966544' $ cd libbpf-bootstrap/examples/c
~/libbpf-bootstrap/examples/c$ make

如沒有錯誤提示,且通過下述的 ls 命令可檢視到對應的二進位制檔案,則表明編譯成功。

$ ls -hl
-rwxrwxr-x 1 vagrant vagrant 1.4M Sep 25 13:30 bootstrap
-rwxrwxr-x 1 vagrant vagrant 1.3M Sep 25 13:30 fentry
-rwxrwxr-x 1 vagrant vagrant 1.3M Sep 25 13:30 kprobe
-rwxrwxr-x 1 vagrant vagrant 1.3M Sep 25 13:30 minimal
-rwxrwxr-x 1 vagrant vagrant 1.3M Sep 25 13:30 uprobe # 通過 bootstrap 程式執行驗證
~/libbpf-bootstrap/examples/c$ sudo ./bootstrap
TIME EVENT COMM PID PPID FILENAME/EXIT CODE
13:41:14 EXEC ls 16473 7633 /usr/bin/ls
13:41:14 EXIT ls 16473 7633 [0] (1ms) # 使用者空間程式與核心中的 BPF 通訊的唯一入口是 bpf() 系統呼叫,可以通過 strace 檢視整個互動流程
~/libbpf-bootstrap/examples/c$ sudo strace -e bpf ./bootstrap
bpf(BPF_PROG_LOAD, {prog_type=BPF_PROG_TYPE_SOCKET_FILTER, insn_cnt=2, insns=0x7fffd2d6e3c0, license="GPL", log_level=0, log_size=0, log_buf=NULL, kern_version=KERNEL_VERSION(0, 0, 0), prog_flags=0, prog_name="", prog_ifindex=0, expected_attach_type=BPF_CGROUP_INET_INGRESS, prog_btf_fd=0, func_info_rec_size=0, func_info=NULL, func_info_cnt=0, line_info_rec_size=0, line_info=NULL, line_info_cnt=0, attach_btf_id=0, attach_prog_fd=0}, 128) = 3
bpf(BPF_BTF_LOAD, {btf="\237\353\1\0\30\0\0\0\0\0\0\0\20\0\0\0\20\0\0\0\5\0\0\0\1\0\0\0\0\0\0\1"..., btf_log_buf=NULL, btf_size=45, btf_log_size=0, btf_log_level=0}, 128) = 3
...

至此我們可以將 VM 匯出為 Box 檔案以便作為後續的基礎,需要在 VM Vagrant 檔案所在的目錄操作執行,匯出過程會自動關閉 VM 虛擬機器。

ubuntu_21_04$ vagrant package --output ubuntu_21_04_bpf.box
==> default: Attempting graceful shutdown of VM...
==> default: Clearing any previously set forwarded ports...
==> default: Exporting VM...

ubuntu_21_04_bpf.box 檔案已經上傳 百度雲盤,提取碼【if2j】,檔案大小 1.1G。

3. 核心程式碼安裝和 sample/bpf 模組編譯

安裝 Ubuntu 21.04 原始碼,並解壓原始碼:

$ sudo apt-cache search linux-source
linux-source - Linux kernel source with Ubuntu patches
linux-source-5.11.0 - Linux kernel source for version 5.11.0 with Ubuntu patches $ sudo apt install linux-source-5.11.0
$ sudo cd /usr/src
$ sudo tar -jxvf linux-source-5.11.0.tar.bz2
$ sudo cd /usr/src/linux-source-5.11.0

原始碼編譯 sample/bpf 模組:( 為方便編譯,統一切換成 root 使用者 )

# cp -v /boot/config-$(uname -r) .config
# make oldconfig && make prepare
# make headers_install
# apt-get install libcap-dev // 解決 sys/capability.h: No such file or directory
# make M=samples/bpf // 編譯最後,可能會報部分 WARNING,可以忽略
samples/bpf/Makefile:231: WARNING: Detected possible issues with include path.
samples/bpf/Makefile:232: WARNING: Please install kernel headers locally (make headers_install).
WARNING: Symbol version dump "Module.symvers" is missing.
Modules may not have dependencies or modversions.
MODPOST samples/bpf/Module.symvers
WARNING: modpost: Symbol info of vmlinux is missing. Unresolved symbol check will be entirely skipped. # ls -hl samples/bpf/ // 檢視是否生產需要的樣例程式碼,如果生成了可執行檔案,這表明編譯成功 # ./tracex1
libbpf: elf: skipping unrecognized data section(4) .rodata.str1.1
libbpf: elf: skipping unrecognized data section(17) .eh_frame
libbpf: elf: skipping relo section(18) .rel.eh_frame for section(17) .eh_frame
<...>-36521 [000] d.s1 2056.935795: bpf_trace_printk: skb 00000000a32b8f51 len 84
<...>-36521 [000] d.s1 2056.935817: bpf_trace_printk: skb 0000000094918e19 len 84

4. 附加 BPF 樣例編譯

在 samples/bpf 目錄包含接近 60 個相關的程式,給與我們學習提供了便利,同時也提供了一個組織編譯的 Makefile 框架。如果我們用 C 語言編寫的 BPF 程式編譯可以直接在該目錄提供的環境中進行編譯。

如果是需要單獨編譯的場景,也可以參考 BCC 倉庫中的 libbpf-tools 下的樣例。

samples/bpf 下的程式一般組成方式是 xyz_user.c 和 xyz_kern.c:

  • xyz_user.c 為使用者空間的程式用於設定 BPF 程式的相關配置、載入 BPF 程式至核心、設定 BPF 程式中的 map 值和讀取 BPF 程式執行過程中傳送至使用者空間的訊息等。目前 xyz_user.c 與 xyz_kern.c 程式在互動實現都是基於 bpf() 系統呼叫完成的。直接使用 bpf() 系統呼叫涉及的引數和細節比較多,使用門檻較高,因此為了方便使用者空間程式更加易用,核心提供了 libbpf 庫封裝了對於 bpf() 系統呼叫的細節。
  • xyz_kern.c 為 BPF 程式程式碼,通過 clang 編譯成位元組碼載入至核心中,在對應事件觸發的時候執行,可以接受使用者空間程式傳送的各種資料,並將執行時產生的資料傳送至使用者空間程式。

完整的樣例程式碼參見 hello_world_bpf_ex

hello_user.c

#include <stdio.h>
#include <unistd.h>
#include <bpf/libbpf.h>
#include "trace_helpers.h" int main(int ac, char **argv)
{
struct bpf_link *link = NULL;
struct bpf_program *prog;
struct bpf_object *obj;
char filename[256]; snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
obj = bpf_object__open_file(filename, NULL);
if (libbpf_get_error(obj)) {
fprintf(stderr, "ERROR: opening BPF object file failed\n");
return 0;
} prog = bpf_object__find_program_by_name(obj, "bpf_hello");
if (!prog) {
fprintf(stderr, "ERROR: finding a prog in obj file failed\n");
goto cleanup;
} /* load BPF program */
if (bpf_object__load(obj)) {
fprintf(stderr, "ERROR: loading BPF object file failed\n");
goto cleanup;
} link = bpf_program__attach(prog);
if (libbpf_get_error(link)) {
fprintf(stderr, "ERROR: bpf_program__attach failed\n");
link = NULL;
goto cleanup;
} read_trace_pipe(); cleanup:
bpf_link__destroy(link);
bpf_object__close(obj);
return 0;
}

hello_kern.c

#include <uapi/linux/bpf.h>
#include <linux/version.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h> SEC("tracepoint/syscalls/sys_enter_execve")
int bpf_prog1(struct pt_regs *ctx)
{
char fmt[] = "Hello %s !\n";
char comm[16];
bpf_get_current_comm(&comm, sizeof(comm));
bpf_trace_printk(fmt, sizeof(fmt), comm); return 0;
} char _license[] SEC("license") = "GPL";
u32 _version SEC("version") = LINUX_VERSION_CODE;

Makefile 檔案修改

# diff -u Makefile.old Makefile
--- Makefile.old 2021-09-26 03:16:16.883348130 +0000
+++ Makefile 2021-09-26 03:20:46.732277872 +0000
@@ -55,6 +55,7 @@
tprogs-y += xdp_sample_pkts
tprogs-y += ibumad
tprogs-y += hbm
+tprogs-y += hello # Libbpf dependencies
LIBBPF = $(TOOLS_PATH)/lib/bpf/libbpf.a
@@ -113,6 +114,7 @@
xdp_sample_pkts-objs := xdp_sample_pkts_user.o
ibumad-objs := ibumad_user.o
hbm-objs := hbm.o $(CGROUP_HELPERS)
+hello-objs := hello_user.o $(TRACE_HELPERS) # Tell kbuild to always build the programs
always-y := $(tprogs-y)
@@ -174,6 +176,7 @@
always-y += hbm_out_kern.o
always-y += hbm_edt_kern.o
always-y += xdpsock_kern.o
+always-y += hello_kern.o ifeq ($(ARCH), arm)
# Strip all except -D__LINUX_ARM_ARCH__ option needed to handle linux

在當前目錄重新執行 make 命令即可。編譯完成後,啟動命令後在其他視窗執行 ls -hl ,顯示效果如下:

$ sudo ./hello
<...>-46054 [000] d... 7627.000940: bpf_trace_printk: Hello bash ! $ ldd hello
linux-vdso.so.1 (0x00007ffd71306000)
libelf.so.1 => /lib/x86_64-linux-gnu/libelf.so.1 (0x00007f99d67fd000)
libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f99d67e1000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f99d65f5000)
/lib64/ld-linux-x86-64.so.2 (0x00007f99d6cf1000)

5. 系統核心升級

5.1 Ubuntu 核心官方包升級

mainline 為 Ubuntu 提供視覺化的核心升級工具,早期版本為 ukuu 。該工具支援的特性如下:

  • 從 Ubuntu Mainline PPA 中獲取可用的核心列表
  • 當有新的核心更新時,可以選擇觀察並顯示通知
  • 自動下載和安裝軟體包
  • 方便地顯示可用和已安裝的核心
  • 從介面上安裝 / 解除安裝核心
  • 對於每個核心,相關的軟體包(標頭檔案和模組)會同時安裝或解除安裝

使用 Vagrant 快速搭建一個 VM 環境:

$ vagrant box add  ubuntu/hirsute64-ukuu ~/Downloads/ubuntu_21_04_bpf.box
$ mkdir ubuntu_21_04_ukuu && cd ubuntu_21_04_ukuu
$ vagrant init ubuntu/hirsute64-ukuu
$ vagrant up

可以通過以下命令自動安裝:

$ sudo add-apt-repository ppa:cappelikan/ppa
$ sudo apt update
$ sudo apt install mainline $ mainline -h
mainline 1.0.15
Distribution: Ubuntu 21.04
Architecture: amd64
Running kernel: 5.11.0-36-generic mainline 1.0.15 - Ubuntu Mainline Kernel Installer

list 子命令會顯示出來當前系統支援升級的或者已經安裝的系統:

$ mainline --list
mainline 1.0.15
Distribution: Ubuntu 21.04
Architecture: amd64
Running kernel: 5.11.0-36-generic
Fetching index from kernel.ubuntu.com...
OK Fetching index... ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ 100 % ----------------------------------------------------------------------
Found installed: 5.11.0.36.38
Found installed: 5.11.0-36.40
----------------------------------------------------------------------
---------------------------------------------------------------------- ======================================================================
Available Kernels
======================================================================
5.14.8
5.14.7 $ sudo mainline --install 5.14.7 # 則會啟動對應的核心安裝
mainline 1.0.15
Distribution: Ubuntu 21.04
Architecture: amd64
Running kernel: 5.11.0-36-generic
Fetching index from kernel.ubuntu.com...
OK Fetching index... ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ 100 % ----------------------------------------------------------------------
Found installed: 5.11.0.36.38
Found installed: 5.11.0-36.40
----------------------------------------------------------------------
---------------------------------------------------------------------- Downloading: 'amd64/linux-headers-5.14.7-051407-generic_5.14.7-051407.202109221210_amd64.deb'...
OK Downloading: 'amd64/linux-image-unsigned-5.14.7-051407-generic_5.14.7-051407.202109221210_amd64.deb'...
OK Downloading: 'amd64/linux-headers-5.14.7-051407_5.14.7-051407.202109221210_all.deb'...
OK Downloading: 'amd64/linux-modules-5.14.7-051407-generic_5.14.7-051407.202109221210_amd64.deb'...
OK
...

通過上述輸出我們也可以看出,和手工安裝的過程類似,同樣需要 4 個主要檔案,( 手動安裝可以到 http://kernel.ubuntu.com/~kernel-ppa/mainline/ 這裡下載 ):

  • linux-headers-5.14.7-051407-generic_5.14.7-051407.202109221210_amd64.deb(通用標頭檔案) 和 amd64/linux-headers-5.14.7-051407_5.14.7-051407.202109221210_all.deb(基礎標頭檔案)
  • amd64/linux-image-unsigned-5.14.7-051407-generic_5.14.7-051407.202109221210_amd64.deb
  • amd64/linux-modules-5.14.7-051407-generic_5.14.7-051407.202109221210_amd64.deb

在本次升級到 5.14.7 的過程中,modules 和 linux images 成功,但是安裝 linux-headers-5.14.7-051407-generic 標頭檔案的時候,遇到了以下的問題:

dpkg: dependency problems prevent configuration of linux-headers-5.14.7-051407-generic:
linux-headers-5.14.7-051407-generic depends on libc6 (>= 2.34); however:
Version of libc6:amd64 on system is 2.33-0ubuntu5. dpkg: error processing package linux-headers-5.14.7-051407-generic (--install):
dependency problems - leaving unconfigured
....
Errors were encountered while processing:
linux-headers-5.14.7-051407-generic
E: Installation completed with errors

錯誤提示的大意是我們安裝的 linux-headers-5.14.7-051407 需要 libc 的版本 >= 2.34,而我們系統的 libc 版本為 2.33。而 linux-headers 是我們 BPF 需要的環境 linux-headers,所以還需要手動修復安裝。

$ sudo reboot
...
$ uname -a
Linux ubuntu-hirsute 5.14.7-051407-generic #202109221210 SMP Wed Sep 22 15:15:48 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

系統重啟後已經證明我們成功升級了系統,這裡我們繼續手工修復 linux-headers 問題,首先嚐試使用手工安裝:

$ sudo apt install linux-headers-$(uname -r)
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
linux-headers-5.14.7-051407-generic is already the newest version (5.14.7-051407.202109221210).
You might want to run 'apt --fix-broken install' to correct these.
The following packages have unmet dependencies:
linux-headers-5.14.7-051407-generic : Depends: libc6 (>= 2.34) but 2.33-0ubuntu5 is to be installed
E: Unmet dependencies. Try 'apt --fix-broken install' with no packages (or specify a solution).

錯誤提示我們使用 apt --fix-broken install 來修訂錯誤,該命令其實做的事情就是刪除存在問題的標頭檔案包:

$ sudo apt --fix-broken install
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
Correcting dependencies... Done
The following packages will be REMOVED:
linux-headers-5.14.7-051407-generic
0 upgraded, 0 newly installed, 1 to remove and 4 not upgraded.
1 not fully installed or removed.
After this operation, 23.3 MB disk space will be freed.
Do you want to continue? [Y/n] y
(Reading database ... 162397 files and directories currently installed.)
Removing linux-headers-5.14.7-051407-generic (5.14.7-051407.202109221210) ... $ dpkg -l |grep -i linux-headers-5.14.7
ii linux-headers-5.14.7-051407 5.14.7-051407.202109221210 all Header files related to Linux kernel version 5.14.7

從網上搜索 libc-2.34 版本的安裝包,下載並安裝:

$ wget http://launchpadlibrarian.net/560614488/libc6_2.34-0ubuntu3_amd64.deb

# 因為本地已經安裝了 libc6_2.33,安裝會提示衝突,這裡使用 --force-all 引數
$ sudo dpkg -i --force-all libc6_2.34-0ubuntu3_amd64.deb $ dpkg -l|grep libc6
ii libc6:amd64 2.34-0ubuntu3 amd64 GNU C Library: Shared libraries

Ubunt 5.14.7 主線 下載安裝出錯的 linux-headers-5.14.7-051407-generic,然後手工進行安裝:

$sudo dpkg -i linux-headers-5.14.7-051407-generic_5.14.7-051407.202109221210_amd64.deb
Selecting previously unselected package linux-headers-5.14.7-051407-generic.
(Reading database ... 152977 files and directories currently installed.)
Preparing to unpack linux-headers-5.14.7-051407-generic_5.14.7-051407.202109221210_amd64.deb ...
Unpacking linux-headers-5.14.7-051407-generic (5.14.7-051407.202109221210) ...
Setting up linux-headers-5.14.7-051407-generic (5.14.7-051407.202109221210) ...

這種強制安裝的 pkg 的方式,會影響到其他依賴包的管理,一般不推薦。 這種情況一般是我們升級的核心版本比較新,其他的對應元件還未能完全更新。如果是升級到 Ubuntu 的官方新發布版本(比如從 20.04 升級到 21.04),建議通過官方提供的方式升級,核心和其他依賴包都可以一起升級,保證整體完整型,可參考 如何升級到 Ubuntu 20.04

5.2 核心原始碼編譯升級核心(進階版)

某些情況我們需要更新版本的核心,如果 Ubuntu 官方還未提供該版本的安裝包及核心原始碼,那麼我們就需要通過原始碼編譯和安裝的方式進行。本文從 Ubuntu 環境中編譯,其他的 Linux 發行版可以參考 核心原始碼編譯指南

5.2.1 原始碼下載及編譯工具安裝

基於 Ubuntu 21.04 的系統,我們從 核心網站 上選擇 5.14.7 版本進行編譯。

$ wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.14.7.tar.xz
$ xz -d linux-5.14.7.tar.xz
$ tar xvf linux-5.14.7.tar

安裝必要的核心編譯工具:

$ sudo apt install build-essential libncurses-dev bison flex libssl-dev libelf-dev -y

Linux BTF 功能需要 pahole 工具 (>= 1.16) ,該工具位於 dwarves 包中,需要提前進行安裝。

$ sudo apt install dwarves -y

5.2.2 原始碼編譯

編譯前需要將系統中執行的 config 檔案複製到核心編譯目錄中為 .config 檔案。

$ cd linux-5.14.7/
~/linux-5.14.7$ cp -v /boot/config-5.11.0-36-generic .config
'/boot/config-5.11.0-36-generic' -> '.config' $ make menuconfig

因為當前目錄已經存在 .config 檔案,make menuconfig 會直接使用 .config 作為預設值,通過圖形介面修改值以後,預設會將當前的 .config 備份成 .config.old,並生成新的 .config 檔案。

這裡如果採用 make oldconfig,那麼則是通過命令介面配置核心,會自動載入既有的 .config 配置檔案,並且只有在遇到先前沒有設定過的選項時,才會要求我們手動設定。同樣也會將老的 .config 備份成 .config.old 檔案。

執行 make 命令編譯, -j 引數可指定並行 CPU 核數:

$ make -j $(getconf _NPROCESSORS_ONLN)

如果編譯過程中遇到 No rule to make target 'debian/canonical-certs.pem' 的錯誤,可以通過禁用證書或者將執行系統中原始碼的相關證書複製到當前目錄下的 debian 目錄。

** 方案 1:複製系統中的證書 **

將當前執行系統原始碼中的 /usr/src/linux-source-5.11.0/debian/canonical-certs.pem 和 /usr/src/linux-source-5.11.0/debian/canonical-revoked-certs.pem 複製到當前 debian 目錄中:

$ mkdir debian
$ cp /usr/src/linux-source-5.11.0/debian/canonical-certs.pem ./debain/
$ cp /usr/src/linux-source-5.11.0/debian/canonical-revoked-certs.pem ./debain/

** 方案 2:採用禁用 SYSTEM_TRUSTED_KEYS 的方式 **

$ scripts/config --disable SYSTEM_TRUSTED_KEYS  # 關閉禁止證書,修訂 No rule to make target 'debian/canonical-certs.pem'

待證書設定以後,就可以執行 make 命令進行編譯。

編譯的時長使用的 CPU 核數相關,編譯大概佔用 20G 左右的產品空間。

5.2.3 核心安裝和啟動選擇

編譯成功後,使用以下命令安裝核心:

$ sudo make modules_install && make install && make headers_install
$ sudo reboot
[....]
$ uname -a
Linux ubuntu-hirsute 5.14.7 #1 SMP Sun Sep 26 11:32:44 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

reboot 重啟後,我們使用使用 uname 檢視,發現系統核心已經更新為了 5.14.7 版本。

Ubuntu 21.04 採用 GRUB2 來管理啟動選單,對應的檔案為 /boot/grub/grub.cfg ,該檔案為 update-grub 命令依據 /etc/default/grub 檔案自動生成。如果需要調整選單選項,這需要通過 /etc/default/grub 檔案修改,然後通過執行 update-grub 命令生效。

/etc/default/grub 檔案中的:

GRUB_DEFAULT=0
GRUB_TIMEOUT_STYLE=hidden
GRUB_TIMEOUT=0

GRUB_DEFAULT 指定了預設啟動的選單選項,0 表示第一個選單選項。

GRUB_TIMEOUT 選項表明了啟動選單顯示的時間,方便用於使用者選擇,預設為 0,在除錯新的核心時建議設定成一個非 0 值,同時需要將 GRUB_TIMEOUT_STYLE 的值從 hidden 調整為 menu:

GRUB_TIMEOUT_STYLE=menu
GRUB_TIMEOUT=5

然後通過 update-grub 命令更新生效,再重新啟動就可以看到啟動選單。其他引數詳細配置可參見 Grub 配置

todo: 在 VirtualBox 中能看到停頓,但是未能夠看到啟動介面

5.2.4 其他 - 編譯核心以後磁碟管理

Virtualbox 底層使用 vmdk 的磁碟格式,當前會存在只是單向增大不會自動收縮的問題(即使我們在編譯核心後,刪除了編譯相關的問題),請參見 VirtualBox VM 空間瘦身記(vmdk)

6. 參考



  1. Ubuntu 21.04 is here