Linux 内核学习(17) --- linux 内核编译和 qemu 环境搭建

目录

        • [qemu 基础说明](#qemu 基础说明)
        • [qemu 安装 和 arm64 交叉编译器安装](#qemu 安装 和 arm64 交叉编译器安装)
        • [linux 内核下载和编译](#linux 内核下载和编译)
        • [busybox 下载编译](#busybox 下载编译)
        • [打包 initramfs](#打包 initramfs)
        • [启动 qemu](#启动 qemu)
qemu 基础说明

QEMU 是一个开源的虚拟机仿真器,可模拟多种硬件架构(如 ARMx86 等),适用于嵌入式开发、内核调试和系统测试

如果在 ubunut/deepin 系统上安装 qemu ,并启动 linux arm64 内核 initramfs 的最小系统,需要的步骤如下:

  1. 下载安装 qemuarm64 交叉编译器
  2. 下载 linux kernel内核(6.6)并编译
  3. 下载 busybox 并编译
  4. 打包为 ramdisk 最小系统并启动内核

另外也提供了一个 编译生成 userspace binary 的方法,可以用于测试自己对 linux 内核的开发

qemu 安装 和 arm64 交叉编译器安装

步骤如下:

shell 复制代码
# 安装 qemu
sudo apt update
sudo apt install qemu-system-arm

# 打印出版本号
qemu-system-arm --version

# 安装 aarch64-linux-gnu-
sudo apt install gcc-aarch64-linux-gnu

# 打印出版本号
aarch64-linux-gnu-gcc -v
linux 内核下载和编译
shell 复制代码
# 选择自己需要的内核版本
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.tar.xz

linux 内核编译,参考下面的脚本:

shell 复制代码
KERNEL_VERSION="6.6"
BUILD_DIR="$(pwd)/build"
KERNEL_DIR="$BUILD_DIR/linux"
KERNEL_BUILD_DIR="$BUILD_DIR/linux/out"

CROSS_COMPILE=aarch64-linux-gnu-
ARCH=arm64

#========================
#编译内核
#========================
build_kernel() {
    echo "build kernel ($KERNEL_VERSION)..."
    cd "$KERNEL_DIR"
    mkdir -p $KERNEL_BUILD_DIR
    make ARCH=$ARCH CROSS_COMPILE=$CROSS_COMPILE O=$KERNEL_BUILD_DIR dmabuf_defconfig
    make -j$(nproc) ARCH=$ARCH CROSS_COMPILE=$CROSS_COMPILE O=$KERNEL_BUILD_DIR Image
    cd -
}

#========================
#kernel menuconfig
#========================
build_kernel_menuconfig() {
    echo "make kernel ($KERNEL_VERSION) menuconfig and save..."
    cd "$KERNEL_DIR"
    mkdir -p $KERNEL_BUILD_DIR
    make ARCH=$ARCH CROSS_COMPILE=$CROSS_COMPILE O=$KERNEL_BUILD_DIR menuconfig
    make -j$(nproc) ARCH=$ARCH CROSS_COMPILE=$CROSS_COMPILE O=$KERNEL_BUILD_DIR savedefconfig
    cp -v $KERNEL_DIR/out/defconfig $KERNEL_DIR/arch/arm64/configs/dmabuf_defconfig
    cd -
}

#========================
#编译dtb all dtb
#========================
build_kernel_dtb() {
    echo "build kernel ($KERNEL_VERSION)..."
    cd "$KERNEL_DIR"
    mkdir -p $KERNEL_BUILD_DIR
    make ARCH=$ARCH CROSS_COMPILE=$CROSS_COMPILE O=$KERNEL_BUILD_DIR dtbs
    cd -
}

注意点:

  • linux 内核编译的 O(OUTPUT) 参数表示内核编译的输出目录,这样可以保持代码目录干净
  • menuconfig 可以用于选择配置,由分布在内核里的 Kconfig 决定,但是需要配合 savedefconfig
  • savedefconfig 生成的配置保存在 defconfig,在这里拷贝保存成了自己的配置

生的 dtb 文件,用下面命令进行反编译:

shell 复制代码
dtc -I dtb -O dts -o output.dts.txt qemu-default.dtb
busybox 下载编译

使用下面命令下载 busybox:

c 复制代码
wget -nc https://busybox.net/downloads/busybox-1.36.1.tar.bz2

编译方法

shell 复制代码
#========================
#编译 BusyBox
#========================
build_busybox() {
    echo "build BusyBox ($BUSYBOX_VERSION)..."
    mkdir -p $BUSYBOX_BUILD_DIR
    
    cd "$BUSYBOX_DIR"
    make ARCH=$ARCH CROSS_COMPILE=$CROSS_COMPILE KBUILD_OUTPUT=$BUSYBOX_BUILD_DIR defconfig
    # 启用静态链接
    sed -i 's/# CONFIG_STATIC is not set/CONFIG_STATIC=y/' $BUSYBOX_BUILD_DIR/.config
    make -j$(nproc) ARCH=$ARCH CROSS_COMPILE=$CROSS_COMPILE KBUILD_OUTPUT=$BUSYBOX_BUILD_DIR
    cd -
}

注意:

  • 这里强制启用了静态链接,因为没有拷贝对应的 glibc lib 到根文件系统
打包 initramfs
shell 复制代码
#========================
#构建根文件系统
#========================
build_rootfs() {
    echo "generate rootfs..."
    rm -rf "$ROOTFS_DIR"
    mkdir -p "$ROOTFS_DIR"

    # 安装 busybox 到 rootfs
    cd "$BUSYBOX_DIR"
    make ARCH=$ARCH CROSS_COMPILE=$CROSS_COMPILE KBUILD_OUTPUT=$BUSYBOX_BUILD_DIR CONFIG_PREFIX="$ROOTFS_DIR" install
    cd -

    # 创建必要目录
    mkdir -p "$ROOTFS_DIR"/{proc,sys,dev,etc/init.d}
    
    #直接将 busybox 拷贝为 init
    cp -v "$ROOTFS_DIR"/bin/busybox "$ROOTFS_DIR"/init
    
    # 拷贝启动文件
    cp  -v "$BUILD_DIR"/rootfs_files/rcS "$ROOTFS_DIR"/etc/init.d/rcS
    chmod +x "$ROOTFS_DIR"/etc/init.d/rcS
}

#========================
#打包 initramfs
#========================
pack_initramfs() {
    echo "pack initramfs..."
    cd "$ROOTFS_DIR"
    find . | cpio -o --format=newc | gzip -9 > "$INITRD_FILE"
    cd -
    echo "okay initramfs : $INITRD_FILE"
}

其中启动文件 rcS 内容:

shell 复制代码
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
/sbin/mdev -s
echo "Welcome to wangchen ARM64 QEMU System!"
exec /sbin/getty -L -n -l /bin/sh ttyAMA0 115200 vt100
启动 qemu

真实的 HW 启动必须要指定 dtb 文件,但是这里使用的 virt machineqemu 启动,所以:

可以不指定 dtb, 使用默认 virtdtb 启动 qemu:

shell 复制代码
run_qemu() {
    echo "starting qemu arm64..."
    qemu-system-aarch64 \
        -machine virt \
        -cpu cortex-a72 \
        -smp 2 \
        -m 512 \
        -kernel "$KERNEL_BUILD_DIR/arch/arm64/boot/Image" \
        -initrd "$INITRD_FILE" \
        -append "console=ttyAMA0,115200 earlyprintk=serial loglevel=8" \
        -nographic \
        -serial mon:stdio
}

说明:

-machine virt:使用通用 ARM64 虚拟机平台

-smp 2 :设置处理器为双核

-m 512 :设置内存大小为 512M

-nographic:禁用图形界面,使用串口输出(适合服务器环境)

virtdtb 可以使用下面的命令 dump 出来:

shell 复制代码
# 注意 virt 后加逗号,之后不能有空格
qemu-system-aarch64 \
    -machine virt,dumpdtb=qemu_default.dtb \
    -cpu cortex-a72 \
    -smp 2 \
    -m 512

QEMUvirt 机器模型会自动生成一个 dtb,这个自动生成的 dtb 包括:

  1. 内存节点(根据 -m 参数,比如 -m 1G → reg = <0 0x40000000 0 0x40000000>)
  2. 中断控制器(GIC);
  3. 串口(pl011 或 8250);
  4. 定时器、RTC etc

这些节点的内容也可以从 /proc/device-tree 显示出来

相关推荐
cooldream20098 小时前
Vim 报错 E325:swap 文件冲突的原理、处理流程与彻底避免方案
linux·编辑器·vim
i建模8 小时前
在 Rocky Linux 上安装轻量级的 XFCE 桌面
linux·运维·服务器
lxl13079 小时前
学习C++(5)运算符重载+赋值运算符重载
学习
Data_Journal9 小时前
Scrapy vs. Crawlee —— 哪个更好?!
运维·人工智能·爬虫·媒体·社媒营销
若风的雨9 小时前
WC (Write-Combining) 内存类型优化原理
linux
YMWM_9 小时前
不同局域网下登录ubuntu主机
linux·运维·ubuntu
zmjjdank1ng9 小时前
restart与reload的区别
linux·运维
哼?~9 小时前
进程替换与自主Shell
linux
Suchadar9 小时前
Docker常用命令
运维·docker·容器
你才是臭弟弟9 小时前
MinIo开发环境配置方案(Docker版本)
运维·docker·容器