Linux学习——使用QEMU搭建ARM64环境
Linux学习——使用QEMU搭建ARM64环境
文章目录
- Linux学习——使用QEMU搭建ARM64环境
- 一、了解QEMU
- 二、安装QEMU虚拟机
- 1、安装 QEMU虚拟机
- 2、创建虚拟硬盘
- 3、效果展示
- 三、安装交叉编译环境
- 四、了解根文件系统
- 五、创建基于 Ubuntu Linux 的根文件系统
- 1、下载busybox1.36.0:
- 2、制定编译工具链
- 3、配置BusyBox
- 4、编译busybox
- 5、创建需要的目录
- 六、下载内核kernel
- 1、下载内核kernel压缩包
- 2、指定编译工具
- 3、将根文件系统放到源码根目录
- 4、配置生成.config
- 5、配置支持
- 6、编译
- 七、启动QEMU虚拟机
- 八、QEMU虚拟机的其他使用方法
- 九、编译一个简单的内核模块并在QEMU上运行
- 1、创建文件
- 2、在编写Makefile文件
- 3、编译
- 4、启动QEMU
- 5、结果
- 出现问题
一、了解QEMU
两种工作模式:全系统模拟系统
用户模式
本质工作:二进制翻译
设备的基本模式:backend+device
硬件加速支持:KVM
二、安装QEMU虚拟机
1、安装 QEMU虚拟机
打开虚拟机的命令行,并输入一下代码
sudo apt update
sudo apt install qemu-utils //用于网络桥接的工具
qemu-img --version //确认是否安装成功
sudo apt install qemu qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils virt-manager
//qemu-kvm 是QEMU和KVM之间的集成模块
//libvirt-daemon-system libvirt-clients管理虚拟化平台的开源API。守护进程和工具集合
//bridge-utils提供了用于网络桥接的工具
//Virtual Machine Manager (Virt-Manager) 是一个图形界面应用程序,用于管理虚拟机
2、创建虚拟硬盘
qemu-img create -f qcow2 ubuntu.qcow2 20G //创建虚拟硬盘
ls -lh unbuntu.qcow2 //检查创建是否成功
3、效果展示
可在虚拟机的home文件中找到ubuntu.qcow2文件
三、安装交叉编译环境
sudo apt-get install gcc-aarch64-linux-gnu //下载交叉编译工具
sudo apt-get install libncurses5-dev build-essential git bison flex libssl-dev
sudo apt install qemu-system-arm
四、了解根文件系统
Linux的根文件系统一般也叫做 rootfs,Linux的根文件系统更像是一个文件夹或者叫做目录,在这个目录里面会有很多的子目录。根目录下和子目录中会有很多的文件,这些文件是Linux运行所必须的,比如库、常用的软件和命令、设备文件、配置文件等等。
根文件系统的“根”字就说明了这个文件系统的重要性,它是其他文件系统的根,没有这个“根” ,其他的文件系统或者软件就别想工作。比如我们常用的 ls、mv、ifconfig 等命令其实就是一个个小软件,只是这些软件没有图形界面,而且需要输入命令来运行。这些小软件就保存在根文件系统中。
在构建根文件系统之前,先来看一下根文件系统里面都有些什么内容,根文件系统的目录名字为‘/’ ,就是一个斜杠。下面以Ubuntu为例,一些常用的子目录介绍如下表示:
五、创建基于 Ubuntu Linux 的根文件系统
1、下载busybox1.36.0:
wget http://www.busybox.net/downloads/busybox-1.36.0.tar.bz2 //下载
tar -vxjf busybox-1.36.0.tar.bz2 //解压
ls ~/busybox-1.36.0//查看解压文件
2、制定编译工具链
cd busybox-1.36.0
export ARCH=arm64
//设置ARCH环境变量为arm64
//这个变量告诉编译器我们正在为目标架构arm64(即64位ARM架构)进行编译
export CROSS_COMPILE=aarch64-linux-gnu-
//设置CROSS_COMPILE环境变量为aarch64-linux-gnu-
//这个变量指定了交叉编译工具链的前缀。在这里,我们使用的是针对arm64架构的GNU交叉编译工具链
echo $ARCH //检测ARCH环境变量是否配置成功,输出"arm64"
echo $CROSS_COMPILE //检测CROSS_COMPILE环境变量是否配置成功,输出"aarch64-linux-gnu-"
3、配置BusyBox
busybox中文字符支持:若直接编译busybox,使用串口工具时是不支持中文显示的,会显示为“?” ,可修改源码,取消 busybox对中文显示的限制
打开文件/libbb/printable_string.c,将函数printable_string()中的部分程序注释掉,修改后的函数内容如下:
/* vi: set sw=4 ts=4: */
/*
* Unicode support routines.
*
* Copyright (C) 2010 Denys Vlasenko
*
* Licensed under GPLv2, see file LICENSE in this source tree.
*/
#include "libbb.h"
#include "unicode.h"
const char* FAST_FUNC printable_string2(uni_stat_t *stats, const char *str)
{
char *dst;
const char *s;
s = str;
while (1) {
unsigned char c = *s;
if (c == '\0') {
/* 99+% of inputs do not need conversion */
if (stats) {
stats->byte_count = (s - str);
stats->unicode_count = (s - str);
stats->unicode_width = (s - str);
}
return str;
}
if (c < ' ')
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 == '\0')
break;
//if (c < ' ' || c >= 0x7f)
if (c < ' ')
*d = '?';
d++;
}
if (stats) {
stats->byte_count = (d - dst);
stats->unicode_count = (d - dst);
stats->unicode_width = (d - dst);
}
}
#endif
return auto_string(dst);
}
const char* FAST_FUNC printable_string(const char *str)
{
return printable_string2(NULL, str);
}
接着打开文件/libbb/unicode.c,修改如下内容:
//在unicode.c的1011到1058之间
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);
while ((int)--width >= 0) {
unsigned char c = *src;
if (c == '\0') {
do
*d++ = ' ';
while ((int)--width >= 0);
break;
}
//*d++ = (c >= ' ' && c < 0x7f) ? c : '?';
*d++ = (c >= ' ' ) ? c : '?';
src++;
}
*d = '\0';
} else {
d = dst = xstrndup(src, width);
while (*d) {
unsigned char c = *d;
//if (c < ' ' || c >= 0x7f)
if (c < ' ')
*d = '?';
d++;
}
}
if (stats) {
stats->byte_count = (d - dst);
stats->unicode_count = (d - dst);
stats->unicode_width = (d - dst);
}
return dst;
}
dst = NULL;
uni_count = uni_width = 0;
dst_len = 0;
while (1) {
int w;
wchar_t wc;
安装需要的开发库
sudo apt update
sudo apt install make
sudo apt install make-guile
sudo apt-get install build-essential
sudo apt-get install libncurses5-dev
配置选项
make defconfig #使用默认配置
make menuconfig #打开图形化配置界面
设置setting-> Build static binary (no shared libs)
设置Settings -> Support Unicode,使能busybox的unicode编码以支持中文
4、编译busybox
make
make install CONFIG_PREFIX=~/linux/rootfs
#CONFIG_PREFIX指定编译结果的存放目录
ls ~/linux/rootfs
//打开rootfs文件,新增了bin linuxrc sbin usr这些文件
5、创建需要的目录
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
mev -s
六、下载内核kernel
1、下载内核kernel压缩包
Liunx下载地址
tar xvf linux-6.13.6.tar.xz//解压
2、指定编译工具
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
3、将根文件系统放到源码根目录
cd linux-6.13.6
sudo cp ~/linux/rootfs rootfs_arm64 -a
4、配置生成.config
make defconfig
make menuconfig
5、配置支持
添加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)
6、编译
make all -j8
七、启动QEMU虚拟机
1)配置虚拟机属性
qemu-system-x86_64 \
-drive if=virtio,file=ubuntu.qcow2,format=qcow2 \
-cdrom ~/Downloads/ubuntu-20.04.6-live-server-amd64.iso \
-m 2048 \
-boot order=d
2)启动虚拟机
qemu-system-x86_64 -hda /home/sunrise/ubuntu.qcow2 -cdrom /home/sunrise/Downloads/ubuntu-20.04.6-live-server-amd64.iso -m 2048 -boot d
//ubuntu-20.04.6-live-server-amd64.iso可替换成你在虚拟机中所下载的ubuntu镜像名称
如果在过程中鼠标消失可按ctrl+aly+G
3)启动后需要等待两个阶段
4)等待过后会出现引导性的指示
5)若点击继续会格式化所选磁盘
八、QEMU虚拟机的其他使用方法
1)打开后选择监视器
2)在命令中输入help
,可以显示当前虚拟机的信息
九、编译一个简单的内核模块并在QEMU上运行
1、创建文件
在根目录下创建一个文件夹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");
2、在编写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
3、编译
编译module,拷贝到共享目录
make modules
make install
4、启动QEMU
qemu-system-x86_64 -hda /home/sunrise/ubuntu.qcow2 -cdrom /home/sunrise/Downloads/ubuntu-20.04.6-live-server-amd64.iso -m 2048 -boot d
5、结果
出现问题
1、MENU打开虚拟机没有反应
退出后重新配置虚拟机属性
2、在配置busybox时,不能成功打开图形化配置
按照提示添加需要的库