qemu 嵌入式linux 开发环境搭建

主要记录大致步骤.

qemu 安装

https://www.qemu.org/download/

wget https://download.qemu.org/qemu-9.2.1.tar.xz
tar --xz --get --file qemu-9.2.1.tar.xz
cd qemu-9.2.1
./configure
make -j$(nproc) # $(nproc) 获取cpu核心数
sudo make install

qemu-system-arm -M help # 查看支持的板子

交叉编译工具链安装

sudo apt-get install gcc-arm-linux-gnueabi

怎么选择交叉编译工具链: https://www.cnblogs.com/wxishang1991/p/5322499.html

u-boot 移植

git clone git://git.denx.de/u-boot.git

ls configs/ | grep vexpress
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- vexpress_ca9x4_defconfig

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- -j$(nproc)

linux 内核移植

清理编译和配置文件
make mrproper

#下载解压内核源码
#通常来说内核源码分别可以从官方,芯片厂商,开发板厂商处获取.
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.13.1.tar.xz
tar --get --file linux-6.13.1.tar.xz 

# 生成默认.config文件, 把默认配置文件复制到kernel顶层目录
# 我使用的是vexperss-a9开发板,直接找找有没有想关配置文件
ls arch/arm/configs/ | grep vexpress
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- vexpress_defconfig

#配置内核,通过menuconfig修改.config文件
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- menuconfig

#编译内核
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- -j $(nproc)

#仿真
#仿真还需要设备树文件, Linux kernel 里有一些默认的设备树文件
ls arch/arm/boot/dts/arm | grep vexpress

qemu-system-arm -M vexpress-a9 -m 512M \
               -kernel arch/arm/boot/zImage \
               -dtb arch/arm/boot/dts/arm/vexpress-v2p-ca9.dtb \
               -append "console=ttyAMA0" \
               -nographic 
#此时内核可以运行一段但会因为没有文件系统停下来.
# Unable to mount root fs on unknown-block(0,0)

不知道为什么 make menuconfig 后.config变成了x86 编译器字段也变成了gcc而不是交叉编译工具.
因为开始make后面没有带环境变量所以相当于是修改了.正常是读取顶层.config的

linux内核的配置选项很多, 通常都是基于默认或硬件厂商提供的配置文件, 然后根据需求进行修改.

编译过程中可能会因为有些依赖没有安装而报错, 根据提示安装缺少的依赖即可.

使用busybox制作根文件系统

https://www.cnblogs.com/challa/p/15291249.html#根文件系统是什么

  1. 下载安装busybox
git clone git://busybox.net/busybox.git

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- defconfig
#选择静态编译// 有其他的需求在这一步进行配置,可以参考上面的链接
make menuconfig
#   勾选 Busybox Setting-> Build Options-> [*] Build static binary (no shared libs)
make -j$(nproc)
  1. 制作根文件系统
目录 说明 补充
linuxrc 第一个执行的用户程序, 提供操作界面 必须存在, busybox生成
bin 所有用户都可以使用的、基本的命令 必须存在, busybox创建并集成一些命令
sbin 基本的系统命令 必须存在, busybox创建并集成一些命令
usr 用户目录 可以删除, busybox创建并集成一些命令
etc 存放配置文件, 被 /linuxrc 所调用执行 必须存在
lib 存放的是当前操作系统中的动态和静态链接库文件 必须存在
dev 设备文件 必须存在
sys 虚拟文件系统, 不可省略, 但是只要创建了空文件夹即可 必须存在
proc 虚拟文件系统, 不可省略, 但是只要创建了空文件夹即可 必须存在
mnt 用来挂载 可以删除
  • 创建根文件系统目录
#!/bin/bash
rm -rf rootfs/

echo "----Create rootfs directons"
# 创建文件系统目录
mkdir -p rootfs/{bin,sbin,usr,dev,etc,lib,proc,sys,mnt,root}
mkdir -p rootfs/dev/pts

# 复制busybox生成的文件
echo "----Copy busybox{bin,linuxrc,sbin,usr}"
cp busybox-*/_install/* rootfs/ -arf #保留符号链接因为busybox生成的命令都是符号链接到busybox.bin

# 创建etc/inittab文件
echo "----Create etc/inittab"
cat <<EOL > rootfs/etc/inittab
#/etc/inittab                 # 井号是注释

::sysinit:/etc/init.d/rcS     # 系统启动以后运行 /etc/init.d/rcS 这个脚本文件
console::askfirst:-/bin/sh    # 将 console 作为控制台终端,也就是 ttymxc0。
::restart:/sbin/init          # 重启的话运行/sbin/init。
::ctrlaltdel:-/sbin/reboot    # 按下 ctrl+alt+del 组合键的话就运行 /sbin/reboot(重启系统)
::shutdown:/bin/umount -a -r  # 关机的时候执行 /bin/umount,也就是卸载各个文件系统
::shutdown:/sbin/swapoff -a   # 关机的时候执行 /sbin/swapoff,也就是关闭交换分区。
EOL

# 创建/etc/init.d/rcS文件
echo "----Create etc/init.d/rcS"
mkdir -p rootfs/etc/init.d
cat <<EOL > rootfs/etc/init.d/rcS
#!/bin/sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin:
LD_LIBRARY_PATH=/lib:/usr/lib
export PATH LD_LIBRARY_PATH     # 导出上面这些环境变量
mount -a                        # 挂载所有的文件系统,需存在/etc/fstab 文件
EOL
chmod +x rootfs/etc/init.d/rcS

# 创建etc/fstab文件
echo "----Create etc/fstab"
cat <<EOL > rootfs/etc/fstab
proc            /proc           proc     defaults        0 0 #用于暴露内核信息,驱动编程需要/proc/devices
sysfs           /sys            sysfs    defaults        0 0 #用于暴露系统硬件和内核对象
devtmpfs        /dev            devtmpfs defaults        0 0
EOL

# 复制动态库文件
echo "----Add dynamic library"
#直接复制交叉编译工具lib目录下的动态库
cp /usr/arm-linux-gnueabi/lib/*.so* rootfs/lib/ -arf
arm-linux-gnueabi-strip rootfs/lib/*.so* #去除动态库的调试信息

# 创建设备节点
echo "----make node: dev/console dev/null"
sudo mknod -m 600 rootfs/dev/console c 5 1
sudo mknod -m 600 rootfs/dev/null    c 1 3
  • 把配置好的文件系统打包成ext3文件系统
#!/bin/bash

rm -f a9rootfs.ext3
dd if=/dev/zero of=a9rootfs.ext3 bs=1M count=64
mkfs.ext3 a9rootfs.ext3
mkdir -p tmpfs
chmod 777 tmpfs
sudo mount -t ext3 a9rootfs.ext3 tmpfs/ -o loop
sudo cp -r rootfs/*  tmpfs/
sudo umount tmpfs
rm -rf tmpfs
  1. 仿真
qemu-system-arm -M vexpress-a9 -m 512M \
               -kernel /home/wuuu/vmDevEnv/a9_linux/linux-6.13.1/arch/arm/boot/zImage \
               -dtb /home/wuuu/vmDevEnv/a9_linux/linux-6.13.1/arch/arm/boot/dts/arm/vexpress-v2p-ca9.dtb \
               -nographic \
               -append "root=/dev/mmcblk0 rw console=ttyAMA0" \
               -sd a9rootfs.ext3

根文件系统给linux提供了什么?

  1. 一些基本的命令
  2. 一些基本的库
  3. 一些配置文件
  4. 一些设备文件
  5. 一些启动脚本

补充1 网络挂载

服务端

  1. 安装配置NFS服务
sudo apt update
sudo apt install nfs-kernel-server
  1. 配置NFS服务
sudo vim /etc/exports
# 添加如下内容
/home/wuuu/vmDevEnv/a9_linux/rootfs *(rw, sync, no_root_squash, no_subtree_check)
# 格式: /path/to/share  允许访问的ip(rw,sync,no_root_squash,no_subtree_check)
# rw:允许读写。
# sync:同步写入。
# no_root_squash:允许远程 root 用户具有与本地 root 用户相同的权限
# no_subtree_check # 访问子目录时不进行权限检查

sudo exportfs -ra # 重新加载配置文件
  1. 启动NFS服务
sudo systemctl start nfs-kernel-server
sudo systemctl enable nfs-kernel-server #开机启动

# 查看NFS服务状态
sudo systemctl status nfs-kernel-server

#查看export的NFS共享目录
sudo showmount -e

qemu网络配置

虚拟机需要和宿主机在同一网络下才能访问到NFS服务, 所以使用桥接模式.

  1. 宿主机网络配置

ifconfig # 查看宿主机的ip和mask
# eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        # inet 172.24.29.97  netmask 255.255.240.0  broadcast 172.24.31.255
route # 查看宿主机的网关
# Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
# default         DESKTOP-GFVJBIC 0.0.0.0         UG    0      0        0 eth0
# 172.24.16.0     0.0.0.0         255.255.240.0   U     0      0        0 eth0

# 创建一个桥接设备br0
sudo ip link add br0 type bridge
# 创建一个tap设备tap0, 给qemu使用, qemu启动的时候把虚拟机的网卡连接到tap0上
sudo ip tuntap add dev tap0 mode tap
# 把tap0添加到br0上
sudo ip link set dev tap0 master br0
# 把宿主机的网卡eth0添加到br0上
sudo ip link set dev eth0 master br0
# 启动br0和tap0
sudo ip link set dev br0 up
sudo ip link set dev tap0 up
# 把eth0的ip解绑, 然后绑到br0上
sudo ip address delete $ip dev eth0 # 解绑的时候只用ip
sudo ip address add ${ip/mask} dev br0 # 给br0绑定的时候还有掩码, 格式:172.24.29.97/20, /后面的20就是掩码,表示前20位是1,对应的mask是255.255.240.0
# 添加路由
sudo ip route add default via $ROUTE dev br0 #根据上面的route显示的内容这里$ROUTE应该是172.24.16.0
#--------------------------桥接网络配置完成---------------------------------------------------------

# 补充命令
# 删除br0和tap0
sudo ip link delete br0 type bridge
sudo ip tuntap delete dev tap0 mode tap
  1. qemu配置
    在启动项里添加
    -netdev tap,id=net0,ifname=tap0,script=no,downscript=no -device virtio-net-device,netdev=net0
    
    简单解释下这个选项, 现代qemu把设备分为前端后端, -netdev 是创建一个后端, ifname=tap0表示这个后端和宿主的tap0连接在一起. -device 是创建一个前端(虚拟机内的虚拟设备), netdev=net0表示这个前端和后端net0连接在一起.

客户端

  1. 配置内核支持NFS
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- menuconfig
# File systems -> Network File Systems -> NFS client support
# ENable NFS version 4 sup
  1. 配置虚拟机ip和宿主机在一个子网内
    在启动脚本rcS中添加ifconfig eth0 172.24.29.21, 设置eth0的ip.

    配置好ip后, 可以通过ping -c 4 $ip 来测试是否能ping通宿主机. 然后尝试挂载NFS服务.

mount -t nfs $ip:/path/to/share /mnt

# 如遇到下面报错
# svc: failed to register lockdv1 RPC service (errno 101).
# 使用 -o nolock 选项
mount -t nfs -o nolock 172.24.29.97:/home/wuuu/vmDevEnv/a9_linux/rootfs /mnt

到此满足我目前的需求了, 挂载nfs作为根目录以后再研究.

posted @ 2025-02-19 21:15  天刚刚破晓  阅读(171)  评论(0)    收藏  举报