操作系统lab8实验报告
本次实验涉及的是文件系统,通过分析了解 ucore 文件系统的总体架构设计,完善读写文件操作,从新实现基于文件系统的执行程序机制(即改写do_execve
),从而可以完成执行存储在磁盘上的文件和实现文件读写等功能。可以看到,在kern_init
函数中,多了一个fs_init
函数的调用。fs_init
函数就是文件系统初始化的总控函数,它进一步调用了虚拟文件系统初始化函数 vfs_init
,与文件相关的设备初始化函数 dev_init
和 Simple FS
文件系统的初始化函数 sfs_init
。这三个初始化函数联合在一起,协同完成了整个虚拟文件系统、SFS文件系统和文件系统对应的设备(键盘、串口、磁盘)的初始化工作。
练习0:填写已有实验
同样运用一款名为meld
的软件,将已完成的lab7和lab8进行对比,大致截图如下:
需要修改的文件罗列如下:
proc.c
default_pmm.c
pmm.c
swap_fifo.c
vmm.c
trap.c
sche.c
monitor.
check_sync.c
同样只需要依次将实验1-7的代码填入本实验中代码即可,无需进行其他的修改。
练习1 完成读文件操作的实现
要求是首先了解打开文件的处理流程,然后参考本实验后续的文件读写操作的过程分析,编写在sfs_inode.c
中sfs_io_nolock
读文件中数据的实现代码。
根据实验指导书,我们可以了解到,ucore的文件系统架构主要由四部分组成:
通用文件系统访问接口层
:该层提供了一个从用户空间到文件系统的标准访问接口。这一层访问接口让应用程序能够通过一个简单的接口获得ucore内核的文件系统服务。文件系统抽象层
:向上提供一个一致的接口给内核其他部分(文件系统相关的系统调用实现模块和其他内核功能模块)访问。向下提供一个抽象函数指针列表和数据结构来屏蔽不同文件系统的实现细节。Simple FS文件系统层
:一个基于索引方式的简单文件系统实例。向上通过各种具体函数实现以对应文件系统抽象层提出的抽象函数。向下访问外设接口外设接口层
:向上提供device访问接口屏蔽不同硬件细节。向下实现访问各种具体设备驱动的接口,比如disk设备接口/串口设备接口/键盘设备接口等。
这里借助实验指导书中的一张图可以比较好的理解这四个部分的关系:
接下来分析下打开一个文件的详细处理的流程。
例如某一个应用程序需要操作文件(增删读写等),首先需要通过文件系统的通用文件系统访问接口层给用户空间提供的访问接口进入文件系统内部,接着由文件系统抽象层把访问请求转发给某一具体文件系统(比如Simple FS文件系统
),然后再由具体文件系统把应用程序的访问请求转化为对磁盘上的block的处理请求,并通过外设接口层交给磁盘驱动例程来完成具体的磁盘操作。
在介绍ucore中打开文件的具体流程之前,先简单分析下一些重要的数据结构,如下:
首先是file
数据结构:
struct file {
enum {
FD_NONE, FD_INIT, FD_OPENED, FD_CLOSED,
} status; //访问文件的执行状态
bool readable; //文件是否可读
bool writable; //文件是否可写
int fd; //文件在filemap中的索引值
off_t pos; //访问文件的当前位置
struct inode *node;//该文件对应的内存inode指针
atomic_t open_count;//打开此文件的次数
};
接下来inode
数据结构,它是位于内存的索引节点,把不同文件系统的特定索引节点信息(甚至不能算是一个索引节点)统一封装起来,避免了进程直接访问具体文件系统
struct inode {
union { //包含不同文件系统特定inode信息的union域
struct device __device_info; //设备文件系统内存inode信息
struct sfs_inode __sfs_inode_info; //SFS文件系统内存inode信息
} in_info;
enum {
inode_type_device_info = 0x1234,
inode_type_sfs_inode_info,
} in_type; //此inode所属文件系统类型
atomic_t ref_count; //此inode的引用计数
atomic_t open_count; //打开此inode对应文件的个数
struct fs *in_fs; //抽象的文件系统,包含访问文件系统的函数指针
const struct inode_ops *in_ops; //抽象的inode操作,包含访问inode的函数指针
};
对应到我们的ucore
上,具体的过程如下:
- 1、 以打开文件为例,首先用户会在进程中调用
safe_open()
函数,然后依次调用如下函数open->sys_open->syscall
,从而引发系统调用然后进入内核态,然后会由sys_open
内核函数处理系统调用,进一步调用到内核函数sysfile_open
,然后将字符串"/test/testfile"
拷贝到内核空间中的字符串path中,并进入到文件系统抽象层的处理流程完成进一步的打开文件操作中。 - 2、 在文件系统抽象层,系统会分配一个file数据结构的变量,这个变量其实是current->fs_struct->filemap[]中的一个空元素,即还没有被用来打开过文件,但是分配完了之后还不能找到对应对应的文件结点。所以系统在该层调用了
vfs_open
函数通过调用vfs_lookup
找到path对应文件的inode,然后调用vop_open
函数打开文件。然后层层返回,通过执行语句file->node=node;
,就把当前进程的current->fs_struct->filemap[fd]
(即file所指变量)的成员变量node指针指向了代表文件的索引节点node。这时返回fd。最后完成打开文件的操作。 3、 在第2步中,调用了
SFS文件系统层
的vfs_lookup
函数去寻找node,这里在sfs_inode.c
中我们能够知道.vop_lookup = sfs_lookup
,所以讲继续跟进看sfs_lookup
函数,如下:static int</