DragonOS 招新任务:重写 ramfs 并挂载
添加 mod
在 kernel/src/filesystem/myramfs
下添加 mod.rs
文件
在 kernel/src/filesystem/mod.rs
中添加 pub mod myramfs;
结构体定义
在 mod.rs
中定义如下结构体:
pub struct MyRamFs {
/// MyRamFs的root inode
root_inode: Arc<LockedMyRamFsInode>,
}
pub struct MyRamFsInode {
/// 指向父Inode的弱引用
parent: Weak<LockedMyRamFsInode>,
/// 指向自身的弱引用
self_ref: Weak<LockedMyRamFsInode>,
/// 子Inode的B树
children: BTreeMap<String, Arc<LockedMyRamFsInode>>,
/// 当前inode的数据部分
data: Vec<u8>,
/// 当前inode的元数据
metadata: Metadata,
/// 指向inode所在的文件系统对象的指针
fs: Weak<MyRamFs>,
/// 指向特殊节点
special_node: Option<SpecialNodeData>,
}
/// 加锁的MyRamFsInode
pub struct LockedMyRamFsInode(SpinLock<MyRamFsInode>);
trait定义
在 myramfs/mod.rs
中实现 Filesystem
trait:
impl FileSystem for MyRamFs {
fn root_inode(&self) -> Arc<dyn IndexNode>;
fn info(&self) -> FsInfo;
fn as_any_ref(&self) -> &dyn Any;
}
实现 IndexNode
trait:
impl IndexNode for MyRamFsInode {
fn read_at(
&self,
offset: usize,
len: usize,
buf: &mut [u8],
_data: &mut FilePrivateData,
) -> Result<usize, SystemError>;
fn write_at(...);
fn metadata(...);
fn set_metadata(...);
fn link(...);
fn unlink(...);
fn find(...);
fn find_mut(...);
fn children(...);
fn as_any_ref(...);
...
}
挂载
编写一个 pub fn my_ram_fs_init()
函数,用于挂载 MyRamFs
文件系统:
pub fn my_ram_fs_init() -> Result<(), SystemError> {
static INIT: Once = Once::new();
let mut result = None;
INIT.call_once(|| {
kinfo!("Initializing MyRamFs...");
// 创建 myMyRamFs 实例
let my_ram_fs: Arc<MyRamFs> = MyRamFs::new();
// myMyRamFs 挂载
let _t = ROOT_INODE()
.find("myramfs")
.expect("Cannot find /myramfs")
.mount(my_ram_fs)
.expect("Failed to mount myramfs");
kinfo!("MyRamFs mounted.");
result = Some(Ok(()));
});
return result.unwrap();
}
在加载文件系统时调用 my_ram_fs_init()
函数。
#[inline(never)]
pub fn vfs_init() -> Result<(), SystemError> {
// 使用Ramfs作为默认的根文件系统
let ramfs = RamFS::new();
let mount_fs = MountFS::new(ramfs, None);
let root_inode = mount_fs.root_inode();
// 创建文件夹
...
// 其他文件系统初始化
...
// 加入初始化MyRamFs的代码
my_ram_fs_init().expect("Failed to initialize myramfs");
...
return Ok(());
}
在 vfs_init()
函数中,我们创建了一个 MountFS
实例,并将 RamFS
挂载到根目录下。然后创建了 /proc
、/dev
、/sys
、/myramfs
四个文件夹。最后调用 my_ram_fs_init()
函数挂载 MyRamFs
文件系统。
MountFS
是一个虚拟文件系统,用于挂载其他文件系统。RamFS
是一个简单的内存文件系统,用于挂载到根目录下。
在最新版本的DragonOS,有些代码有所变动,这个方法可能不适用。
调试
在最初编写完成时,发现写入文件时追加部分数据会出现错误,经过调试发现是由于 write_at
函数中未更新文件大小导致的。在 write_at
与 read_at
函数中添加如下代码:
fn write_at(
&self,
offset: usize,
len: usize,
buf: &[u8],
_data: &mut FilePrivateData,
) -> Result<usize, SystemError> {
...
kdebug!("write_at: offset: {}, len: {}, buf.len: {}, inode.data.len(): {}", offset, len, buf.len(), inode.data.len());
// 如果offset + len > inode.data.len(),则需要扩容
if offset + len > inode.data.len() {
inode.data.resize(offset + len, 0);
kdebug!("write_at(): resize to: {}", offset + len);
inode.metadata.size = (offset + len) as i64; // 更新文件大小,影响到下一次调用的offset
kdebug!("write_at(): inode.metadata.size: {}", inode.metadata.size);
}
inode.data[offset..offset + len].copy_from_slice(&buf[..len]);
return Ok(len);
}
fn read_at(
&self,
offset: usize,
len: usize,
buf: &mut [u8],
_data: &mut FilePrivateData,
) -> Result<usize, SystemError> {
...
kdebug!("read_at: offset: {}, len: {}, buf.len: {}, inode.data.len(): {}", offset, len, buf.len(), inode.data.len());
let start = offset.min(inode.data.len());
let end = (offset + len).min(inode.data.len());
let read_len = end - start;
buf[..read_len].copy_from_slice(&inode.data[start..end]);
Ok(read_len)
}
虽然 kdebug!
宏在最新版本的DragonOS中已经被移除,但是通过打印调试信息,我们可以更好地理解代码的执行流程,这是一种重要的思想。
总结
本次文件系统实验难度适中。关键在于理解已有的文件系统框架,然后根据需求实现相应的trait
。在实现过程中,我们需要注意文件系统的数据结构设计,以及文件系统的挂载与初始化。在调试过程中,我们可以通过打印调试信息来帮助我们理解代码的执行流程。最后,我们需要注意代码的风格与规范,以便于他人阅读与维护。
本来在学期第13周已经完成了这次实验,但是拖到放假才写了这个帖子,实在不好意思。今后会更加努力学习进一步的知识,比如从用户态到内核态的过程和挂载机制的实现。