Linux内核学习(一)——Vmware虚拟机安装Ubuntu20.4系统及QEMU模拟ARM64 Linux
Linux内核学习(一)——Vmware虚拟机安装Ubuntu20.4系统及QEMU模拟ARM64 Linux
小编有话说:
在实验进行前,建议大家提前去学习快照功能,具体原因如下:
1.因为我们在虚拟机上面完成实验的时候,经常会因为马虎或者资料寻找错误导致整个实验报废,如果不使用快照,很有可能会导致之前那的努力白费;
2.如果想深入学习命令行,可以启动同样的快照,在不同的命令行操作下编译,更能深刻理解其中的含义。
学习博客:https://zhuanlan.zhihu.com/p/196701930 , https://www.cnblogs.com/liuyuelinfighting/p/15484974.html
目录标题
- Linux内核学习(一)——Vmware虚拟机安装Ubuntu20.4系统及QEMU模拟ARM64 Linux
- 一.实验目的
- 二.实验
- (一)实验1.1**Ubuntu Linux 20.04系统的安装**
- 实验目的
- 实验步骤
- (二)实验 1-2:给 Ubuntu Linux 系统更换心脏(内核升级)
- 实验目的
- 实验步骤
- 1. 安装依赖包
- 启用 deb-src 仓库
- 更新软件包列表
- 验证操作
- 2. 下载最新内核源码
- 3. 解压内核源码
- 4. 配置内核选项
- 5. 编译内核
- 安装缺失的依赖库
- 清理编译环境并重新编译
- 6. 安装内核模块
- 7. 安装内核
- 8. 更新引导加载程序(GRUB)
- 9. 重启并验证
- (三)实验 1-3:使用 QEMU 虚拟机运行 ARM64 Linux 系统并编译内核模块
- 实验目的
- 实验步骤
- 1.安装 aarch64 交叉编译工具:
- 2.安装QEMU
- 3.启动QEMU
- 创建共享文件目录
- 安装预编译内核
- 确定内核位置
- 运行QEMU模拟器
- 退出QEMU模拟器
- 4.编译一个简单的内核模块并在QEMU上运行
- (四)实验1-4:创建基于Ubuntu的根文件系统
- 实验目的
- 实验步骤
- 1.BusyBox构建根文件系统
- 2.制定编译工具链
- 3.配置编译BusyBox
- 4.创建需要的目录
- 5.复制动态库文件到 `lib` 目录
- (五)实验 1-6:编译和运行一个 ARM64 Linux 最小系统
- 实验目的
- 实验步骤
- 1. 编译内核源码
- 2. 启动 QEMU
- 3. 编译一个简单的内核模块并在 QEMU 上运行
- 三.总结
一.实验目的
- 掌握 VMware 虚拟机软件的基本使用方法。
- 学习在虚拟机中安装 Ubuntu 20.04 Desktop 系统。
- 熟悉 Linux 系统的初始配置及快照管理。
二.实验
(一)实验1.1Ubuntu Linux 20.04系统的安装
实验目的
Ubuntu Linux 20.04系统的安装
实验步骤
-
创建虚拟机
- 打开 VMware,选择
创建新的虚拟机
→典型配置
→ 选择 ISO 文件路径。 - 设置用户名和密码(例如:
user/123456
)。 - 指定虚拟机名称和存储位置(如
D:\VM\Ubuntu20.04
)。 - 分配硬件资源:4 核 CPU、4GB 内存、50GB 硬盘。
- 打开 VMware,选择
-
安装 Ubuntu 系统
-
启动虚拟机,进入 Ubuntu 安装界面:
-
安装完成后重启系统。
学习链接:https://blog.csdn.net/Z_Date/article/details/137855618?utm_source=miniapp_weixin
-
(二)实验 1-2:给 Ubuntu Linux 系统更换心脏(内核升级)
实验目的
- 通过本实验学会如何给Linux系统更换最新版本的Linux内核。
- 如何编译和安装Linux内核
实验步骤
以下是给Ubuntu Linux系统更换最新内核的具体操作步骤:
1. 安装依赖包
sudo apt update
sudo apt install -y libncurses5-dev libssl-dev build-essential openssl flex bison dwarves
# 或者使用以下命令安装所有编译依赖
sudo apt build-dep -y linux-image-generic
如果遇到这种情况,完整操作如下:
解决方案
出现 E: 必须在 sources.list 中指定代码源 (deb-src) URI
错误时,说明系统缺少源代码仓库的配置。以下是分步解决方法:
启用 deb-src 仓库
-
备份当前的源列表文件(可选):
sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak
-
编辑
sources.list
文件:sudo nano /etc/apt/sources.list # 使用 nano 编辑器,也可替换为 vim 或 gedit
-
添加或取消注释 deb-src 行:
- 找到已有的
deb
行(例如):deb http://archive.ubuntu.com/ubuntu focal main restricted
- 在下方添加对应的
deb-src
行:deb-src http://archive.ubuntu.com/ubuntu focal main restricted
- 如果已有
deb-src
行但被注释(以#
开头),删除#
取消注释。
- 找到已有的
-
保存并退出编辑器:
- nano:按
Ctrl + O
保存,Ctrl + X
退出。 - vim:按
:wq
保存并退出。
- nano:按
更新软件包列表
sudo apt update
验证操作
重新执行之前出错的命令( sudo apt build-dep linux-image-generic
),检查是否仍然报错。
2. 下载最新内核源码
- 访问内核官网或清华镜像站查找最新稳定版(例如
5.10.15
)。 - 在此之前一定自己要创建文件夹!!!!(否则后面会因为找不到文件使命令失效)
- 完整操作如下:
cd ~/kernel # 进入自定义目录(需提前创建:mkdir ~/kernel)
wget https://mirrors.tuna.tsinghua.edu.cn/kernel/v5.x/linux-5.10.15.tar.xz
3. 解压内核源码
tar -xvf linux-5.10.15.tar.xz
cd linux-5.10.15
4. 配置内核选项
- 方法1(推荐,基于当前系统配置):
- 建议操作之前先查看自己下载的内核版本
cp /boot/config-$(uname -r) .config
make olddefconfig
- 方法2(默认设置):
make defconfig
如果出错,那基本原因就是前面指定代码源那一步出错了,建议删除之前下载的镜像,重新从建立文件夹操作(在没弄清楚命令行具体含义和作用之前,不建议直接在现有基础上改,会越改越乱!)
5. 编译内核
- 使用多线程编译(根据CPU核心数调整
-j8
,例如8线程):
make -j$(nproc) # 或者手动指定:make -j8
- 此过程可能耗时较长(30分钟至数小时),确保系统电源稳定。
如果遇到这,按下列提示操作:
解决方法
安装缺失的依赖库
执行以下命令安装 libelf
开发包:
sudo apt-get install libelf-dev elfutils
(如果系统是CentOS/RHEL,则使用:sudo yum install elfutils-libelf-devel
)
清理编译环境并重新编译
安装依赖后,重新执行完整编译流程:
make clean # 清理旧编译文件
make -j$(nproc) # 重新编译内核,确保无报错
6. 安装内核模块
sudo make modules_install
7. 安装内核
sudo make install
8. 更新引导加载程序(GRUB)
sudo update-initramfs -c -k 5.10.15 # 替换为实际内核版本号
sudo update-grub
9. 重启并验证
sudo reboot
- 重启后执行以下命令检查内核版本:
uname -r
输出应为新安装的内核版本(如5.10.15
)。
注意事项
这里有一些DeepSeek给出的常见错误,可以借鉴:(建议大家看它给出的解决步骤之前,先看它的思考分析过程,因为有时候我们表述不清,AI生成的内容会有出错)
通过以上步骤即可完成Ubuntu Linux内核的编译和更换。
(三)实验 1-3:使用 QEMU 虚拟机运行 ARM64 Linux 系统并编译内核模块
实验目的
- 学习如何编译 ARM64 版本的 Linux 内核镜像。
- 掌握在 QEMU 虚拟机上运行 ARM64 Linux 系统的流程。
- 实践编译并加载最简单的内核模块
hello_world
,验证模块功能。
实验步骤
1.安装 aarch64 交叉编译工具:
sudo apt-get update
sudo apt-get install gcc-aarch64-linux-gnu
sudo apt-get install libncurses5-dev build-essential git bison flex libssl-dev
2.安装QEMU
在终端中输入 sudo apt install qemu-system-arm安装
sudo apt install qemu-system-arm
安装完成后,输入 qemu-system-aarch64 --version 来查看 qemu版本
qemu-system-aarch64 --version
3.启动QEMU
创建共享文件目录
mkdir kmodules
安装预编译内核
sudo apt install linux-image-generic
确定内核位置
ls -l /boot
在我的 /boot 目录下,内核文件的名称是 vmlinuz-5.4.0-208-generic。那么我可以尝试使用这个文件来启动 QEMU。
注意:
若没有权限,可以调整:
sudo chmod +r /boot/vmlinuz-5.4.0-208-generic
若无挂载路径,可以创建该目录:
mkdir -p /home/xm/kmodules
运行QEMU模拟器
在内核源码目录下执行下面命令(一次性输入)
qemu-system-aarch64 -machine virt -cpu cortex-a57 -machine type=virt -smp 4 -kernel /boot/vmlinuz-5.4.0-208-generic -append "console=ttyAMA0 loglevel=8 root=/dev/vda rw" -nographic -d int,cpu_reset -drive file=/home/xm/disk.img,format=qcow2,if=virtio -fsdev local,id=km_dev,path=/home/xm/kmodules,security_model=none -device virtio-9p-device,fsdev=km_dev,mount_tag=kmount
如果出现错误,说明没有创建虚拟磁盘文件:
qemu-img create -f qcow2 /home/xm/disk.img 10G
退出QEMU模拟器
poweroff
4.编译一个简单的内核模块并在QEMU上运行
在根目录下创建一个文件夹module_test,并编写一个简单的hello.c代码
// 包含内核模块编程所需的头文件
#include <linux/init.h> // 包含模块初始化和退出函数的宏
#include <linux/module.h> // 包含模块相关的宏和函数
#include <linux/kernel.h> // 包含内核打印函数 printk 的头文件// 模块初始化函数
// 当模块被加载到内核时,此函数会被调用
static int __init test_init(void)
{// 在内核日志中打印 "hello world!"printk("hello world!\n");// 返回 0 表示初始化成功return 0;
} // 模块退出函数
// 当模块从内核中卸载时,此函数会被调用
static void __exit test_exit(void)
{// 在内核日志中打印 "hello exit!"printk("hello exit!\n");
}// 注册模块的初始化函数
// 当模块被加载时,test_init 函数会被调用
module_init(test_init);// 注册模块的退出函数
// 当模块被卸载时,test_exit 函数会被调用
module_exit(test_exit);// 声明模块的许可证
// GPL 是 GNU 通用公共许可证,表示这是一个开源模块
MODULE_LICENSE("GPL");
再编写Makefile文件
# 设置目标架构为 ARM64
export ARCH=arm64# 设置交叉编译工具链前缀为 aarch64-linux-gnu-
export CROSS_COMPILE=aarch64-linux-gnu-# 定义内核源码目录
KERNEL_DIR ?= /home/xm/linux-6.13.5# 定义要编译的内核模块目标文件
# obj-m 表示将 hello.c 编译为内核模块 hello.ko
obj-m := hello.o# 默认目标:编译内核模块
modules:# 调用内核源码目录的 Makefile,编译当前目录下的模块$(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules# 清理目标:删除编译生成的文件
clean:# 调用内核源码目录的 Makefile,清理当前目录下的生成文件$(MAKE) -C $(KERNEL_DIR) M=$(PWD) clean# 安装目标:将编译好的内核模块复制到指定目录
install:# 将当前目录下所有的 .ko 文件复制到内核源码目录的 kmodules 子目录中cp *.ko $(KERNEL_DIR)/kmodules
编译module,拷贝到共享目录
make modules
make install
启动QEMU
qemu-system-aarch64
-machine virt
-cpu cortex-a57
-machine type=virt
-m 1024
-smp 4
-kernel arch/arm64/boot/Image
--append "rdinit=/linuxrc root=/dev/vda rw console=ttyAMA0 loglevel=8"
-nographic
--fsdev local,id=kmod_dev,path=$PWD/kmodules,security_model=none
-device virtio-9p-device,fsdev=kmod_dev,mount_tag=kmod_mountls /mnt
(四)实验1-4:创建基于Ubuntu的根文件系统
实验目的
创建关于Ubuntu linux的根文件系统
实验步骤
1.BusyBox构建根文件系统
1)下载BusyBox源码
BusyBox是一个集成了大量的Linux命令(如ls、mv、ifconfig 等命令)和工具的软件。借助BusyBox,进行配置和编译,就可以方便的构建一个嵌入Linux平台所需要的根文件系统。
可在BusyBox官网 https://busybox.net/ 下载源码。
将压缩包拖拽到Ubuntu虚拟机中的桌面或文件夹中,并解压
tar -vxjf busybox-1.37.0.tar.bz2
2.制定编译工具链
cd busybox-1.37.0
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
//检测是否配置成功
echo $ ARCH
echo $CROSS_COMPILE
3.配置编译BusyBox
修改源码以支持中文显示(可选):
打开文件 /libbb/printable_string.c,注释掉 printable_string 函数中 if (c >= 0x7f) break; 的代码行。
打开文件 /libbb/unicode.c,修改 unicode_conv_to_printable2 函数中的相关代码。
安装交叉编译工具链
你需要安装适用于 ARM64 架构的交叉编译工具链。在 Ubuntu 或 Debian 系统上,可以运行以下命令:
sudo apt update
sudo apt install gcc-aarch64-linux-gnu
配置busybox:有以下几种配置选项,一般使用默认配置即可
make defconfig #使用默认配置
make menuconfig #打开图形化配置界面
如果出现这个错误,把终端窗口放大就行。
** 设置Settings -> Build static binary (no shared libs)
设置Settings -> Support Unicode,使能busybox的unicode编码以支持中文**
编译busybox:配置好busybox以后就可以编译了,输入如下命令
sudo apt install make
make
make install CONFIG_PREFIX=/home/xm/linux/rootfs
#CONFIG_PREFIX指定编译结果的存放目录
如果出现这样的错误,操作如下:(我们下载的很多压缩包都是有bug或者和我们的操作版本不兼容的,出现这种本身自带的文件编译错误,可以直接找到位置更改)
编译完成以后,busybox的所有工具和文件就会被安装到rootfs目录中,如下图;rootfs目录下有bin、sbin和usr三个目录,以及linuxrc文件。
4.创建需要的目录
cd ~/linux/rootfs
mkdir dev etc lib sys proc tmp var home root mnt
创建 profile
文件并添加内容
cd /etcsudo nano /etc/profile
将以下内容复制粘贴到 profile
文件中:
#!/bin/sh
export HOSTNAME=user
export USER=root
export HOME=/home
export PS1="[$USER@$HOSTNAME \W]\# "
PATH=/bin:/sbin:/usr/bin:/usr/sbin
LD_LIBRARY_PATH=/lib:/usr/lib:$LD_LIBRARY_PATH
export PATH LD_LIBRARY_PATH
保存并退出:
- 按下
Ctrl + O
保存文件。 - 按下
Enter
确认文件名。 - 按下
Ctrl + X
退出nano
。
创建 inittab
文件并添加内容
cd /etc
sudo nano inittab
将以下内容复制粘贴到 inittab
文件中:
::sysinit:/etc/init.d/rcS
::respawn:-/bin/sh
::askfirst:-/bin/sh
::ctrlaltdel:/bin/umount -a -r
保存并退出:
- 按下
Ctrl + O
保存文件。 - 按下
Enter
确认文件名。 - 按下
Ctrl + X
退出nano
。
创建 fstab
文件并添加内容
cd /etc
sudo nano fstab
将以下内容复制粘贴到 fstab
文件中:
#device mount-point type options dump fsck order
proc /proc proc defaults 0 0
tmpfs /tmp tmpfs defaults 0 0
sysfs /sys sysfs defaults 0 0
tmpfs /dev tmpfs defaults 0 0
debugfs /sys/kernel/debug debugfs defaults 0 0
kmod_mount /mnt 9p trans=virtio 0 0
保存并退出:
- 按下
Ctrl + O
保存文件。 - 按下
Enter
确认文件名。 - 按下
Ctrl + X
退出nano
。
创建 init.d
目录并在其中创建 rcS
文件
cd /etc
mkdir init.d
cd init.d
sudo nano rcS
将以下内容复制粘贴到 rcS
文件中:
cd init.dmkdir -p /sys
mkdir -p /tmp
mkdir -p /proc
mkdir -p /mnt
/bin/mount -a
mkdir -p /dev/pts
mount -t devpts devpts /dev/pts
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s
保存并退出:
- 按下
Ctrl + O
保存文件。 - 按下
Enter
确认文件名。 - 按下
Ctrl + X
退出nano
。
添加权限
sudo chmod 777 rcS
检查 etc
目录结构
sudo apt update
sudo apt install tree
sudo tree /etc
创建 dev
目录下的必要文件
cd /dev
sudo mknod console c 5 1
检查是否创建成功:
ls -l /dev/console
5.复制动态库文件到 lib
目录
cd /lib
sudo cp /usr/aarch64-linux-gnu/lib/*.so* -a .
检查当前目录是否包含了 .so
文件:
ls -l *.so*
(五)实验 1-6:编译和运行一个 ARM64 Linux 最小系统
实验目的
本实验旨在指导你如何编译一个 ARM64 架构的 Linux 内核,并使用 QEMU 虚拟机运行该内核,同时加载一个简单的内核模块进行测试。
实验步骤
可以继承实验1-4的快照
1. 编译内核源码
下载和解压源码
- 访问 Linux 内核官网(https://www.kernel.org/),找到你需要的内核版本(如 linux-6.13.6.tar.xz)。
- 使用
wget
命令下载源码:wget https://www.kernel.org/pub/linux/kernel/v6.x/linux-6.13.6.tar.xz
- 解压源码:
tar xvf linux-6.13.6.tar.xz
指定编译工具
- 设置架构为 arm64,并指定交叉编译工具链前缀为 aarch64-linux-gnu-:
export ARCH=arm64 export CROSS_COMPILE=aarch64-linux-gnu-
将根文件系统放到源码根目录
- 使用
sudo cp
命令将构建好的根文件系统复制到内核源码根目录下:sudo cp ~/linux/rootfs rootfs_arm64 -a
配置生成.config
- 运行
make defconfig
生成默认配置文件:make defconfig
- 运行
make menuconfig
打开图形化配置界面,进行以下配置:- 添加 hotplug 支持:
Device Drivers-> Generic Driver Options-> Support for uevent helper(/sbin/hotplug) path to uevent helper
- 添加 initramfs 支持:
General setup --->[*]Initial RAM filesystem and RAM disk(initramfs/initrd) support(_install_arm64) Initramfs souce file(s)
- 配置 Virtual address space:
Kernel Features --->Page size(4KB) --->Virtual address space size(48-bit)--->
- 添加 hotplug 支持:
编译
- 运行
make all -j8
进行编译,-j8
表示使用 8 个线程进行并行编译:make all -j8
2. 启动 QEMU
- 运行以下命令启动 QEMU 虚拟机:
qemu-system-aarch64 -machine virt -cpu cortex-a57 -machine type=virt -m 1024 -smp 4 -kernel arch/arm64/boot/Image --append "rdinit=/linuxrc root=/dev/vda rw console=ttyAMA0 loglevel=8" -nographic --fsdev local,id=kmod_dev,path=$PWD/kmodules,security_model=none -device virtio-9p-device,fsdev=kmod_dev,mount_tag=kmod_mount
3. 编译一个简单的内核模块并在 QEMU 上运行
创建 module_test
文件夹并编写 hello.c
和 Makefile
文件
- 创建
module_test
文件夹:mkdir module_test cd module_test
- 编写
hello.c
文件,内容如下:// 包含内核模块编程所需的头文件 #include <linux/init.h> // 包含模块初始化和退出函数的宏 #include <linux/module.h> // 包含模块相关的宏和函数 #include <linux/kernel.h> // 包含内核打印函数 printk 的头文件// 模块初始化函数 // 当模块被加载到内核时,此函数会被调用 static int __init test_init(void) {// 在内核日志中打印 "hello world!"printk("hello world!\n");// 返回 0 表示初始化成功return 0; } // 模块退出函数 // 当模块从内核中卸载时,此函数会被调用 static void __exit test_exit(void) {// 在内核日志中打印 "hello exit!"printk("hello exit!\n"); }// 注册模块的初始化函数 // 当模块被加载时,test_init 函数会被调用 module_init(test_init);// 注册模块的退出函数 // 当模块被卸载时,test_exit 函数会被调用 module_exit(test_exit);// 声明模块的许可证 // GPL 是 GNU 通用公共许可证,表示这是一个开源模块 MODULE_LICENSE("GPL");
- 编写
Makefile
文件,内容如下:# 设置目标架构为 ARM64 export ARCH=arm64# 设置交叉编译工具链前缀为 aarch64-linux-gnu- export CROSS_COMPILE=aarch64-linux-gnu-# 定义内核源码目录 KERNEL_DIR ?= /home/xm/linux-6.13.6# 定义要编译的内核模块目标文件 # obj-m 表示将 hello.c 编译为内核模块 hello.ko obj-m := hello.o# 默认目标:编译内核模块 modules:# 调用内核源码目录的 Makefile,编译当前目录下的模块$(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules# 清理目标:删除编译生成的文件 clean:# 调用内核源码目录的 Makefile,清理当前目录下的生成文件$(MAKE) -C $(KERNEL_DIR) M=$(PWD) clean# 安装目标:将编译好的内核模块复制到指定目录 install:# 将当前目录下所有的 .ko 文件复制到内核源码目录的 kmodules 子目录中cp *.ko $(KERNEL_DIR)/kmodules
编译模块并拷贝到共享目录
- 运行
make modules
编译模块:make modules
- 运行
make install
将编译好的模块拷贝到内核源码目录的kmodules
子目录中:make install
启动 QEMU 并在 QEMU 中执行模块的插入与卸载
- 启动 QEMU 虚拟机(同上一步骤)。
- 在 QEMU 中执行以下命令插入模块:
insmod hello.ko
- 查看内核日志,确认模块加载成功:
dmesg | tail
- 卸载模块:
rmmod hello.ko
- 再次查看内核日志,确认模块卸载成功:
dmesg | tail
三.总结
本次实验学习“奔跑吧Linux内核”课件及资料,参照实验指导书中的第1章中的实验1-1到1-6的内容,进行学习理解和模仿实践。在实验过程中经历了很多次失败,感悟很多,推荐大家利用deepseek,kimi等大模型来处理报错,在这个过程之中,除了copy大模型给出的解决方法,也要重点学习一下它的思路(如下图),否则下一次还是可能会经历同样的错误。