【记录贴】csdiy 操作系统 MIT 6.S081

本贴仅记录笔者学习过程与经验分享,非教程。
csdiy是一个北大老哥整理的一套非常完整的cs自学体系。近期突然想重新看一下这部分内容以补齐本科欠的债…

笔者使用操作系统为fedora39.

笔者的课后Lab链接GitHub - UncleBigLu/xv6-labs-2021: For myself learning xv6 and os

Github上也有个老哥写了课后lab,(也许?)可以参考 GitHub - NebulorDang/xv6-lab-2021: Code record for MIT 6.S081 xv6-riscv lab 2021, some differences refer to the repository "xv6-lab-2020" https://github.com/NebulorDang/xv6-lab-2020

Lab1

QEMU运行XV6.
按照教程走,缺啥补啥。
运行make qemu, 报错

ser/sh.c: In function ‘runcmd’:
user/sh.c:58:1: error: infinite recursion detected [-Werror=infinite-recursion]
   58 | runcmd(struct cmd *cmd)
      | ^~~~~~
user/sh.c:89:5: note: recursive call
   89 |     runcmd(rcmd->cmd);
      |     ^~~~~~~~~~~~~~~~~
user/sh.c:109:7: note: recursive call
  109 |       runcmd(pcmd->left);
      |       ^~~~~~~~~~~~~~~~~~
user/sh.c:116:7: note: recursive call
  116 |       runcmd(pcmd->right);
      |       ^~~~~~~~~~~~~~~~~~~
user/sh.c:95:7: note: recursive call
   95 |       runcmd(lcmd->left);
      |       ^~~~~~~~~~~~~~~~~~
user/sh.c:97:5: note: recursive call
   97 |     runcmd(lcmd->right);
      |     ^~~~~~~~~~~~~~~~~~~
user/sh.c:127:7: note: recursive call
  127 |       runcmd(bcmd->cmd);
      |       ^~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors
make: *** [<builtin>: user/sh.o] Error 1

根据该issue尝试解决。补齐依赖后即可进入xv6 shell.

[unclebiglu@ublvlc xv6-labs-2021]$ make qemu
qemu-system-riscv64 -machine virt -bios none -kernel kernel/kernel -m 128M -smp 3 -nographic -drive file=fs.img,if=none,format=raw,id=x0 -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0

xv6 kernel is booting

hart 1 starting
hart 2 starting
init: starting sh
$ ls
.              1 1 1024
..             1 1 1024
README         2 2 2226
xargstest.sh   2 3 93
cat            2 4 24208
echo           2 5 23056
forktest       2 6 13512
grep           2 7 27416
init           2 8 23808
kill           2 9 22960
ln             2 10 22816
ls             2 11 26544
mkdir          2 12 23064
rm             2 13 23056
sh             2 14 41256
stressfs       2 15 24040
usertests      2 16 151768
grind          2 17 38104
wc             2 18 25200
zombie         2 19 22312
console        3 20 0

刚好也准备学6.S081!!!持续追更!!

wow , 更进更进! 在此帖子后面更进出一些RISCV 特权架构级别的东西,和 XV6 的源码解析

lab1题目难度并不算很高,参考user/下的其它程序基本都可以搞定(虽然但是,之前代码写少了,还是写着恼火…)
运行./grade-lab-util <name>即可测试自己的程序有没有写对

Lab2

尝试按照老师的操作使用gdb调试xv6. 我没有在找到fedora下可以直接用的rv64 gdb, 因此选择从源码编译。

根据readme 编译即可。
没有看明白他的newLib版本和linux版本有什么区别…有懂的佬可以指点一下吗
之后即尝试进入gdb环境。首先启动qemu gdb

cd xv6-labs-2021
make CPUS=1 qemu-gdb

之后在另一个窗口中运行gdb
./bin/riscv64-unknown-linux-gnu-gdb

xv6-labs-2021]$ ../../../../riscv-gnu-toolchain/build/bin/riscv64-unknown-linux-gnu-gdb
GNU gdb (GDB) 14.1
Copyright (C) 2023 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "--host=x86_64-pc-linux-gnu --target=riscv64-unknown-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word".
warning: File "/home/unclebiglu/Documents/repostories/csdiy/s081os/lab1/xv6-labs-2021/.gdbinit" auto-loading has been declined by your `auto-load safe-path' set to "$debugdir:$datadir/auto-load".
To enable execution of this file add
        add-auto-load-safe-path /home/unclebiglu/Documents/repostories/csdiy/s081os/lab1/xv6-labs-2021/.gdbinit
line to your configuration file "/home/unclebiglu/.config/gdb/gdbinit".
To completely disable this security protection add
        set auto-load safe-path /
line to your configuration file "/home/unclebiglu/.config/gdb/gdbinit".
For more information about this security protection see the
"Auto-loading safe path" section in the GDB manual.  E.g., run from the shell:
        info "(gdb)Auto-loading safe path"
(gdb) set confirm off
(gdb) set architecture riscv:rv64
The target architecture is set to "riscv:rv64".
(gdb) target remote 127.0.0.1:26000
Remote debugging using 127.0.0.1:26000
warning: No executable has been specified and target does not support
determining executable automatically.  Try using the "file" command.
0x0000000000001000 in ?? ()
(gdb) symbol-file kernel/kernel
Reading symbols from kernel/kernel...
(gdb) set disassemble-next-line auto
(gdb) set riscv use-compressed-breakpoints yes
(gdb) b _entry
Breakpoint 1 at 0x8000000a

xv6仓库下面有一个.gdbinit的文件给了如何进行gdb的指令,我这里直接一条一条手动执行了…应该也有方法自动读这个文件。之后即可设置断点进行调试。


发现编译的gdb不支持tui,但是找到了个方便的脚本用python提供一些功能

─── Output/messages ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────

Breakpoint 1, 0x000000008000000a in _entry ()
=> 0x000000008000000a <_entry+10>:      f14025f3                csrr    a1,mhartid
─── Assembly ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
!0x000000008000000a  ? csrr     a1,mhartid
 0x000000008000000e  ? addi     a1,a1,1
 0x0000000080000010  ? mul      a0,a0,a1
 0x0000000080000014  ? add      sp,sp,a0
 0x0000000080000016  ? jal      0x8000574e <start>
 0x000000008000001a  ? j        0x8000001a <spin>
 0x000000008000001c  ? addi     sp,sp,-32
 0x000000008000001e  ? sd       ra,24(sp)
 0x0000000080000020  ? sd       s0,16(sp)
 0x0000000080000022  ? sd       s1,8(sp)
─── Breakpoints ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
[1] break at 0x000000008000000a for _entry hit 1 time
─── Expressions ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
─── History ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
─── Memory ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
─── Registers ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
    zero 0x0000000000000000     ra 0x0000000000000000     sp 0x0000000080021140    gp 0x0000000000000000    tp 0x0000000000000000
      t0 0x0000000080000000     t1 0x0000000000000000     t2 0x0000000000000000    fp 0x0000000000000000    s1 0x0000000000000000
      a0 0x0000000000001000     a1 0x0000000087e00000     a2 0x0000000000001028    a3 0x0000000000000000    a4 0x0000000000000000
      a5 0x0000000000000000     a6 0x0000000000000000     a7 0x0000000000000000    s2 0x0000000000000000    s3 0x0000000000000000
      s4 0x0000000000000000     s5 0x0000000000000000     s6 0x0000000000000000    s7 0x0000000000000000    s8 0x0000000000000000
      s9 0x0000000000000000    s10 0x0000000000000000    s11 0x0000000000000000    t3 0x0000000000000000    t4 0x0000000000000000
      t5 0x0000000000000000     t6 0x0000000000000000     pc 0x000000008000000a
─── Source ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
─── Stack ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
[0] from 0x000000008000000a in _entry
─── Threads ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
[1] id 1 from 0x000000008000000a in _entry
─── Variables ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
>>> 

实验代码部分

Lab2要求增加新的系统调用,然而根本没看明白系统调用是怎么工作的…

首先user/user.h下存放系统调用声明
user/usys.pl 下存放stub
生成的usys.S里面是汇编代码,目测是把SYS_fork一类的system call number送进寄存器之后ecall由内核完成系统调用。
kernel/syscall.h 里面存放的是宏定义的call number.
kernel/sysproc.c下面目测是一部分系统调用源码,fork一类的函数会去调用放在kernel/proc.c里的实现

为什么uint64的返回值可以返回-1…


trace

第一道题做完几天了,抽点时间过来整理一下做题思路。
首先观察题目要求输出格式,<pid> <syscall_name> -> <return value>
然后观察被调用的用户代码user/trace.c, 其接受三个参数,第一个参数是mask, 传递给系统调用trace, 后续全部参数被塞在一个新的*argv[]数组里面然后exec去执行。trace系统调用的功能是把所有被mask标记的系统调用名称及返回值打印出来。

首先为了程序编译通过,系统调用的入口得加上。
修改user/user.huser/usys.plkernel/syscall.h.
syscall.c 里面extern声明的系统调用和函数指针数组里面也记得加上我们写的系统调用。

根据教案提示在/kernel/proc.hproc结构体里塞个新变量存mask.
sys_trace系统调用本体没啥工作,就把用户态传过来的参数存进结构体就行了。
所有的系统调用都会通过syscall()调用。判断一下mask和syscall number打印出来就行。


sysinfo

首先还是该加的定义都加上

按照提示观察sys_fstat()copyout 用法

grep -rnw 'kernel/' -e 'copyout' 全局搜一下函数定义…

第一个参数是进程pagetable, 第二个是目的地址,第三个…这咋把局部变量地址传出去了… 第四个是大小

总之照葫芦画瓢先用着

…全记录下来有点麻烦…思路不复杂,有问题去看git diff…


newlib glibc uclibc musl都是不同的libc实现,和编译器工具链是紧密耦合的,但也会有裸机工具链这里会是none。编译os的时候哪个都可以用,因为不依赖任何libc,但是在已经配置好的os上编译用户态软件的时候需要对应选正确的工具链

1 个赞