X2W-OS Make Targets#

X2W-OS使用 make来管理项目。具体来说,X2W-OSMakefile中所有的目标可以分为三类:

  • 编译相关目标:编译内核、编译 SBI、编译文档

  • 运行调试相关目标:使用 QEMU运行 X2W-OS、使用 GDBVSCode调试 X2W-OS

  • Miscellaneous目标:具有多种用途的目标,例如杀死所有 QEMU进程、启动 HttpServer阅读文档……

下面将分别介绍 X2W-OS中这三类目标。

关于 Makefile的源代码,请参考 X2W-OS项目根目录下的Makefile文件

编译相关目标#

X2W-OSMakefile中分别提供了编译源文件、内核、SBI、文档的目标

编译源文件:obj#

Makfile中定义了 obj目标,该目标将会编译内核和 SBI的源文件、生成目标文件

make obj

编译得到的目标文件和生成得到的内核将输出至 build文件夹下

注意:

  • 该目标通常用于检查内核和 SBI的目标文件

$ make obj
mkdir -p /Users/jack/project/OSKernel2023-X2W/build/disasms
mkdir -p /Users/jack/project/OSKernel2023-X2W/build/temps
mkdir -p  /Users/jack/project/OSKernel2023-X2W/build/lib  /Users/jack/project/OSKernel2023-X2W/build/lib/kernel  /Users/jack/project/OSKernel2023-X2W/build/lib/user  /Users/jack/project/OSKernel2023-X2W/build/kernel  /Users/jack/project/OSKernel2023-X2W/build/device
mkdir -p  /Users/jack/project/OSKernel2023-X2W/build/sbi
/* -------------------------- 编译内核和SBI -------------------------- */
当前内核目标文件:
	build/kernel/boot.o
	build/lib/stdfmt.o
	build/lib/stdlib.o
	build/lib/string.o
	build/lib/kernel/kstdio.o
	build/lib/user/ustdio.o
	build/kernel/init.o
	build/kernel/main.o
	build/kernel/test.o
	build/device/uart.o
-----------------------------------------------------------------------
当前SBI目标文件:
	build/sbi/sbi_boot.o
	build/sbi/sbi_main.o
-----------------------------------------------------------------------

编译内核:kernel#

Makfile中定义了 kernel目标,该目标将会编译内核源文件、生成目标文件,最后通过 kernel/kernel.ld链接脚本指导链接器链接得到内核

make kernel

编译得到的目标文件和生成得到的内核将输出至 build文件夹下

注意:

  • build文件夹下默认生成的 ELF格式内核,文件名为 os.elf

  • 为了方便后续的反汇编,kernel目标还会生成内核的裸二进制文件 os.bin

  • 为了方便提交测试,kernel目标会将 os.elf复制到根目录下,并将其重命名为 ${KNAME}

KNAME变量定义在 Makefile中,修改该变量的值即可控制最终在根目录下的内核文件名

# 编译生成的内核的名字,将会得到 <path-to-X2W-OS>/kernel.elf
KNAME := kernel.elf

编译SBI:sbi#

Makfile中定义了 sbi目标,该目标将会编译 SBI源文件、生成目标文件,最后通过 sbi/sbi.ld链接脚本指导链接器链接得到 SBI

make sbi

编译得到的目标文件和生成得到的 SBI将输出至 build文件夹下

注意:

  • build文件夹下默认生成的 ELF格式 SBI,文件名为 sbi.elf

  • 为了方便后续的反汇编,sbi目标还会生成 SBI的裸二进制文件 os.bin

  • 为了方便提交测试,kernel目标会将 sbi.elf复制到根目录下,并将其重命名为 ${SNAME}

SNAME变量定义在 Makefile中,修改该变量的值即可控制最终在根目录下的 SBI文件名

# 编译生成的内核的名字,将会得到 <path-to-X2W-OS>/sbi.elf
SNAME := sbi.elf

编译文档:documentation#

Makfile中定义了 documentation目标,该目标将会通过 DoxygenSphinx编译文档系统

make documentation

编译得到的 Doxygen文档和 Sphinx文档将分别输出至:

  • <path-to-X2W-OS>/docs/doxygen/build

  • <path-to-X2W-OS>/docs/sphinx/build

注意:

  • Sphinx将会输出 html格式的文档

  • Doxygen将会输出 htmllatexxml三种格式的文档,你可以修改 <path-to-X2W-OS>/docs/doxygen/Doxyfile来改变这一行为

  • 你可以使用 pdflatex等其它工具生成诸如 PDF等其他格式的文档

关于如何在本地阅读 HTML格式的文档,请参考

编译内核和SBI:all#

Makfile中定义了 all目标,该目标将会:

  • 编译内核

  • 编译 SBI

make all

编译内核、SBI和文档:full#

Makfile中定义了 full目标,该目标将会:

  • 编译内核

  • 编译 SBI

  • 编译文档

make full

运行和调试相关目标#

X2W-OSMakefile中提供了直接运行内核、使用 GDB调试内核、使用 VSCode调试内核的目标

直接运行内核:run#

Makefile提供了 run目标,该目标将启动 QEMU运行内核。QEMU的输出信息将输出在终端上。

make run

警告:

  • 当前仅在 Ubuntu上进行了测试,其他类 Unix/Linux平台没有进行测试

  • 无法在 MacOS上运行,因为:

    • 通过 brew安装的 qemu-system-riscv64没有通过所有测试,无法将诸如 mret等指令翻译为本地指令(JIT

    • 通过 port安装依赖库后从源码编译 QEMU-7.2.0时会因为导入头文件问题报错

GDB调试:debug-gdb#

QEMU支持通过 GDB连接到 QEMU中进行调试,因此 Makefile提供了 debug-gdb目标,以通过 GDB连接 QEMU进行调试。

make debug-gdb

运行该命令后,GDB将在前台运行,而 QEMU将在后台运行。退出时首先按下 Ctrl+C以终止 QEMU,而后输入 quit后按下回车以退出 GDB

注意:

  • Makefile将搜索 gdb-multiarch或者 riscv64-unknown-elf-gdb,你可以通过包管理安装或者手动编译安装

  • 如果 Makefile无法找到两个 GDB中的任意一个,那么就会报错,而后终止运行

VSCode调试:debug-vscode#

VSCode内嵌入了 UI形式的 GDB。因此诸如函数调用栈、全局变量、寄存器等调试信息非常易于观察,因此 Makefile提供了 debug-vscode实现以通过 VSCode连接 QEMU进行调试

make debug-vscode

此时,QEMU将在前台运行,QEMU的输出信息将输出到屏幕上,并将 GDB端口暴露到本机的 1234端口。

接下来,按照下述步骤配置 VSCode,以能够在 VSCode中调试:

  • 在项目根目录中的 .vscode文件夹下的 launch.json文件的 configuration项中添加如下配置:

    {
        "name": "GDB Debug",
        "type": "cppdbg",
        "request": "launch",
        "program": "${workspaceFolder}/build/os.elf",
        "externalConsole": false,
        "stopAtConnect": true,
        "MIMode": "gdb",
        "miDebuggerArgs": "<本机gdb-multiarch或者riscv64-unknown-elf-gcc的绝对路径",
        "miDebuggerServerAddress": "<本机IP地址>:1234",
        "miDebuggerPath": "<本机gdb-multiarch或者riscv64-unknown-elf-gcc的绝对路径>",
        "cwd": "${workspaceFolder}",
    }
    

    注意:

    • GDB的绝对路径可以通过which命令获得

    • <本机IP地址>可以通过运行ifconfig命令查看

    • <本机IP地址>最好写成本机在局域网下的IP地址,不要写成本地回环地址。例如192.168.0.16,而非127.0.0.1

      • 因为GDB Server可能运行在同一局域网下另外的机器,而VSCode运行在当前机器上

      • <本机IP地址>的值设定为127.0.0.1,则VSCode会尝试访问当前机器上的GDB Server(事实上并没有运行),而非同一局域网下的另外的机器

      • 此时就无法成功运行

  • VSCode的侧边栏中的 运行和调试选项卡下选择 GDB Debug调试配置

  • 按下 F5启动 VSCode中的 GDB,即可连接到本机的 1234端口开始调试

Miscellaneous目标#

X2W-OSMakefile中提供了其他诸多目标以帮助更好的进行开发

反汇编:disasm#

Makfile中定义了 disams目标,该目标将会对 ELF格式的内核和 SBI进行反汇编,从而得到符号表、指令序列以及裸二进制内容。

make disasm

得到的反汇编内容将输出至 build/disasms文件夹下,其中:

  • 反汇编指令序列为:build/disasms/{os,sbi}.disasm

  • 内核符号表为:build/disasms/{os,sbi}.symbol

  • 二进制内容为:build/disasms/{os,sbi}.machine

阅读文档:read#

为了提升文档阅读体验,Makefile中定义了 read目标,该目标将在本地启动 HttpServer来阅读 Sphinx文档。

make read

将会在后台运行 HttpServer,端口号每次运行时随机指定,按下 Ctrl + C即可终止 HttpServer

注意:

  • 目前 Makefile仅支持运行 PythonHttpServer,如果使用诸如 node.jsHttpServer,请自行添加

  • Makefile对Python版本差异进行了屏蔽:

    • Python2中的 HttpServerSimpleHTTPServer提供

    • Python3中的 HttpServerhttp.server提供

q此外,make read默认会阻塞终端,你也可以使用如下命令来避免终端被阻塞。

make read &

注意,PythonHttpServer的日志信息不会被重定向,因此会依旧输出到终端上,需要用户手动进行重定向。

杀死遗留QEMU、HttpServer进程:kill#

很多时候可能会导致存在遗留的进程:

  • 在使用 GDB调试的时候,可能因为错误的退出方式,导致 QEMU进程没有关闭

  • 与此类似,可能有时候把 HttpServer进程挂到后台运行忘记关闭

  • ……

因此 Makefile提供了 kill命令来杀死遗留的 QEMUHttpServerGDB进程

make kill

清除中间文件:clean#

Makefile提供了 clean目标以清除编译过程中产生的所有的文件

make clean

将会清空 build文件夹和 docs文件夹中的文档。