Vmware虚拟机安装Ubuntu20.4系统及QEMU模拟ARM64 Linux
学习“奔跑吧Linux内核”课件及资料,参照(根据自己电脑实际情况、而非一五一十完全照搬)实验指导书中的第1章中的实验1-1到1-6的内容,进行学习理解和模仿实践。
在虚拟机中安装 Ubuntu Linux 20.04系统
安装aarch64交叉编译工具
搭建QEMU的模拟环境首先需要下载安装对应架构的交叉编译工具链(这里是arm64架构):
sudo apt-get install gcc-aarch64-linux-gnu
sudo apt-get install libncurses5-dev build-essential git bison flex libssl-dev
安装完成之后查看版本说明安装完成:
aarch64-linux-gnu-gcc -v
安装QEMU
在终端中输入 sudo apt install qemu-system-arm
安装
安装完成后,输入 qemu-system-aarch64 --version
来查看 qemu版本
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.36.1
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
//检测是否配置成功
echo $ ARCH
echo $CROSS_COMPILE
3)配置编译BusyBox
busybox中文字符支持:若直接编译busybox,使用串口工具时是不支持中文显示的,会显示为“?” ,可修改源码,取消 busybox对中文显示的限制
打开文件/libbb/printable_string.c,将函数printable_string()中的部分程序注释掉,修改后的函数内容如下:
/********** printable_string.c代码段 **********/
const char* FAST_FUNC printable_string(uni_stat_t *stats, const char *str)
{
char *dst;
const char *s;
s = str;
while (1) {
......
if (c < ' ')
break;
/* 注释掉下面这个两行代码,禁止字符大于0X7F以后 break */
/* if (c >= 0x7f)
break; */
s++;
}
#if ENABLE_UNICODE_SUPPORT
dst = unicode_conv_to_printable(stats, str);
#else
{
char *d = dst = xstrdup(str);
while (1) {
unsigned char c = *d;
if (c == ' ')
break;
/* 修改下面代码,禁止字符大于0X7F以后输出‘?’ */
/* if (c < ' ' || c >= 0x7f) */
if( c < ' ')
*d = '?';
d++;
}
......
#endif
return auto_string(dst);
}
接着打开文件/libbb/unicode.c,修改如下内容:
/********** unicode.c代码段 **********/
static char* FAST_FUNC unicode_conv_to_printable2(uni_stat_t *stats, const char *src, unsigned width, int flags)
{
char *dst;
unsigned dst_len;
unsigned uni_count;
unsigned uni_width;
if (unicode_status != UNICODE_ON) {
char *d;
if (flags & UNI_FLAG_PAD) {
d = dst = xmalloc(width + 1);
......
/* 修改下面一行代码 */
/* *d++ = (c >= ' ' && c < 0x7f) ? c : '?'; */
*d++ = (c >= ' ') ? c : '?';
src++;
}
*d = ' ';
} else {
d = dst = xstrndup(src, width);
while (*d) {
unsigned char c = *d;
/* 修改下面一行代码 */
/* if (c < ' ' || c >= 0x7f) */
if(c < ' ')
*d = '?';
d++;
}
}
......
return dst;
}
......
return dst;
}
配置busybox:
make defconfig #使用默认配置
make menuconfig #打开图形化配置界面
设置Settings -> Build static binary (no shared libs)
设置Settings -> Support Unicode,使能busybox的unicode编码以支持中文
- 编译busybox:配置好busybox以后就可以编译了,输入如下命令
make
make install CONFIG_PREFIX=/home/xlq/linux/rootfs
#CONFIG_PREFIX指定编译结果的存放目录
4)创建需要的目录
cd ~/linux/rootfs
mkdir dev etc lib sys proc tmp var home root mnt
(a) etc目录更新
- 创建 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
- 创建 inittab 文件,添加下面内容
::sysinit:/etc/init.d/rcS
::respawn:-/bin/sh
::askfirst:-/bin/sh
::ctrlaltdel:/bin/umount -a -r
- 创建 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
- 创建init.d目录
mkdir init.d
- 在init.d下创建 rcS文件,添加下面内容
cd init.d
mkdir -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
- 添加权限
chmod 777 rcS
- 利用tree命令查看etc下目录结构如下所示
(b) dev目录下的必要文件
cd dev
sudo mknod console c 5 1
输出下面命令检查是否创建了console的设备文件:
ls -l /dev/console
lib目录下的必要文件
为了支持动态编译的应用程序的执行,根文件系统需要支持动态库,所以我们添加arm64相关的动态库文件到lib下
cd lib
cp /usr/aarch64-linux-gnu/lib/*.so* -a .
检查当前目录是否包含了.so文件:
四、编译内核源码
1、下载源码
The Linux Kernel Archives
同BusyBox放到Ubuntu合适位置进行解压
tar xvf linux-6.13.5.tar.xz
2、指定编译工具
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
3、将根文件系统放到源码根目录
cd linux-6.13.5
sudo cp ~/linux/rootfs rootfs_arm64 -a
4、配置生成.config
make defconfig
make menuconfig
5、编译
make all -j8
五、启动QEMU
1、创建共享文件目录
在内核源码目录下创建目录
mkdir kmodules
2、运行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
4、编译一个简单的内核模块并在QEMU上运行
-
在根目录下创建一个文件夹module_test,并编写一个简单的hello.c代码
// 包含内核模块编程所需的头文件
#include // 包含模块初始化和退出函数的宏
#include // 包含模块相关的宏和函数
#include // 包含内核打印函数 printk 的头文件
// 模块初始化函数
// 当模块被加载到内核时,此函数会被调用
static int __init test_init(void)
{
// 在内核日志中打印 "hello world!"
printk("hello world!
");
// 返回 0 表示初始化成功
return 0;
}
// 模块退出函数
// 当模块从内核中卸载时,此函数会被调用
static void __exit test_exit(void)
{
// 在内核日志中打印 "hello exit!"
printk("hello exit!
");
}
// 注册模块的初始化函数
// 当模块被加载时,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/xlq/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_mount
- 在QEUM执行module的插入与卸载,可以看到成功执行并打印log.
六、实验过程中遇到的一些问题及解决办法
启动qemu会报错qemu-system-aarch64: rom check and register reset failed
解决方法:是qemu版本的问题,换个新版本进行测试.