目录
编程题
第1题
题目要求:实现一个裸机应用程序A,能打印调用栈。
实现
以 rCore tutorial ch2 代码为例,在编译选项中我们已经让编译器对所有函数调用都保存栈指针(参考 os/.cargo/config ),因此我们可以直接从 fp 寄存器追溯调用栈:
os/src/stack_trace.rs
use core::{arch::asm, ptr};
pub unsafe fn print_stack_trace() -> () {
let mut fp: *const usize;
asm!("mv {}, fp", out(reg) fp);
println!("== Begin stack trace ==");
while fp != ptr::null() {
let saved_ra = *fp.sub(1);
let saved_fp = *fp.sub(2);
println!("0x{:016x}, fp = 0x{:016x}", saved_ra, saved_fp);
fp = saved_fp as *const usize;
}
println!("== End stack trace ==");
}
之后我们将其加入 main.rs 作为一个子模块:
加入 os/src/main.rs
// ...
mod syscall;
mod trap;
mod stack_trace;
// ...
作为一个示例,我们可以将打印调用栈的代码加入 panic handler 中,在每次 panic 的时候打印调用栈:
os/src/lang_items.rs
use crate::sbi::shutdown;
use core::panic::PanicInfo;
use crate::stack_trace::print_stack_trace;
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
// ...
unsafe { print_stack_trace(); }
shutdown()
}

现在,进入panic 的时候输入的信息变成了这样:
Panicked at src/batch.rs:68 All applications completed!
== Begin stack trace ==
0x0000000080200e12, fp = 0x0000000080205cf0
0x0000000080201bfa, fp = 0x0000000080205dd0
0x0000000080200308, fp = 0x0000000080205e00
0x0000000080201228, fp = 0x0000000080205e60
0x00000000802005b4, fp = 0x0000000080205ef0
0x0000000080200424, fp = 0x0000000000000000
== End stack trace ==
这里打印的两个数字,第一个是栈帧上保存的返回地址,第二个是保存的上一个 frame pointer。
但在我本地运行时,感觉是由于作者给出的代码不会进入panic状态,所以不会有最后这个trace输出;

但是,我们可以看一下如下路径;
os/src/batch.rs
我们在这个文件中插入下面两条代码;
use crate::stack_trace::print_stack_trace;
unsafe { print_stack_trace(); }


这样在每次运行完一个app后就会打印调用栈;


angin,在评论区里发现一个ch2-lab版本;
git clone https://github.com/rcore-os/rCore-Tutorial-v3.git
cd rCore-Tutorial-v3
git checkout ch2-lab
然后根据如下图的流程进行更改;

就有了如下输出;

第2题
题目要求:扩展内核,实现新系统调用get_taskinfo,能显示当前task的id和task name;实现一个裸机应用程序B,能访问get_taskinfo系统调用。
实现
pub fn print_app_info(&self) {
println!("[kernel] num_app = {}", self.num_app);
for i in 0..self.num_app {
println!(
"[kernel] app_{} [{:#x}, {:#x})",
i,
self.app_start[i],
self.app_start[i + 1]
);
}
}
第3题
题目要求:扩展内核,能够统计多个应用的执行过程中系统调用编号和访问此系统的调用的次数。
实现

文章介绍了在RISC-V架构下实现裸机应用程序和内核扩展的方法,包括打印调用栈、实现系统调用get_taskinfo、异常处理和陷阱机制。主要内容涉及编程题的解答,如追踪调用栈、用户trap处理函数,以及系统调用和异常的统计和管理。此外,还讨论了操作系统如何保护自身不受应用程序破坏的问题。
最低0.47元/天 解锁文章
1149

被折叠的 条评论
为什么被折叠?



