2023开源操作系统训练营三阶段 proj2 练习1

练习实验书: https://scpointer.github.io/rcore2oscomp/docs/lab1/intro.html

1.编程作业

1.1 扩展 easy-fs-fuse

跟随前面文档的指引,扩展 easy-fs-fuse,使得它可以生成同时包含 Rust 和 C 用户程序的镜像

  1. 修改内核代码
    此步骤参考任务书
  2. 修改Makefile
    根据项目文档的说明, 基本上完成了生成同时包含 Rust 和 C 用户程序的镜像的需求, 但Makefile需要进行一定的修改
    此处我基于ch8的代码进行修改
    1
    2
    3
    4
    5
    6
    7
    8
    FS_IMG := target/fs.img
    ...
    fs-img: $(APPS)
    @make -C ../user build TEST=$(TEST) CHAPTER=$(CHAPTER) BASE=$(BASE)
    @make -C ../testcases build
    @rm -f $(FS_IMG)
    @cp ../user/build/elf/* ../testcases/build/
    @cd ../easy-fs-fuse && cargo run --release -- -s ../testcases/build -o ../os/$(FS_IMG)
    主要区别在于, 此次构建fs-img之前需要先编译testcases目录下的c程序, 并将user目录下的elf文件复制到testcases/build/下, 根据testcases/build/生成镜像时需要将任务书中的-t参数替换为-o参数
  3. 结果和测试
    os下执行make fs-img CHAPTER=8 BASE=2, 可以生成包含c程序的文件系统镜像。
    同理,os下执行make run CHAPTER=8 BASE=2后运行42hello程序:
    • 42运行结果
      1
      2
      >> 42
      Shell: Process 2 exited with code 42
      可以看出42返回值为42, 这与期望值相符合
    • hello运行结果
      1
      2
      3
      4
      5
      6
      >> hello xwd
      Incorrect argc
      Shell: Process 2 exited with code 1
      >> hello
      Incorrect argc
      Shell: Process 2 exited with code 1
      可以看出, hello程序无论是否添加参数, 都会输出错误信息, 这也是接下来要解决的问题。

1.2 修改os内核支持C程序

在 usershell 里运行了 42 和 hello 两个用户程序。42 的运行结果是符合预期的,但 hello 的结果看起来不太对,你的任务是修改内核,使得 hello 测例给出正常输出(即给出一行以 my name is 开头的输出,且 exit_code为0)。

  1. 原因分析
    阅读原rCore文档与本实验文档
    可以看出, 二者的栈分布是不同的, 先看旧的栈内存分布
    img

    可以看到在此栈的内存分布中, 实际的参数放在接近栈底的位置, 其指向的实际参数在靠近栈顶的位置, 并且通过阅读源码process.rs中的exec:
    1
    2
    trap_cx.x[10] = args.len();
    trap_cx.x[11] = argv_base;
    可知参数个数argc时通过手动计算args的长度计算得到的, 并没有存储在栈上。
    阅读本实验指导书可知,C程序的栈内存分布如下:
    img

    在此栈的内存分布中, 实际的参数放在接近栈顶的位置, 其指向的实际参数在靠近栈底的位置, 并且栈指针指向了argc
    而且由testcases/lib/main.c可知:
    1
    2
    3
    4
    5
    6
    7
    8
    int __start_main(long *p)
    {
    int argc = p[0];
    char **argv = (void *)(p+1);

    exit(main(argc, argv));
    return 0;
    }
    p是传递的栈指针sp, 因此argc会被置为栈指针当前指向的数据, 而根据前述分析可知, argc是存放于a0寄存器的, 因此后续使用argc时报错, 这是栈分布于C程序标准不一致导致的。
  2. 修改os思路
    因此,需要将os中初始化栈分布相关的代码进行修改, 使之符合C语言的约定, 实际上我们要修改的代码位于:os/src/task/process.rs中的pub fn exec(self: &Arc<Self>, elf_data: &[u8], args: Vec<String>)
    修改内存分布为上图的形式, 次过程不难, 具体代码实现可参考: 我的实现
  3. 小bug修改: 去除文件名
    观察hello.c源码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    int main(int argc, char *argv[]) {
    char greeting[11] = "my name is ";
    char error[15] = "Incorrect argc\n";
    if (argc != 1) {
    write(1, error, 15);
    return 1;
    }
    int len = 0;
    while(argv[0][len] != 0) {
    len++;
    }
    write(1, greeting, 11);
    write(1, argv[0], len);
    return 0;
    }
    可知其argv[0]应当为命令行输入的参数, 而非约定中的程序名, 因此需要在系统调用中对args中去除第一个函数名参数:
    具体代码实现可参考: 我的实现
  4. 运行结果
    再次执行make run CHAPTER=8 BASE=2后运行hello程序
    1
    2
    3
    Rust user shell
    >> hello xwd
    my name is xwdShell: Process 2 exited with code 0
    结果截图

2 问答作业

  1. elf 文件和 bin 文件有什么区别?
    elf是包含符号信息的二进制文件, bin文件是剥离了二进制信息的符号文件
    以下是各个命令的输出

    1
    2
    3
    4
    $ file elf/ch6_file0.elf
    elf/ch6_file0.elf: ELF 64-bit LSB executable, UCB RISC-V, RVC, double-float ABI, version 1 (SYSV), statically linked, stripped
    $ file bin/ch6_file0.bin
    bin/ch6_file0.bin: data

    elf文件包含的信息有:

    • 类型: ELF 64-bit LSB executable
    • RISC-V: 表示这是一个 RISC-V 架构的可执行文件。
    • RVC: 表示该文件使用了 RISC-V 的压缩指令集。
    • double-float ABI: 表示该文件使用了双精度浮点数 ABI(Application Binary Interface)。
    • 版本 1 (SYSV): 表示该 ELF 文件采用了 SYSV 版本 1 的格式。
    • 静态链接: 表示该文件是静态链接的,即所有的库和依赖在编译时就被链接进来了。
    • stripped: 表示该文件已经被剥离了符号信息。
      bin文件包含的信息有:
    • “data” 表示这是一个二进制数据文件

    总而言之, elf包含了程序的代码、数据、和用于指示操作系统如何运行程序的元数据, bin是纯二进制格式的文件,不包含元数据

    因此riscv64-linux-musl-objdump -ld ch6_file0.bin > debug.S命令会报错:

    1
    2
    $ riscv64-linux-musl-objdump -ld ch6_file0.bin > debug.S
    riscv64-linux-musl-objdump: ch6_file0.bin: file format not recognized

    riscv64-linux-musl-objdump -ld ch6_file0.elf > debug.S可以得到反汇编文件debug.S