From 20d2ac2eca6c3edde73121439241c7015707d379 Mon Sep 17 00:00:00 2001 From: WangRunji Date: Mon, 7 May 2018 20:08:28 +0800 Subject: [PATCH] Add report for 'Linking Rust SFS to uCore' --- docs/rust_port_report.md | 183 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 182 insertions(+), 1 deletion(-) diff --git a/docs/rust_port_report.md b/docs/rust_port_report.md index 22cf15d..6dce5fa 100644 --- a/docs/rust_port_report.md +++ b/docs/rust_port_report.md @@ -59,4 +59,185 @@ * 接入ucore中能够跑起来 * 实现mksfs等周边工具 -* 多线程支持及并行优化 \ No newline at end of file +* 多线程支持及并行优化 + + + +## 与ucore_os_lab8 对接报告 + +王润基 2018.05.07 + +搞定这事儿耗费了大约15小时的时间 + +### 实现的功能 + +通过C兼容层将Rust VFS接口和ucore VFS层对接起来。 + +经测试,在shell中能够正常执行各个程序,`ls`输出正确。 + +在实现上:清理掉ucore_os_lab8的sfs文件夹,只留下`sfs.h`(未修改)和`sfs.c`(添加少许辅助函数)。再将Rust SFS编译成静态库,链接到kernel中。 + +### 如何运行 + +整合之后的ucore可以从[这里](https://github.com/wangrunji0408/ucore_os_lab/tree/rust-fs/labcodes_answer/lab8_result)获得。 + +```bash +# 下载项目 +git clone https://github.com/wangrunji0408/ucore_os_lab.git -b rust-fs --recursive +# 切到lab8目录 +cd ucore_os_lab/labcodes_answer/lab8_result/ +# 如果没有安装Rust,用以下命令,但愿能work +make -f rust_sfs/Makefile install-rust +# 编译运行 +make qemu +``` + +### 如何调试 + +我使用CLion + Rust插件编写代码。 + +调试时,首先用`make dbg4ec`开启QEMU,再用CLion中自带的`GDB Remote Debug`连接上去,即可GUI调试。此外还需在ucore的makefile中添加编译参数`-g`,才能在CLion中定位C的源代码。 + +### 遇到的问题 + +- 链接丢失: + + ucore_os_lab的linker script少写了一些section,包括`*.data.*` `*.got.*` `*.bss.*`,导致Rust lib链接过去后丢失了一些段,比较坑的是这不会有任何提示。没有成功重定位的地址都是0,运行时直接Page fault。为了找出出错位置,各种gdb,objdump全上了,还得看汇编追踪寄存器,真是大坑。 + +- 引用LLVM内置函数: + + Rust lib会引用一些LLVM内置函数(如udivdi3,都是除法运算相关),链接时会报undefined symbol。但实际上并没有代码用到它们。《Writing an OS in Rust》中提到了这个问题,它的解决方案是链接时加`--gc-sections`选项,将未用到的段删掉,结果我对ucore如此操作之后所有段都没了,都boot不起来。。。最后是在C中强行定义这些符号解决的。 + +- 对ucore VFS接口理解的歧义: + + 例如:`getdirentry`,`gettype` + + 经常需要查看ucore SFS的原始实现,以及输出它传给我的结构内容,才能理解它到底是如何工作的。 + +### C兼容层的设计 + +#### ucore VFS分析 + +```c +// [1] SFS模块的入口点。从这里获得fs。 +int sfs_do_mount(struct device *dev, struct fs **fs_store); + +struct fs { + // 头部是具体文件系统的内容 + union { + struct sfs_fs __sfs_info; + } fs_info; + + // 元信息 + enum { + fs_type_sfs_info, + } fs_type; + + // 虚函数表 + int (*fs_sync)(struct fs *fs); + // [2] 从这里获得inode + struct inode *(*fs_get_root)(struct fs *fs); + int (*fs_unmount)(struct fs *fs); + void (*fs_cleanup)(struct fs *fs); +}; + +struct inode { + // 头部是具体文件系统的内容 + union { + struct device __device_info; + struct sfs_inode __sfs_inode_info; + } in_info; + + // 元信息,引用计数 + enum { + inode_type_device_info = 0x1234, + inode_type_sfs_inode_info, + } in_type; + int ref_count; + int open_count; + + // 引用fs + struct fs *in_fs; + + // 虚函数表 + // [3] 通过其中的lookup()/create()获得其他inode + const struct inode_ops *in_ops; +}; +``` + +#### Rust VFS分析 + +```rust +type INodePtr = Rc>; + +pub trait FileSystem { + // [1] 从这里获得FS + fn new(device: Box) -> Result; + // [2] 从这里获得INode + fn root_inode(&self) -> INodePtr; + // 其他函数... +} + +pub trait INode { + // 引用fs + fn fs(&self) -> Weak; + // [3] 获得其他INode + fn lookup(&self, path: &'static str) -> Result; + // 其他函数... +} +``` + +#### 如何合并 + +ucore VFS中fs和inode头部是具体FS的struct。Rust VFS使用Rc指针相互引用。为了将它们合并起来,考虑过两种方案: + +* **在头部放Rc指针** + * Rust VFS = ucore SFS + * 还需建立Rust INode => ucore INode的反向引用 + * 要么侵入式地增加Rust INode的字段 + * 要么**在兼容层搞一个全局Map** + * 这种方式耦合较低,但多一层指针跳转,性能可能略差。 +* 在头部放Rust SFS的结构本体 + * Rust VFS = ucore VFS + * 需要在Rust new出SFS结构时做文章, 委托ucore分配多一点空间并做VFS的初始化,得魔改Rust的全局内存分配器。 + * 这种方式耦合较高,需对Rust结构的实际内存布局有深入理解。 + +最终采取的方案加粗表示。 + +在这个框架下,将ucore虚函数表中的每一个都用RustVFS提供的接口去实现,并处理参数格式转化。以比较复杂的`lookup`为例: + +```rust +// 这个函数的地址保存在虚表中,传给ucore +extern fn lookup(inode: &mut INode, path: *mut u8, inode_store: &mut *mut INode) -> ErrorCode { + // 【参数处理】将C字符串转为Rust字符串 + let path = unsafe{ libc::from_cstr(path) }; + // 【完成功能】调用 Rust VFS 的函数 + let target = inode.borrow().lookup(path); + // 【结果处理】 + match target { + Ok(target) => { + // 从ucore inode中获取ucore fs + let fs = unsafe{ ucore::inode_get_fs(inode) }; + // 使用全局Map,将Rust INode映射为ucore inode,如果不存在则新建一个(要用到ucore fs) + let inode = INode::get_or_create(target, fs); + // 按ucore接口要求,增加引用计数 + unsafe { ucore::inode_ref_inc(inode) }; + // 给出ucore inode + *inode_store = inode; + ErrorCode::Ok + }, + // 设置正确的错误码 + Err(_) => ErrorCode::NoEntry, + } +} +``` + +经过统计,`C兼容层`的有效代码量约500行,和实现核心功能的`Rust SFS层`相当。说明这种胶水代码真是又臭又长,搞兼容性都是脏活累活。 + +### 那么……意义何在? + +* 将FS模块接入实际OS,验证其可行性和可靠性(尤其是在RustOS还遥遥无期的背景下……) +* 检验了Rust和C的互操作性,证明Rust编写系统模块是可行的。 +* (试图)为ucore提供一套更好更实用的文件系统接口(个人认为Rust接口更容易去实现),方便日后将其他FS接入(如CS140e.FAT32) +* (也许)能为OS教学提供另一种可能性 +* 锻炼了我的跨语言+汇编级别debug能力,让我对ucore FS有更深入的了解 \ No newline at end of file