rust 语言特性
写这篇文章的目的在于记录在页表转换过程中使用
unsafe
造成的一些问题,以及rust
在升级过程中产生的一些不同。
- 首先我们有一个 unsafe 的地址翻译的函数,然后返回的是一个 Option 的值。我们想要在函数调用之后,输出返回值,那么我们可以有以下的代码 ```rust for &address in &addresses { let virt = VirtAddr::new(address); let phys = unsafe { translate_addr(virt, phys_mem_offset); }; // unsafe 里面加了一个分号,这样类型判断就完全不一样了 println!(“ -> {:?}”, virt, phys); }
但是其实这样我们可以看到,rust 的自动类型判断 phys 的类型是 () ,而不是一个 Option<PhysAddr> 所以我们直接打印的结果并不是物理地址而是() 。
而我们使用下面的代码就是正确的。
```rust
for &address in &addresses {
let virt = VirtAddr::new(address);
unsafe{
let phys = translate_addr(virt, physical_mem_offset);
println!("{:?} -> {:?}", virt, phys);
};
}
使用 hlt_loop 降低功耗
条件编译中的 与或非
#[cfg(all(target_arch = "x86_64", target_os = "linux"))]
fn linux_specific() {}
#[cfg(any(feature = "instructions", target_arch = "x86_64"))]
fn feature_or_arch() {}
#[cfg(not(feature = "instructions"))]
关于 trait 对象的使用
- &mut impl FrameAllocator
静态分发,接受任何实现了 FrameAllocator 的可变引用,会在编译时确定具体的类型。没有运行时开销。 - &mut dyn FrameAllocator
动态分发,接受一个指向实现 FrameAllocator 的 trait 对象的可变引用,调用函数会在运行时通过虚函数表动态进行。会有一定的运行时开销。
如何分辨 static 和 Box 这类分配在堆上的数据结构
我们知道在内核的初期阶段是没有堆的,但是我们还是需要像 idt 这样的数据结构,仅仅分配在栈上生命周期是不够的,所以我们需要静态变量,但是如果不是在堆上,那么静态变量是分配在哪里的呢?
静态变量是在以区别于栈的固定的地方。rust 规定静态变量是只读的,因为我们要避免数据竞争,也就是避免多个地方同时改动导致数据不一致。但是如果我们要改静态变量需要用 Mutex
包一层,这样我们可以保证同一时间只有一个可变引用。
我们为什么需要堆(动态数据)
- 局部变量:函数调用结束后,局部变量会被销毁
- 静态变量:整个程序运行过程中静态变量都不会被释放,即便我们不再需要使用它们。它们会一直占用固定的内存。而且它们可以被任何一个函数调用。 局部变量和静态变量都有各自的限制,并且它们都是固定大小的。并不能动态的扩展大小。
为什么我们的内核需要堆?
我们需要在两种地方使用堆:一种是动态生命周期,另一种是动态尺寸的数据类型。
我们将会在进程管理中使用动态大小的数据结构来管理进程。
关于一个中断时的闭包
#[inline]
pub fn without_interrupts<F, R>(f: F) -> R
where
F: FnOnce() -> R,
{
// true if the interrupt flag is set (i.e. interrupts are enabled)
let saved_intpt_flag = are_enabled();
// if interrupts are enabled, disable them for now
if saved_intpt_flag {
disable();
}
// do `f` while interrupts are disabled
let ret = f();
// re-enable interrupts if they were previously enabled
if saved_intpt_flag {
enable();
}
// return the result of `f` to the caller
ret
}
对于 FnOnce 的闭包,我们可以不为其声明一个函数,直接使用闭包的方式。
而且我们貌似并没有在其他的时候使用这个闭包,所以我们没必要为其生命为一个函数。
我们使用下面的方法来调用。
pub fn _print(args: fmt::Arguments) {
use core::fmt::Write;
use x86_64::instructions::interrupts;
interrupts::without_interrupts(|| {
SERIAL1
.lock()
.write_fmt(args)
.expect("Printing to serial failed");
});
}