【99%环境搭建系列】xv6-riscv内核调试教程
本文最后更新于:2023年12月17日 下午
前言
最近在上《操作系统高级课程》,做的实验是MIT6.828的课程实验,在一个简易操作系统内核XV6上进行调试开发。后续我也会写一些介绍操作系统的文章。
关于XV6,这是MIT开发的一个教学目的的操作系统。XV6是在x86处理器上(x即指x86)用ANSI标准C重新实现的Unix第六版(Unix V6,通常直接被称为V6)。并且MIT6.828这门关于操作系统的课程也是广受好评的,也推荐大家去学习。
这里再贴出一份相关的资源清单(不要再说找不到学习资源了而不学了~):
- 课程网站:https://pdos.csail.mit.edu/6.828/2020
- xv6 官网安装教程:https://pdos.csail.mit.edu/6.828/2020/tools.html
- 北大课程提示:https://github.com/FrankZn/xv6-pku-hints
- xv6-book中文版:https://github.com/FrankZn/xv6-riscv-book-Chinese
- 中文字幕视频:6.S081 / Fall 2020 麻省理工操作系统 - 2020 年秋季
- 实验代码参考:https://home.cnblogs.com/u/weijunji/
回归主题,这次文章的来由就是实现虚拟机上实现gdb调试xv6。一开始因为只下载了内核的代码,所以只能进行编译运行,使用自带的gdb出现各种问题,后来发现是没有安装相应的编译工具。但是实际安装起来却花费了我好大一番功夫,所以特此记录,帮助后来人减少弯路。
再提一下我的机器环境:
- 宿主机操作系统:Win10
- 虚拟机:Ubuntu20.04
追记
后来经过指点,发现根本不需要下面riscv-gnu-toolchain 安装这一步骤,可以跳过那部分(其实那部分操作相当于是自己手动进行编译了,但实际上只需要用到编译好的工具程序即可)。
话不多说,直接上教程。
- 首先可以根据官网教程安装,也就是执行下面这条命令,安装相关工具:
1 |
|
- 然后再克隆实验代码
1 |
|
- 切换分支,并进行编译
1 |
|
如果出现类似下面的界面,则表明安装成功
- 按ctrl + a + x 退出内核运行,在启动make qemu-gdb
1 |
|
- 然后打开另一个中断窗口,直接执行gdb的话,会出现下面的提示
- 按照提示,在
~/.gdbinit
文件中加入set auto-load safe-path /
- 然后执行
gdb-multiarch
,看到出现类似下面的界面,就表示成功了,接下里就是愉快的调试时间了,可以参考下文的GDB调试内容。
riscv-gnu-toolchain 安装
根据官网上的教程安装,发现虚拟机连不上外网(这里还尝试一番功夫,没能成功),所以看到这篇文章,riscv-gnu-toolchain工具链下载安装_roockiet的博客[1]
使用gitee上的镜像进行下载,需要执行的命令大体如下:
1 |
|
注意这个riscv-gnu-toolchain所有的仓库都克隆下来后,并且直接执行make安装的话,耗时较久,估计有1~2小时,最后大约有18G(我是安装在了/opt/bin目录下),所以一定要确保对应分区的容量足够,不然就会因为容量不够而写失败(都是血淋淋的教训啊,看到下图根目录的挂载点了吗,之前就是因为安装在根目录对应的分区下,导致容量空间直接满了。。)。
如果为了节省空间和时间,可以只尝试下面的配置方式,只安装riscv64-unknown-elf-gcc,参考这篇文章,riscv各种版本gcc工具链编译与安装_weiqi7777的博客-CSDN博客_riscv编译器[2]
因为后面调试时只用到了riscv64-unknown-elf-gdb,所以我猜测只需要安装对应gcc工具即可,不过我自已没有尝试过可不可行,所以还是交由各位看官自行决定。
1 |
|
GDB调试
- 首先在一个窗口上执行:
1 |
|
- 然后打开另一个窗口,执行:
1 |
|
- 然后可以看到进入了gdb界面,接着远程连接上一个窗口的gdb,执行下面的命令:
target remote localhost:26000
(这里的26000填上一个窗口的端口号)
- 连接成功之后,显示如下:
- 然后把自己编译好的用户程序加载进来,也就是user目录下的文件
file user/_find
- 然后设置好断点,就可以按下 c 让程序运行到断点处,再返回上个窗口,执行对应命令,就可以看到程序暂停住了,返回gdb调试程序窗口,也能看到当前暂停的地方就是刚刚设置的断点处。
- 之后就是痛苦地愉快地调试bug啦~有关gdb调试技巧,还可以参考我之前写的这篇文章,GDB调试学习。
踩坑记录
这次安装过程中遇到两个坑,一个是在克隆仓库的时候,虚拟机无法访问外网,一个是安装riscv编译工具时,分区的容量不够了,这里就简单地做个记录。
虚拟机网络配置
一开始的需求是实现虚拟机共享主机的VPN,也能够访问外网,参考了这篇文章,Ubuntu虚拟机共享主机VPN(适用于NAT或桥接)[3]
尝试之后,报错代理服务器错误,正常的国内网页无法访问了。因为这个时候我用的是NAT模式,所以就想要尝试用桥接模式再试一下。
参考这里的视频教程 VMware网络选择和ubuntu 20_04 网络配置教程[4],
配置静态IP的方式,我之前也提到过【99%环境搭建系列】云计算管理平台Devstack安装,后来虽然国内网络能够正常访问了,但是外网还是不行。
后来尝试很久还是没能成功,最后也就作罢了。既然外网访问不了,那就找国内镜像吧,幸运的是这条路走通了。
最后没有实现需求,我想可能还是自己对网络的了解还是太粗浅了,日后一定要再恶补一下。不过借此机会,也了解学习了下VPN、桥接模式、NAT模式的概念,这里做个简单总结吧。
VPN:Vitrual Private Network,简单来说就是通信双方之间的数据进行了加密。更多内容参考 VPN概念,技术原理和误区_哔哩哔哩_bilibili[5]
桥接模式:虚拟机使用宿主机的真实网卡,宿主机和虚拟机在相同的公网IP段下。
NAT模式:虚拟机和宿主机使用同一个虚拟网卡,从而实现在同一个局域网网段下,比如192.168.17.0 / 24。
ubuntu添加磁盘分区
还有一个坑就是在编译安装的时候,磁盘分区容量不够了。所以这里在演示一下如何给linux虚拟机添加磁盘分区。
- 首先在VMware -> 虚拟机选项 -> 管理,选择扩展,添加想要增加的分区容量。(注意需要关闭虚拟机进行设置)
- 此时是还无法使用新增容量的,需要在虚拟机上添加分区,并挂载某一目录。那么首先登陆虚拟机,添加分区。
1 |
|
添加好分区之后,就是格式化
sudo mkfs.ext4 /dev/sda3
(设备名换成刚刚新增的分区)然后就是挂载目录
sudo mount /dev/sda3 /opt/bin
(选择自己需要挂载的目录)使用
df -h
可以看是否挂载成功。挂载成功之后就可以使用该分区了。上面的挂载,每次重启后都需要手动重新设置,所以这里再实现开机自动挂载,其实就是往配置文件
/etc/fstab
里写配置,格式如下:
1 |
|
总结
至此,折腾了我快一星期,总计耗时约10小时的调试环境安装之旅终于落下了帷幕。此时此刻我的心情是复杂的,一方面是为自己的无能而流泪(一个简单的调试环境搭建弄了这么久),兜兜转转这么久,也还只是完成了调试前的准备工作,查bug还在后头呢。另一方面也是一种释然,至少我没有半途而废,虽然过程坎坷,但我终究还是来到了终点。或许一开始我可以尝试用print输出来调试,但是我知道这种调试方式终究走不远,因为真正出问题的时候,是没有办法通过实时输出来进行调试的。所以这次使用gdb来调试内核,是一个千载难逢的机会,我不该错过。
缓慢的、持久的、有纪律的努力,最终会导致令人难以置信的结果。
每当我无法忍受日常生活时,我就提醒自己,没有什么比每天坚持做下去更重要了。虽然我很难看到未来会怎样,但我知道持久性具有强大的威力,就像一句古老格言说的“继续去做”(keep doing)。
——《大海教给我的》