书接上回。
5. 为什么 tool/ 下面的工具又能用了?
大概就是做 Lab1 的时候死活用不了,结果准备做 Lab2 的时候代码一点没改突然就能用了。具体为啥我也不知道,可能是因为我在做 Lab2 之前和远程仓库同步了一下导致一些配置改了,然而现在直接在根目录下面 cargo run 又用不了了。
当然这不是问题,我们可以直接 cargo tt -b lab2 看测试结果,但是问题是调试会比较麻烦。这里建议如果某个 case 爆了就直接使用 kprintln! 打印调试信息,然后使用 cargo tt -c <failed-case> -v 查看完整输出。如果需要打开 debug 参数查看原始代码里面给出的调试输出的信息,可以直接在 tool/ 下面的 test.rs 里面修改运行参数。很容易找到需要修改的位置。
总之,只要 tool/ 下面和根目录下面有一个能跑,就不成问题。
6. 虚拟内存的用法
由于我不会 Rust 所以也暂时没完全看懂它的虚拟内存代码,这里就只给出需要的用法了:
- 检查指针是否有效时,需要在页表中提取对应项,并使用
is_valid查看是否有效,如下所示。
1 | pub fn check_valid_va_ptr(ptr: usize) -> bool { |
- 真正使用指针时,不需要进行任何翻译。例如在
read中向用户提供的 buffer 中写入数据可以这么写:
1 | let buf_slice = unsafe { core::slice::from_raw_parts_mut(buffer, size) }; |
对于 Lab2,我们暂时不需要其他更复杂的用法。
7. 参数调用
熟悉虚拟内存的用法之后就很简单。所有的参数都需要加载到用户栈上,包括 argv 的所有字符串,以及指向这些字符串的指针。当然由于 argv 的类型是 char**,所以指向字符串的指针所处的位置必须是连续的,并且从低地址到高地址依次指向 argv[0]、argv[1],以此类推,如下所示:
1 | ↑ High address (stack bottom) |
注意所有指针都要 8 字节对齐。最后根据 calling convention,argc 和 argv 指针要放到寄存器中。注意到 execute 函数中本来就有设置寄存器的代码(设置的是 x[2] 即栈指针),仿照其写即可。需要将 x[10] 设为 argc 并将 x[11] 设置为 argv。
8. 进程相关系统调用
由于 TacOS 没有要求我们做进程内多线程,我们可以巨大偷懒,直接把进程当作线程的一个属性。后面我可能会混用进程/线程,可以认为进程指的就是拥有该进程的线程。
首先我们直接令进程编号等于线程编号,这样我们不需要额外维护任何编号信息。