Kika's
Blog
图片简介 | CC BY 4.0 | 换一张

rCore Memo

2024-05-08

做rCore时的随手备忘 施工中🚧

Rust

所有权

  • 堆上的变量才需要考虑所有权,因为栈上的变量直接深拷贝了。
  • 可变引用&mut同时只能存在一个
  • 可变引用与不可变引用不能同时存在
fn main() {
    let mut s = String::from("hello world");
    let word = first_word(&s);
    s.clear(); // clear()需要可变引用,但已经存在不可变引用word,所以报错
    println!("the first word is: {}", word);
}
fn first_word(s: &String) -> &str {
    &s[..1]
}
  • 引用的作用域从创建开始,一直持续到它最后一次使用的地方,而不是}结束处
fn main() {
    let mut s = String::from("hello");

    let r1 = &s;
    let r2 = &s;
    println!("{} and {}", r1, r2);
    // r1,r2作用域在这里结束

    let r3 = &mut s;
    println!("{}", r3);
}
// r3作用域在这里结束
  • 具有Copy特征的类型无需所有权转移

切片

  • 切片&s[6..11],左闭右开区间$[6,11)$

迭代器

a..b相当于左闭右开$[a,b)$的迭代器,类型为core::ops::Range

fn find_next_task(&self) -> Option<usize> {
        let inner = self.inner.exclusive_access();
        let current = inner.current_task;
        (current + 1..current + self.num_app + 1)
                .map(|id| id % self.num_app)
                .find(|id| {
                        inner.tasks[*id].task_status == TaskStatus::Ready
                })
}

字符串

  • str是硬编码到文件中的字符串,Rust内置的类型,Unicode编码,占4个字节;而String则是标准库提供的UTF-8字符串,每个字符大小不定(1~4字节)
  • 字符串无法通过下标访问
  • +连接字符串,相当于调用了fn add(self, s: &str) -> String,需要传递切片引用类型,返回一个新的字符串
  • 使用format!来连接字符串 format!("{} {}!", s1, s2);
  • 遍历unicode字符
for c in "中国人".chars() {
    println!("{}", c);
}

元组

fn main() {
    let x: (i32, f64, u8) = (500, 6.4, 1);
    let five_hundred = x.0;
    let six_point_four = x.1;
    let one = x.2;
}

结构体

结构体更新语法

user2仅仅在email上与user1不同,因此我们只需要对email进行赋值,剩下的通过结构体更新语法 ..user1即可完成,但注意所有权的转移

let user2 = User {
    email: String::from("another@example.com"),
    ..user1
};

元组结构体

没名称的结构体

struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);

打印结构体

#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}
fn main() {
    let scale = 2;
    let rect1 = Rectangle {
        width: dbg!(30 * scale),
        height: 50,
    };
    dbg!(&rect1);
}

枚举

enum PokerCard {
    Clubs(u8),
    Spades(u8),
    Diamonds(char),
    Hearts(char),
}
fn main() {
   let c1 = PokerCard::Spades(5);
   let c2 = PokerCard::Diamonds('A');
}

数组

// [类型; 长度]
let a: [i32; 5] = [1, 2, 3, 4, 5];
let a = [3; 5];
// 复杂类型没有深拷贝,不能用上面的方法
let array: [String; 8] = std::array::from_fn(|_i| String::from("rust is good!"));

for n in a.iter() {
    print!("\t{} + 10 = {}", n, n+10);
}
for i in 0..a.len() {
    print!("\t{} + 10 = {}", a[i], a[i]+10);
}

循环

let result = loop {
    counter += 1;
    if counter == 10 {
        break counter * 2;
    }
};

模式匹配

匹配多个值

let some_u8_value = 0u8;
match some_u8_value {
    1 => println!("one"),
    3 => println!("three"),
    5 => println!("five"),
    7 => println!("seven"),
    _ => (),
}

只关心一个匹配

let v = Some(3u8);
if let Some(3) = v {
    println!("three");
}

工具

常用工具-分析可执行文件

# 文件格式
file ...
# 文件统计信息(大小、修改、块个数)
stat ...
# 文件头信息
rust-readobj -h ...
# 反汇编
rust-objdump -S ...
# 去除元数据
rust-objcopy --strip-all ... -O binary ...

Qemu

flowchart LR
    0x1000 --Qemu--> 0x80000000 --RustSBI--> 0x80200000
qemu-system-riscv64 \
        # 命名
    -machine virt \
        # 不需要提供图形界面
    -nographic \
        # 设置bootloader
    -bios ./bootloader/rustsbi-qemu.bin \
        # 加载内核镜像到地址
    -device loader,file=target/riscv64gc-unknown-none-elf/release/myrcore.bin,addr=0x80200000 \

GDB

riscv64-unknown-elf-gdb \
-ex 'file target/riscv64gc-unknown-none-elf/release/myrcore' -ex 'set arch riscv:rv64' -ex 'target remote localhost:1234'
# 从当前 PC 值的位置开始,在内存中反汇编 10 条指令
x/10i $pc
# 显示 0x80000000 处的10条数据,格式为16进制32bit
x/10xw 0x80000000
# = info register
i r
i r t0
# 在函数shutdown处设置断点
b shutdown
# 执行100步
si 100
# 继续执行
c

RISC-V

SBI

寄存器组 需要保存 功能
a0~a7(x10~x17) x 传递输入参数和返回值
t0~t6(x5~x7,x28~x31) x 临时寄存器
s0~11(x8~x9,x18~x27) o 临时寄存器
zero(x0) x 恒为零
ra(x1) o ret返回地址,需要保存到栈帧中
sp(x2) o 栈指针(Stack Pointer)
fp(s0) o 栈帧指针(Frame Pointer),父亲栈帧的结束地址
gp(x3),tp(x4) x

栈帧内容

其中fp指向的栈帧起始位置和sp指向的栈帧的当前栈顶位置形成了所对应函数栈帧的空间范围。然后利用prev fp可以倒推出一条调用链。

# 开场
# 为当前函数分配 64 字节的栈帧
addi        sp, sp, -64
# 将 ra 和 fp 压栈保存
sd  ra, 56(sp)
sd  s0, 48(sp)
# 更新 fp 为当前函数栈帧顶端地址
addi        s0, sp, 64

# ...

# 结尾
# 恢复 ra 和 fp
ld  ra, 56(sp)
ld  s0, 48(sp)
# 退栈
addi        sp, sp, 64
# 返回,使用 ret 指令或其他等价的实现方式
ret

特权

OS 内核位于 Supervisor 特权级,而 RustSBI 位于 Machine 特权级,也是最高的特权级。类似 RustSBI 这样运行在 Machine 特权级的软件被称为 Supervisor Execution Environment(SEE),即 Supervisor 执行环境。两层软件之间的接口被称为 Supervisor Binary Interface(SBI),即 Supervisor 二进制接口。 SBI Specification (简称 SBI spec)规定了 SBI 接口层要包含哪些功能,该标准由 RISC-V 开源社区维护。RustSBI 按照 SBI spec 标准实现了需要支持的大多数功能,但 RustSBI 并不是 SBI 标准的唯一一种实现,除此之外还有社区中的前辈 OpenSBI 等等。

trap

异常与当前 CPU 的指令执行是 同步 (Synchronous) 的,异常被触发的原因一定能够追溯到某条指令的执行;而中断则 异步 (Asynchronous) 于当前正在进行的指令,也就是说中断来自于哪个外设以及中断如何触发完全与处理器正在执行的当前指令无关。