Add report for 'Linking Rust SFS to uCore'

master
WangRunji 7 years ago
parent 203130d2b0
commit 20d2ac2eca

@ -59,4 +59,185 @@
* 接入ucore中能够跑起来 * 接入ucore中能够跑起来
* 实现mksfs等周边工具 * 实现mksfs等周边工具
* 多线程支持及并行优化 * 多线程支持及并行优化
## 与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。为了找出出错位置各种gdbobjdump全上了还得看汇编追踪寄存器真是大坑。
- 引用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<RefCell<INode>>;
pub trait FileSystem {
// [1] 从这里获得FS
fn new(device: Box<Device>) -> Result<FileSystem>;
// [2] 从这里获得INode
fn root_inode(&self) -> INodePtr;
// 其他函数...
}
pub trait INode {
// 引用fs
fn fs(&self) -> Weak<FileSystem>;
// [3] 获得其他INode
fn lookup(&self, path: &'static str) -> Result<INodePtr>;
// 其他函数...
}
```
#### 如何合并
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有更深入的了解
Loading…
Cancel
Save