自己在前人的基础上构建一个小而全的OS,该OS包含虚存管理、进程管理、处理器调度、同步互斥、进程间通信、文件系统等主要内核功能,总的内核代码量(C+asm)不会超过5K行。
我们在构建或调试操作系统时,为了方便可以使用CPU硬件模拟器如QEMU、BOCHS等软件来进行模拟操作。
对操作系统的调试本文推荐使用GDB(配合qemu)来进行调试和分析。
Ubuntu下安装qemu:
• sudo apt-get install qemu # install QEMU
• sudo ln -s /usr/bin/qemu-system-i386 /usr/bin/qemu
如果使用的是默认的安装路径,那么在 “/usr/local/bin”下面即可看到安装结果:
qemu-system-i386 qemu-img qemu-nbd ……
建立符号链接文件qemu
sudo ln –s/usr/local/bin/qemu-system-i386 /usr/local/bin/qemu
比如在实验中,例如 lab1,可能用到的命令如:
qemu -hda ucore.img -parallel stdio # 让ucore在qemu模拟的x86硬件环境中执行
或
qemu -S -s -hda ucore.img -monitor stdio # 用于与gdb配合进行源码调试
或
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrdrootfs.img -s -S # 关于-s和-S选项的说明:
# -S freeze CPU at startup (use ’c’ to start execution)
# -s shorthand for -gdb tcp::1234 若不想使用1234端口,则可以使用-gdb tcp:xxxx来取代-s选项
其中qemu使用的参数说明:
`-hda file' `-hdb file' `-hdc file' `-hdd file'
使用 file 作为硬盘0、1、2、3镜像。
`-fda file' `-fdb file'
使用 file 作为软盘镜像,可以使用 /dev/fd0 作为 file 来使用主机软盘。
`-cdrom file'
使用 file 作为光盘镜像,可以使用 /dev/cdrom 作为 file 来使用主机 cd-rom。
`-boot [a|c|d]'
从软盘(a)、光盘(c)、硬盘启动(d),默认硬盘启动。
`-snapshot'
写入临时文件而不写回磁盘镜像,可以使用 C-a s 来强制写回。
`-m megs'
设置虚拟内存为 msg M字节,默认为128M 字节。
`-smp n'
设置为有 n 个 CPU 的 SMP 系统。以 PC 为目标机,最多支持 255 个 CPU。
`-nographic'
禁止使用图形输出。
其他:
可用的主机设备 dev 例如:
vc
虚拟终端。
null
空设备
/dev/XXX
使用主机的 tty。
file: filename
将输出写入到文件 filename 中。
stdio
标准输入/输出。
pipe:pipename
命令管道 pipename。
等。
使用 dev 设备的命令如:
`-serial dev'
重定向虚拟串口到主机设备 dev 中。
`-parallel dev'
重定向虚拟并口到主机设备 dev 中。
`-monitor dev'
重定向 monitor 到主机设备 dev 中。
`-s'
等待 gdb 连接到端口 1234。
`-p port'
改变 gdb 连接端口到 port。
`-S'
在启动时不启动 CPU, 需要在monitor 中输入 'c',才能让qemu继续模拟工作。
`-d' 输出日志到 qemu.log 文件。
然后打开GDB远程调试,另外开启一个终端输入gdb:
(gdb)file linux-3.18.6/vmlinux # 在gdb界面中targe remote之前加载符号表
(gdb)target remote:1234 # 建立gdb和gdbserver之间的连接,按c 让qemu上的Linux继续运行
(gdb)break start_kernel # 断点的设置可以在target remote之前,也可以在之后
生成.img文件
1) # create ucore.img
170 UCOREIMG := $(call totarget,ucore.img)
171
172 $(UCOREIMG):$(kernel) $(bootblock)
173 $(V)dd if=/dev/zero of=$@ count=10000
174 $(V)dd if=$(bootblock) of=$@conv=notrunc
175 $(V)dd if=$(kernel) of=$@ seek=1conv=notrunc
176
177 $(callcreate_target,ucore.img)
2) cp ../menu/init ./
find . | cpio -o -Hnewc|gzip -9 > ../rootfs.img
开始进行GDB调试:
为了与qemu配合进行源代码级别的调试,需要先让qemu进入等待gdb调试器的接入并且还不能让qemu中的CPU执行,因此启动qemu的时候,我们需要使用参数-S –s这两个参数来做到这一点。在使用了前面提到的参数启动qemu之后,qemu中的CPU并不会马上开始执行,这时我们启动gdb,然后在gdb命令行界面下,使用下面的命令连接到qemu:
(gdb) target remote 127.0.0.1:1234
然后输入c(也就是continue)命令之后,qemu会继续执行下去,但是gdb由于不知道任何符号信息,并且也没有下断点,是不能进行源码级的调试的。为了让gdb获知符号信息,需要指定调试目标文件,gdb中使用file命令:
(gdb) file ./bin/kernel
之后gdb就会载入这个文件中的符号信息了。
通过gdb可以对ucore代码进行调试,以调试memset函数为例:
(1) 运行 `qemu -S -s -hda ./bin/ucore.img -monitorstdio`
(2) 运行 gdb并与qemu进行连接
(3) 设置断点并执行
(4) qemu 单步调试。
运行过程以及结果如下:
<table>
<tr><td>窗口一</td><td>窗口二</td>
<tr>
<td>
chy@laptop: ~/lab1$ qemu -S -s -hda ./bin/ucore.img
</td>
<td>
chy@laptop: ~/lab1$ gdb ./bin/kernel<br>
(gdb) target remote:1234<br>
Remote debugging using :1234<br>
0x0000fff0 in ?? ()<br>
(gdb) file obj/kernel/kernel.elf<br>
(gdb) break memset<br>
Breakpoint 1 at 0x100d9f: file libs/string.c, line 54. <br>
(gdb) run<br>
Starting program: /home/chenyu/oscourse/develop/ucore/lab1/bin/kernel <br>
<br>
Breakpoint 1, memset (s=0x1020fc, c=0 '\000', n=12) at libs/string.c:54<br>
54 return __memset(s, c, n); <br>
(gdb)
</td>
</tr></table>