rCore是清华大学操作系统实验uCore之rust版本。不得不说能在本科阶段提供这种高质量的操作系统实验课程,并且还是一次提供两种语言的选择确实很厉害。出于“学习一门语言的最好方法就是用这个语言做一个项目”的想法,这次就选了rCore作为学习rust的桥梁。
在做rCore之前,提前刷了一部分rustlings来熟悉rust语法,之后就一边做一边熟悉了。目前在rCore-tutorial-book-v3上,还要求使用nightly版本的工具链。不过如今rust工具链的已经迭代到v1.83(stable),想必彼时的nightly特性应该早已经稳定了(指导书使用的版本是1.62 nightly)。虽然如此,以防万一这次还是用1.85nightly版本作的实验。
开发环境是VSCode/Neovim + rust-analyzer。当然由于二者都是LSP+语言服务器的设计,所以语言服务的体验上是没有什么差别的。虽然也可以使用JetBrains的RustRover,但是JetBrains系IDE的运行速度在我的低配电脑上实在很难称得上顺滑,也许还是使用编辑器好些。
PART 1
PART1的主要内容是移除Rust标准库依赖,构建一个可以打印字符串的裸机程序。虽然在学408的时候对syscall有些认识,但是真正移除syscall来写一个裸机程序还是第一次。
在处理src/lang_items.rs的时候 碰到了问题
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
if let Some(location) = info.location() {
println!(
"Panicked at {}:{} {}",
location.file(),
location.line(),
- // info.message().unwrap()
+ info.message().as_str().unwrap()
);
} else {
- // println!("Panicked: {}", info.message().unwrap());
+ println!("Panicked: {}", info.message().as_str().unwrap());
}
shutdown(true)
}
调查之后 发现core::panic::PanicInfo::message()
在1.81才stable,并且返回值从nightly版本的Option<&fmt::Arguments<'_>>
#[must_use]
#[unstable(feature = "panic_info_message", issue = "66745")]
pub fn message(&self) -> Option<&fmt::Arguments<'_>> {
self.message
}
变成了结构体PanicMessage
#[must_use]
#[stable(feature = "panic_info_message", since = "0.81.0")]
pub fn message(&self) -> PanicMessage<'_> {
PanicMessage { message: self.message }
由此它返回的就并不是个Option
了,直接unwrap
是不行的
而这PanicMessage
的定义也十分简单,只是存放了fmt::Arguments
的引用
#[stable(feature = "panic_info_message", since = "1.81.0")]
pub struct PanicMessage<'a> {
message: &'a fmt::Arguments<'a>,
}
因此,使用as_str
得到一个Option<&str>
#[stable(feature = "panic_info_message", since = "1.81.0")]
#[rustc_const_stable(feature = "const_arguments_as_str", since = "1.84.0")]
#[must_use]
#[inline]
pub const fn as_str(&self) -> Option<&'static str> {
self.message.as_str()
}
//core/src/fmt/mod.rs
#[stable(feature = "fmt_as_str", since = "1.52.0")]
#[rustc_const_stable(feature = "const_arguments_as_str", since = "1.84.0")]
#[must_use]
#[inline]
pub const fn as_str(&self) -> Option<&'static str> {
match (self.pieces, self.args) {
([], []) => Some(""),
([s], []) => Some(s),
_ => None,
}
}
获取字符串表示,从而打印出panic信息。
qemu的generic loader现在已经支持直接加载ELF, U-Boot, Intel HEX executable格式的文件,所以不再需要剥离出纯二进制格式(xx.bin)也可以直接运行
所以我都直接用
qemu-system-riscv64 \
-machine virt \
-nographic \
-bios ../bootloader/rustsbi-qemu.bin \
-device loader,file=target/riscv64gc-unknown-none-elf/debug/os,addr=0x80200000
就可以测试效果,不再用objcopy剥离出纯二进制格式,这样方便不少。
大概是对rust还不熟悉,看到
(sbss as usize..ebss as usize).for_each(|a|{
unsafe{(a as *mut u8).write_volatile(0)}
});
的时候,给我懵逼了半分钟,然后发现它其实只是
let start=sbss as usize;
let end=ebss as usize;
for memory_addr in start..end{
unsafe{
(memory_addr as *mut u8).write_volatile(0);
}
}
大概上面一种才是rustacean们喜欢的写法,不过我暂时还是更习惯下面的写法。
rust1.82中添加了unsafe attribute的特性。
它说
- The following attributes must now be marked as unsafe:
- export_name
- link_section
- no_mangle
即这些不安全的属性都得标上#[unsafe(attribute)]
才行。而因为我用的是1.85nightly,所以就得顺势写成这样
#[unsafe(no_mangle)]
pub unsafe fn rust_main()->(){
clear_bss();
println!("Hello World");
/*hello world*/
sbi::console_putchar(104);
sbi::console_putchar(101);
sbi::console_putchar(108);
sbi::console_putchar(108);
sbi::console_putchar(111);
sbi::console_putchar(32);
sbi::console_putchar(119);
sbi::console_putchar(111);
sbi::console_putchar(114);
sbi::console_putchar(108);
sbi::console_putchar(100);
/*hello world*/
panic!("Shutdown machine!");
}
糟糕的是大概因为版本太新还没有进入LLM们的训练语料中,几乎所有LLM(Claude,GPT,Tongyi)都会认为这种写法是错的。所以有时候LLM也会坑人啊。
标签:info,console,操作系统,rCore,THU,str,putchar,message,sbi From: https://www.cnblogs.com/daydalekbot/p/18636410