Lyndra's Blog

QEMU开发环境搭建

2024-10-30
编程开发工具教程 QEMULinuxDocker
14分钟
2639字
温馨提示:本文最后更新于 2025-03-11 ,部分信息可能因时间推移而不再适用,欢迎反馈。

QEMU开源软件在Linux上进行开发。在windows上可以采用WSL Linux,也可以自行在电脑上安装Linux原生系统。一般采用vscode作为IDE 开发QEMU。

  本文采用 vscode 连接校内 ubuntu 服务器的方式进行开发环境搭建。ubuntu服务器多人使用,采用docker 容器建立各自独立的开发环境。

docker环境

  使用docker启动作为编译的系统环境。可以将 docker 视作轻量级虚拟机,先创建 docker镜像,再以镜像为基础启动容器。每一个容器视作虚拟机,系统环境数据存在容器中,工作文件夹等需要保存在硬盘上的重要文件,以共享文件夹的方式映射到容器。镜像不保存任何运行数据。

dockfile

  创建名为 Dockerfile​ 的文件,并填入下列内容。并可以根据自行需要(用户\组 id,软件依赖等),增删其中的内容。

  注意:存放 Dockerfile​ 的文件夹中最好不要存放任何其他无关的文件,在创建镜像时,docker 会将该文件夹中的所有内容都复制到容器中,这会大大增加镜像创建的时间。

1
FROM ubuntu:24.04
2
3
# 定义构建时变量,可以在docker build构建通过--build-arg来修改
4
ARG GID=1000
5
ARG UID=1000
6
ARG username=developer
7
8
# 设置 DEBIAN_FRONTEND 环境变量以避免交互式对话框,否则可能会卡在一些交互式输入中
9
ENV DEBIAN_FRONTEND=noninteractive
10
11
# 修改ubuntu的镜像源为阿里云
12
# ubuntu24.04版本修改软件源的位置:/etc/apt/sources.list 替换为 /etc/apt/sources.list.d/ubuntu.sources
13
RUN sed -i s@/archive.ubuntu.com/@/mirrors.aliyun.com/@g /etc/apt/sources.list.d/ubuntu.sources \
14
&& sed -i s@/security.ubuntu.com/@/mirrors.aliyun.com/@g /etc/apt/sources.list.d/ubuntu.sources \
15
&& apt-get update -y && apt-get install -y sudo locales \
49 collapsed lines
16
&& apt-get clean \
17
&& echo "%sudo ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
18
# 修改容器或系统中的 sudoers 文件,允许属于 sudo 组的用户执行 sudo 命令时无需输入密码。
19
20
# 修改语言环境(locale)设置
21
RUN locale-gen en_US.UTF-8 && update-locale
22
23
# 添加用户:赋予sudo权限,指定密码123,建议docker的密码不要太复杂,太多了很容易忘记。
24
25
# 注:用户id和组id尽可能的和当前用户id一致,使得读写共享文件时的权限一致,否则可能出现docker无法写入共享文件的问题。
26
# 命令 id 可以查看用户的各种id
27
RUN getent group ${GID} || groupadd -g ${GID} dev \
28
&& if getent passwd ${UID}; then \
29
usermod -l ${username} $(getent passwd ${UID} | cut -d: -f1) && \
30
usermod -d /home/${username} -m ${username} && \
31
usermod -aG sudo ${username}; \
32
else \
33
useradd -ms /bin/bash -u ${UID} -g ${GID} -G sudo ${username}; \
34
fi \
35
&& echo "${username}:123" | chpasswd \
36
&& echo 'root:123' | chpasswd
37
38
# 安装各种以依赖软件,可以根据需要定制,也可以后续手动安装。
39
40
# QEMU编译需要的依赖
41
RUN apt-get install -y build-essential meson ninja-build pkg-config \
42
diffutils python3 python3-venv \
43
libglib2.0-dev libusb-1.0-0-dev libncursesw5-dev \
44
libpixman-1-dev libepoxy-dev libv4l-dev libpng-dev \
45
libsdl2-dev libsdl2-image-dev libgtk-3-dev libgdk-pixbuf2.0-dev \
46
libasound2-dev libpulse-dev \
47
libx11-dev libfdt-dev libiscsi-dev
48
49
# riscv-gnu-toolchain 需要的依赖
50
RUN apt-get install -y autoconf automake autotools-dev curl \
51
python3 python3-pip libmpc-dev libmpfr-dev libgmp-dev gawk build-essential \
52
bison flex texinfo gperf libtool patchutils bc zlib1g-dev libexpat-dev \
53
ninja-build git cmake libglib2.0-dev libslirp-dev
54
55
RUN apt-get install -y git gdb clang cmake vim gdb
56
57
# 还原 DEBIAN_FRONTEND 环境变量(可选)
58
ENV DEBIAN_FRONTEND=dialog
59
60
# 指定容器启动后的工作目录
61
WORKDIR /home/${username}
62
63
# 指定容器启动后的登录用户
64
USER ${username}

创建镜像

使用 docker 的 buildx 来创建,buildx 命令在 docker 19.03 版本引入。

确认安装了 docker-buildx:docker buildx version,有输出即可,如果没有,在 manjaro 上可以通过 yay -S docker-buildx 安装。

使用下列命令,基于 Dockerfile 构建镜像,如果下载 apt 过慢,则可尝试使用 --network=host 选项,在安装软件时可能会快一些:

Terminal window
1
docker build --network=host -t lyndra-qemu - < Dockerfile

注意,此方法没有传入任何上下文,如果需要构建时使用的上下文,参考 Docker中创建镜像

  若在创建镜像时希望动态修改 Dockerfile​ 中的ARG参数,则在构建时,添加参数 --build-arg ARG_name=value​ ,将 ARG_name​ 修改为 value​。

  由于每个用户 id 和组 id 不一样,需要在终端中运行命令 id ​查询到当前用户的 group 和 user id,并在构建时修改参数,假设用户和组 id 都为 1004:

Terminal window
1
docker build --network=host --build-arg GID=1004 --build-arg UID=1004 -t lyndra-qemu - < Dockerfile

  修改用户 id 和组 id 和当前的账户一致,是为了确保在写入共享文件夹的时候有相同的权限,否则可能造成在容器中无法正常写入的情况。

创建容器

  下面的命令创建一个名为 lyndra_qemu_dev 的容器,并将主机的文件夹 /home/lyndra/Desktop/work/​ 共享到容器中的 /home/developer/work​ 文件夹。

Terminal window
1
docker run -it --name lyndra_qemu_dev --mount type=bind,source=/home/lyndra/Desktop/work/,target=/home/developer/work lyndra-qemu

  注:如果需要使用代理网络,可以通过ssh隧道将流量代理到本机(例如:个人Linux主机通过SSH隧道使服务器访问外网),在创建容器时添加参数--network host​,让docker位于host网络中。

Terminal window
1
docker run -it --network host --name lyndra_qemu_dev --mount type=bind,source=/home/lyndra/Desktop/work/,target=/home/developer/work lyndra-qemu

  在设置代理时,可以手动设置 export http_proxy="localhost:7897" export https_proxy="localhost:7897"​,也可以在启动容器时,添加参数直接设置代理环境 -e HTTP_PROXY=http://localhost:7897

Terminal window
1
docker run -it --network host -e HTTP_PROXY=http://localhost:7897 -e HTTPS_PROXY=http://localhost:7897 --name lyndra_qemu_dev --mount type=bind,source=/home/lyndra/Desktop/work/,target=/home/developer/work lyndra-qemu

  上述命令的含义可询问GPT或者查询网络。

QEMU 源码

  QEMU 是一个大型开源软件,会不断的发布稳定版本,并在 master 主分支上不断更新新的功能。为了便于对旧版本进行维护、修复和发布更新,QEMU 为每一个稳定版本都创建了 stable 分支,并会不断的维护。

  本文以 qemu 9.0 分支版本作为基础,进行开发。

  官方源码仓库:github.com/qemu/qemu.git

官方仓库 fork 到私人仓库

  以 gitee 为例

default

  将私人仓库 clone 到本地:

1
git clone https://gitee.com/code-tang/qemu-sayram2.0.git

私人仓库建立工作分支

  在稳定分支 9.0 基础上,在本地新建新分支 sayram2​、sayram2-dev​,并将本地新建的两个分支 push 远程仓库。

1
git fetch origin stable-9.0:sayram2
2
git push --set-upstream origin sayram2
3
4
git checkout -b sayram2-dev
5
git push --set-upstream origin sayram2-dev

  然后查看本地分支情况:

Terminal window
1
$ git branch -v
2
master 58d49b5895 Merge tag 'net-pull-request' of https://github.com/jasowang/qemu into staging
3
sayram2 6a54d5cf55 Update version for 9.0.3 release
4
* sayram2-dev 6a54d5cf55 Update version for 9.0.3 release

  其中,sayram2-dev​ 作为开发分支,开发稳定后,merge 到稳定分支 sayram2​ 中。若后续官方的 stable-9.0​ 分支出现重大更新,可将其 pull 到本地,和 sayram2​、sayram2-dev​两个分支进行 merge​。

  注意:需要更新官方仓库时,不要在仓库点击强制同步,这样会覆盖仓库代码。

default

  若要更新官方仓库,需在本地添加官方 github 仓库源,并 pull 代码到对应的分支,完成 merge 工作后,再推送到私有仓库。

Terminal window
1
git remote add official https://github.com/qemu/qemu.git
2
git checkout sayram2-dev
3
git pull official stable-9.0 --rebase

  执行该命令后,本地分支将基于 official​ 的 stable-9.0​ 分支的最新提交进行变基,相当于先将远程的更改应用在当前分支上,再重新应用本地的更改,从而避免出现合并提交。出现冲突需要手动解决。

  如果后续 stable-9.0 出现更新,并希望将更新应用到 sayram2 分支,则可以直接 pull stable-9.0

Terminal window
1
git checkout sayram2
2
git pull official stable-9.0

常规编译流程

  参考官方文档:www.qemu.org/docs/master/devel/build-system.html

  一般的开发仅关注于源码,不会对编译脚本做过多改动。
从源码编译 qemu 总共两步:1. configure 2. build

  1. configure

    1
    cd qemu
    2
    ./configure --target-list=riscv32-softmmu --enable-debug

    关于 configure​ 的更多选项可以参考 ./configure --help​ 的输出。

    1
    developer@ubuntu-AS-4124GS-TNR:~/work/qemu/qemu-official$ ./configure --help
    2
    Using './build' as the directory for build output
    3
    4
    Usage: configure [options]
    5
    Options: [defaults in brackets after descriptions]
    6
    7
    Standard options:
    8
    --help print this message
    9
    --target-list=LIST set target list (default: build all)
    10
    Available targets: aarch64-linux-user
    11
    aarch64_be-linux-user alpha-linux-user
  2. build

    可以使用 make 和 ninja 进行编译,两者都行。区别是 ninja 自动开启多线程加速编译,而 make 需要手动加上参数 -j​。

    1
    cd build
    2
    ninja
    3
    4
    or
    5
    cd build
    6
    make -j

    个人喜欢用 ninja,输出的编译信息不会占满整个屏幕。

vscode远程开发项目

  vscode 实际上就是一个编辑器,但是可以通过各种插件将其变为一个IDE。例如远程开发功能,vscode 需要安装扩展:Remote - SSH​、 Dev Containers​。vscode 可以运行在任意电脑上,通过远程网络连接到同一个工作服务器上开发。

SSH连接服务器

default

  新建远程后,会在顶部弹出界面,提示输入 ssh 连接命令。如图所示输入账号和ip地址

default

  顶部会弹出选项框,要求选择一个配置文件,保存当前连接的服务器信息。选择第一个即可。

default

default

  这里的配置文件可以后续再进行修改,其中 Host​ 表示 ssh 服务器名称,可以任意更改,Hostname​ 保存服务器 ip地址, User​ 保存用户名。

  然后远程资源管理器会更新输入的服务器信息。

default

  点击连接按钮后,顶部弹出输入密码。

default

  首次连接,或者更新 vscode 后,服务器会下载 vscode 服务器,需要一定时间。

default

vscode连接容器

  当安装完 Dev Containers​ 扩展后,远程资源管理器会多一个开发容器的选项。

default

  已经连接的容器会显示在 开发容器​ 这一栏,没有连接过的容器显示在 其他容器​ 这一栏。和 SSH 同理,点击容器连接即可,不需要输入密码。

default

  连接后,所有的开发环境都存在于 docker 容器,在本文创建镜像时,共享了主机的 work​ 目录,因此,将代码存放于该目录。

vscode 中调试

  vscode 只是编辑器,还需要配置 task.json​ 调试文件,和 launch.json​ 文件来决定调试需要运行的文件,调试工具的位置等。

  qemu 在 Linux 上的调试文件如下:

  ​.vscode/launch.json​:调试文件,F5快捷键进入调试模式

1
{
2
// Use IntelliSense to learn about possible attributes.
3
// Hover to view descriptions of existing attributes.
4
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5
"version": "0.2.0",
6
"configurations": [
7
8
{//"-init_data","${workspaceFolder}/Hmatrix0.dat "ultichip-u1,pipeline=true",",
9
//配置编译./configure --enable-debug --target-list=riscv32-softmmu
10
//配置编译./configure --enable-debug --disable-werror --target-list=riscv32-softmmu
11
12
"name": "(gdb) Launch",
13
"type": "cppdbg",
14
"request": "launch",
15
"program": "${workspaceFolder}/build/qemu-system-riscv32",
29 collapsed lines
16
17
// QEMU 启动时的参数,需要根据需要,自行调整。
18
"args": ["-machine", "ub", "-cpu", "ultichip-u1,pipeline=true", "-bios", "''", "-m", "128M","-d","in_asm", "-display", "none", "-ulog", "./test_sayram/outlog/runtime.log",
19
"-rlog", "./test_sayram/outlog/reg.log","-savedmem","./test_sayram/outlog/dmemory.dat","-nographic","-kernel",
20
"./test_sayram/input/dl/vp0", "-device", "loader,file=./test_sayram/input/dl/qemu_indmem.bin,addr=0x17b40", "-lbr", "32"],
21
22
"stopAtEntry": true,
23
"cwd": "${workspaceFolder}",
24
"environment": [
25
{
26
"name":"PATH",
27
"value":"%PATH%;\bin"
28
}
29
],
30
//"console": "externalTerminal",
31
"MIMode": "gdb",
32
"miDebuggerArgs": "-q -ex quit; wait() { fg >/dev/null; }; /bin/gdb -q --interpreter=mi",
33
// "miDebuggerPath": "F:/msys/mingw64/bin/gdb.exe",
34
"setupCommands": [
35
{
36
"description": "Enable pretty-printing for gdb",
37
"text": "-enable-pretty-printing",
38
"ignoreFailures": true
39
}
40
],
41
"preLaunchTask": "build",
42
}
43
]
44
}

  ​.vscode/task.json​:编译任务,决定如何编译项目

1
{
2
// See https://go.microsoft.com/fwlink/?LinkId=733558
3
// for the documentation about the tasks.json format
4
"version": "2.0.0",
5
"tasks": [
6
{
7
"label": "build",
8
"type": "shell",
9
// 手动运行一次:./configure --enable-debug --target-list=riscv32-softmmu --disable-werror
10
"command": "cd build; ninja; cd ..",
11
"problemMatcher": [],
12
"group": {
13
"kind": "build",
14
"isDefault": true
15
}
3 collapsed lines
16
}
17
]
18
}

  ‍

本文标题:QEMU开发环境搭建
文章作者:Lyndra
发布时间:2024-10-30
总访问量
总访客数人次
Copyright 2025
站点地图