基於 Ubuntu 21.04 BPF 開發環境全攻略


本文地址: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 ↩︎


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM