THU-rCore 操作系统实验踩坑日寄-1

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)也可以直接运行

generic loader

所以我都直接用

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也会坑人啊。

posted @ 2024-12-27 17:52  daydalek  阅读(165)  评论(0)    收藏  举报