本文分为两部分:
第一部分将详细分析JOS
的文件系统及文件描述符的实现方法。
第二部分将实现工作路径,提供新的系统调用,完善用户空间工具。
本文中支持的新特性:
支持进程工作目录 提供
getcwd
与chdir
新的
syscall
SYS_env_set_workpath
修改工作路径
- 新的用户程序
ls
功能完善pwd
输出当前工作目录cat
接入工作目录touch
由于文件属性没啥可改的,用于创建文件mkdir
创建目录文件msh
更高级的shell
还未完全完工 支持cd
支持默认二进制路径为bin
- 调整目标磁盘生成工具
Github:https://github.com/He11oLiu/MOS
JOS文件系统详解
文件系统总结
Regular env FS env
+---------------+ +---------------+
| read | | file_read |
| (lib/fd.c) | | (fs/fs.c) |
...|.......|.......|...|.......^.......|...............
| v | | | | RPC mechanism
| devfile_read | | serve_read |
| (lib/file.c) | | (fs/serv.c) |
| | | | ^ |
| v | | | |
| fsipc | | serve |
| (lib/file.c) | | (fs/serv.c) |
| | | | ^ |
| v | | | |
| ipc_send | | ipc_recv |
| | | | ^ |
+-------|-------+ +-------|-------+
| |
+-------------------+
- 底层与磁盘有关的丢给
ide_xx
来实现,因为要用到IO
中断,要给对应的权限 - 通过
bc_pgfault
来实现缺页自己映射,利用flush_block
来回写磁盘 - 然后通过分
block
利用block cache
实现对于磁盘的数据读入内存或者写回磁盘 - 再上面一层的
file_read
与file_write
,均是对于blk
的操作。 - 再上面就是文件系统服务器,通过调用
file_read
实现功能了。 - 客户端通过对需求打包,发送
IPC
给文件系统服务器,即可实现读/写文件的功能。
文件系统&文件描述符 Overview
JOS文件系统是直接映射到内存空间DISKMAP
到DISKMAP + DISKSIZE
这块空间。故其支持的文件系统最大为3GB.
IDE ide.c
文件系统底层PIO
驱动放在ide.c
中。注意在IDE
中,是以硬件的角度来看待硬盘,其基本单位是sector
,不是block
。
bool ide_probe_disk1(void)
用于检测disk1
是否存在。voidide_set_disk(int diskno)
用于设置目标磁盘。ide_read ide_write
用于磁盘读写。
block cache bc.c
文件系统在内存中的映射是基于block cache
的。以一个block
为单位在内存中为其分配单元。注意在bc
中,是以操作系统的角度来看待硬盘,其基本单位是block
,不是sector
。
void *diskaddr(uint32_t blockno)
用于查找blockno
在地址空间中的地址。blockno = ((uint32_t)addr - DISKMAP) / BLKSIZE
用于查找addr
对应文件系统中的blockno
。static void bc_pgfault(struct UTrapframe *utf)
用于处理读取不在内存中而出现page fault
的情况。这时需要从file system
通过PIO
读取到block cache
(也就是内存中新分配的一页)中,并做好映射。void flush_block(void *addr)
用于写回硬盘,写回时清理PTE_D
标记。
file system fs.c
文件系统是基于刚才的block cache
和底层ide
驱动的。
bitmap 相关
bitmap
每一位代表着一个block
的状态,用位操作检查/设置block
状态即可。
bool block_is_free(uint32_t blockno)
用于check给定的blockno
是否是空闲的。void free_block(uint32_t blockno)
设置对应位为0int alloc_block(void)
设置对应位为1
文件系统操作
void fs_init(void)
初始化文件系统。检测disk1
是否存在,检测super block
和bitmap block
。static int file_block_walk(struct File *f, uint32_t filebno, uint32_t **ppdiskbno, bool alloc)
用于找到文件f
的fileno
个block
的blockno
。alloc
用于控制当f_indirect
不存在的时候,是否需要新申请一个block
。int file_get_block(struct File *f, uint32_t filebno, char **blk)
用于找到文件f
的fileno
个block
的地址。static int dir_lookup(struct File *dir, const char *name, struct File **file)
用于在dir
下查找name
这个文件。其遍历读取dir
这个文件,并逐个判断其目录下每一个文件的名字是否相等。static int dir_alloc_file(struct File *dir, struct File **file)
在dir
下新申请一个file
。同样也是遍历所有的dir
下的文件。找到第一个名字为空的文件,并把新的文件存在这里。static int walk_path(const char *path, struct File **pdir, struct File **pf, char *lastelem)
用于从根目录获取path
的文件,文件放在pf
中,路径放在pdir
中。如果找到了路径没有找到文件。最后的路径名放在lastelem
中,最后的路径放在pdir
中。
文件操作
int file_create(const char *path, struct File **pf)
用于创建文件。int file_open(const char *path, struct File **pf)
打开文件。ssize_t file_read(struct File *f, void *buf, size_t count, off_t offset)
从f
的offset
读取count
bytes的数据放入buf
中。int file_write(struct File *f, const void *buf, size_t count, off_t offset)
与上面的类似。static int file_free_block(struct File *f, uint32_t filebno)
删除文件中的filebno
static void file_truncate_blocks(struct File *f, off_t newsize)
缩短文件大小。int file_set_size(struct File *f, off_t newsize)
修改文件大小。void file_flush(struct File *f)
将文件写回硬盘void fs_sync(void)
将所有的文件写回硬盘
文件系统服务器 serv.c
服务器主要逻辑
umain
: 初始化文件系统,初始化服务器,开始接收请求。服务器具体函数见上面实现。
int openfile_alloc(struct OpenFile **o)
用于服务器分配一个openfile
结构体
文件描述符 fd.c
struct fd
结构体struct Fd { int fd_dev_id; off_t fd_offset; int fd_omode; union { // File server files struct FdFile fd_file; }; };
其中
fd_file
用于发送的时候传入服务器对应的fileid
包括了
fd_id
文件读取的offset
,读取模式以及FdFile
int fd2num(struct Fd *fd)
从fd
获取其编号char* fd2data(struct Fd *fd)
从fd
获取文件内容int fd_alloc(struct Fd **fd_store)
查找到第一个空闲的fd
,并分配出去。int fd_lookup(int fdnum, struct Fd **fd_store)
为查找fdnum
的fd,并放在fd_store
中。int fd_close(struct Fd *fd, bool must_exist)
用于关闭并free一个fdint dev_lookup(int dev_id, struct Dev **dev)
获取不同的Deviceint close(int fdnum)
关闭fd
void close_all(void)
关闭全部int dup(int oldfdnum, int newfdnum)
dup
不是简单的复制,而是要将两个fd
的内容完全同步,其是通过虚拟内存映射做到的。read(int fdnum, void *buf, size_t n)
后面的与这个类似- 获取
fd
的fd_dev_id
并根据其获取dev
- 调用
dev
对应的function
- 获取
int seek(int fdnum, off_t offset)
用于设置fd
的offset
int fstat(int fdnum, struct Stat *stat)
获取文件状态。