QEMU 模拟器(一)

安装

安装 比较简单, 有几种方案.

  • 远程仓库安装
  • 官网下载可执行文件(Windows 平台适用)
  • 通过源码自己编译
apt-get install qemu                  # Debian/Ubuntu
pacman -S qemu                        # Arch
pacman -S mingw-w64-x86_64-qemu       # MSYS2

可只装指定平台, 如 ARM: sudo apt-get install qemu-system-arm

u-boot 仿真

下载 uboot 源码 这里以版本u-boot-2018.09.tar.bz2 为例

  1. 首先进入uboot根目录, 执行
    export ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-  # 设置编译平台和工具链
    make vexpress_ca9x4_defconfig                     # 加载板子的配置信息
    make -j8                                          # 多线程编译
    
  2. 编译完成后使用 QEMU 启动:
    qemu-system-arm -M vexpress-a9 -m 256M -kernel u-boot -nographic
    
    • -M 指定了主板类型, 和上面编译的 uboot 主板参数一致
    • -m 表示主板的 DRAM 大小
    • -kernel 后面的可执行文件会被QEMU直接加载到内存并执行, 这里等效于 -device loader,file=u-boot,cpu-num=0
    • -nographic 表示不启动图形界面

    [注]: 先按 Ctrl-A 进入QEMU的功能选择模式, 然后按 x 退出.
    [提示]: Ctrl-A 后按 ? 可查看有哪些功能选项.

Linux kernel 仿真

下载 kernel 源码, 这里以5.17版本为例

  1. 在 kernel 根目录执行

    export ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-  # 设置编译平台和工具链
    make vexpress_defconfig                           # 加载板子的配置信息
    make zImage dtbs -j8                              # 编译目标
    

    在新版内核中,需要给内核生成对应的 dtb,因此目标 dtbs 是需要的

    编译后在 arch/arm/boot 生成 zImage, 在 arch/arm/boot/dts 生成 vexpress-v2p-ca9.dtb 可将它们拷贝出来使用, 后面将默认它们已经拷贝出来放在同一路径了

  2. 直接加载内核到QEMU
    load.sh 文件:

     qemu-system-arm                                     \
         -M vexpress-a9                                  \
         -m 256M                                         \
         -nographic                                      \
         -kernel zImage                                  \
         -dtb vexpress-v2p-ca9.dtb                       
    
    • -dtb 告诉QEMU将文件用作设备树二进制(DTB)映像,并在引导时将其传递给内核。

    执行上面脚本后, QEMU将运行Linux内核镜像, 内核显示许多引导消息,然后它将报告: not syncing: VFS, 表示没有找到根文件系统

  3. 制作简单的根文件系统, 并在内核初始化时执行 Hello World 程序
    hello.c

    #include 
    
     void main() 
     {
         printf("Hello World!\n");
    
         while(1);
     }
    

    编译 hello 程序

    arm-linux-gnueabi-gcc -static hello.c -o hello
    
    • -static 表示静态编译, 静态编译会将所有库都链接在一个二进制文件中

    [注] 引入无限循环是因为当Linux执行根文件系统中的第一个程序时,它预计该程序不会退出

    制作基于内存的初始根文件系统 (initramfs), 参考

    Linux 支持把一段内存用来充当临时的文件系统, 这样做是因为在内核启动完成后很可能硬盘等设备并未初始化完成, 将内存作为临时根文件系统, 并在该根文件系统下执行用户程序, 由用户程序来完成初始化工作并挂载在硬盘上的真正的根文件系统

    我们使用cpio工具创建一个简单的文件系统:

    echo hello | cpio -o --format=newc > rootfs
    

    cpio 工具输入一个文件列表, 输出一个文件. 这里指明输出的文件格式是 newc, 它是 initramfs 文件系统格式,Linux内核可以识别.

    load.sh

     qemu-system-arm                                     \
         -M vexpress-a9                                  \
         -m 256M                                         \
         -nographic                                      \
         -kernel zImage                                  \
         -dtb vexpress-v2p-ca9.dtb                       \
         -initrd rootfs                                  \
         -append "root=/dev/ram init=/hello console=ttyAMA0"      
    
    • -initrd QEMU可以使用initrd参数将 initramfs 文件系统二进制映像传递给内核;
    • -append 用于向内核传递参数, 内核还必须知道根文件系统将位于RAM中(因为这是QEMU写入initrd二进制文件的位置),并且指明第一个启动的程序是我们的测试可执行文件hello
    • console=ttyAMA0 是将输出定向到串口, 否则需要去掉 -nographic 才能查看

    上面的shell 脚本执行后将会看到屏幕上显示Hello World, 并且内核停留在 while 循环.

  4. initrd vs initramfs
    initrd 是 Linux 2.6 版本以前制作内存根文件系统的方法了, 该方法已经过时. 这是因为 initrd (initial ram disk 初始化内存磁盘) 是把内存当做一块磁盘, 内核需要先创建 ramdev 块设备, 然后将 initrd 拷贝到块设备,最后挂载.
    同时 initrd 内的文件系统还需要驱动支持, 如果是 ext2 则必须在内核中编译 ext2 驱动程序. initrd 最大的缺点是占用较大内存,不可伸缩. 参考官方文档
    下面演示如何制作 initrd

    dd if=/dev/zero of=rootfs.img bs=1024 count=4096       # 创建 4096k 虚拟磁盘
    mkfs.ext2 rootfs.img                                   # 格式化成 ext2 格式文件系统
    mount -o loop rootfs.img /mnt                          # 将镜像文件和块设备关联并挂载设备到 /mnt
    cp hello /mnt                                          # 将 hello 程序拷贝到根目录
    umount /mnt                                            # 卸载磁盘
    

    新版本 kernel 默认不再支持内存块设备支持, 需要手动打开:
    在内核目录执行 grep BLK_DEV_RAM .config 查看 BLK_DEV_RAM 是否被编译进去, 若没有则需要使用 make menuconfig 进入设置后按 / 查找 BLK_DEV_RAM 并到对应区域打开该功能

    重新编译内核后拷贝出 zImage 执行下面的脚本

    qemu-system-arm                                          \
         -M vexpress-a9                                      \
         -m 256M                                             \
         -nographic                                          \
         -kernel zImage                                      \
         -dtb vexpress-v2p-ca9.dtb                           \
         -initrd rootfs.img                                  \
         -append "root=/dev/ram0 init=/hello console=ttyAMA0"
    

    新版的 initramfs 直接把内存作为文件系统, 因为内核总是内置 tmpfs 文件系统 (initramfs就是tmpfs的实例), 可以直接挂载不需要设备和额外的文件系统驱动程序.

  5. 根文件系统在外部设备
    现代的高级的系统引导程序, 往往都直接支持文件系统, 在初始化磁盘后可以识别文件系统, 并将参数传递给内核从哪个外部设备挂载根文件系统. 制作外部设备的根文件系统镜像和制作 initrd 是一样的, 因为它们其实本质都是包含文件系统的镜像文件, 因此可以直接使用上一节的 rootfs.img 来使用, 只是它们被放到了真正的磁盘, 而 initrd 则是内存虚拟的磁盘. 例如, 我们把它虚拟成sd卡, 让 QEMU 的引导程序传递给内核
    load.sh

     qemu-system-arm                                     \
         -M vexpress-a9                                  \
         -m 256M                                         \
         -nographic                                      \
         -kernel zImage                                  \
         -dtb vexpress-v2p-ca9.dtb                       \
         -sd rootfs.img                                  \
         -append "root=/dev/ram rdinit=/hello console=ttyAMA0"   
    

你可能感兴趣的:(QENU,c语言,ssh)