文件系统接口¶
简易文件与目录抽象¶
与课堂所学相比,我们实现的文件系统进行了很大的简化:
扁平化:仅存在根目录
/
一个目录,所有的文件都放在根目录内。直接以文件名索引文件。不设置用户和用户组概念,不记录文件访问/修改的任何时间戳,不支持软硬链接。
只实现了最基本的文件系统相关系统调用。
打开与读写文件的系统调用¶
打开文件¶
/// 功能:打开一个常规文件,并返回可以访问它的文件描述符。
/// 参数:path 描述要打开的文件的文件名(简单起见,文件系统不需要支持目录,所有的文件都放在根目录 / 下),
/// flags 描述打开文件的标志,具体含义下面给出。
/// dirfd 和 mode 仅用于保证兼容性,忽略
/// 返回值:如果出现了错误则返回 -1,否则返回打开常规文件的文件描述符。可能的错误原因是:文件不存在。
/// syscall ID:56
fn sys_openat(dirfd: usize, path: &str, flags: u32, mode: u32) -> isize
目前我们的内核支持以下几种标志(多种不同标志可能共存):
如果
flags
为 0,则表示以只读模式 RDONLY 打开;如果
flags
第 0 位被设置(0x001),表示以只写模式 WRONLY 打开;如果
flags
第 1 位被设置(0x002),表示既可读又可写 RDWR ;如果
flags
第 9 位被设置(0x200),表示允许创建文件 CREATE ,在找不到该文件的时候应创建文件;如果该文件已经存在则应该将该文件的大小归零;如果
flags
第 10 位被设置(0x400),则在打开文件的时候应该清空文件的内容并将该文件的大小归零,也即 TRUNC 。
在用户库 user_lib
中,我们将该系统调用封装为 open
接口:
// user/src/lib.rs
bitflags! {
pub struct OpenFlags: u32 {
const RDONLY = 0;
const WRONLY = 1 << 0;
const RDWR = 1 << 1;
const CREATE = 1 << 9;
const TRUNC = 1 << 10;
}
}
pub fn open(path: &str, flags: OpenFlags) -> isize {
sys_openat(AT_FDCWD as usize, path, flags.bits, OpenFlags::RDWR.bits)
}
借助 bitflags!
宏我们将一个 u32
的 flags 包装为一个 OpenFlags
结构体,可以从它的 bits
字段获得 u32
表示。
顺序读写文件¶
在打开一个文件之后,我们就可以用之前的 sys_read/sys_write
两个系统调用来对它进行读写了。本教程只实现文件的顺序读写,而不考虑随机读写。
以本章的测试用例 ch6b_filetest_simple
来介绍文件系统接口的使用方法:
1// user/src/bin/ch6b_filetest_simple.rs
2
3#![no_std]
4#![no_main]
5
6#[macro_use]
7extern crate user_lib;
8
9use user_lib::{
10 open,
11 close,
12 read,
13 write,
14 OpenFlags,
15};
16
17#[no_mangle]
18pub fn main() -> i32 {
19 let test_str = "Hello, world!";
20 let filea = "filea\0";
21 let fd = open(filea, OpenFlags::CREATE | OpenFlags::WRONLY);
22 assert!(fd > 0);
23 let fd = fd as usize;
24 write(fd, test_str.as_bytes());
25 close(fd);
26
27 let fd = open(filea, OpenFlags::RDONLY);
28 assert!(fd > 0);
29 let fd = fd as usize;
30 let mut buffer = [0u8; 100];
31 let read_len = read(fd, &mut buffer) as usize;
32 close(fd);
33
34 assert_eq!(
35 test_str,
36 core::str::from_utf8(&buffer[..read_len]).unwrap(),
37 );
38 println!("file_test passed!");
39 0
40}
第 20~25 行,我们以 只写 + 创建 的模式打开文件
filea
,向其中写入字符串Hello, world!
而后关闭文件。第 27~32 行,我们以只读 的方式将文件
filea
的内容读取到缓冲区buffer
中。filea
的总大小不超过缓冲区的大小,因此通过单次read
即可将内容全部读出来而更常见的情况是需要进行多次read
,直到返回值为 0 才能确认文件已被读取完毕。