BUAA-OS-2023-Lab5-2-Exam
完结撒花,感谢陪伴(?)
Exam - 打开相对路径文件
题目背景
在 Lab5 的课下内容中,我们实现了通过绝对路径,也即相对于磁盘根目录而言的路径,打开文件,为其获取 struct Open
与 struct File
的函数—— open
。
int open(const char *path, int mode); |
现在,为了拓展打开文件的方式,我们计划额外实现一个通过指定目录 + 相对路径打开指定文件的函数:openat
。
int openat(int dirfd, const char *path, int mode); |
在这里,我们保证:
- 目录代表的文件已经在文件管理系统中(占据
Open
)与当前进程中(占据Fd
)被打开,即可以通过其fdnum
对应的struct Fd
获取信息 - 调用
open
、openat
的所有目录/文件均存在,且openat
只会打开普通文件(非目录) - 调用
openat
打开的路径均为相对路径,即不会以/
开头
题目要求
我们可以按照通过新建 fsipc
的种类以实现目标功能。那对于实现新 fsipc_*
的全过程,有必要进行总结
user/include/fsreq.h
- 增加一个对于文件服务进程的请求类型
FSREQ_OPENAT
和请求结构体struct Fsreq_openat
;完成基础的数据结构准备
- 增加一个对于文件服务进程的请求类型
user/lib/file.c
- 仿照
open
函数实现openat
函数;供用户程序直接启动openat
流程
- 仿照
user/lib/fsipc.c
- 仿照
fsipc_open
实现fsipc_openat
;完成对Fsreq_openat
各个字段的赋值;使其能发送openat
的请求
- 仿照
fs/serv.c
:- 修改
serve
函数,使其能转发FSREQ_OPENAT
请求 - 仿照
serve_open
实现serve_openat
函数;实现openat
整体的功能
- 修改
fs/fs.c
:- 仿照
walk_path
实现walk_path_at
;从文件层面实现按相对路径path
查找文件的功能 - 仿照
file_open
实现file_openat
,类似地调用walk_path_at
函数;封装功能,供文件服务进程调用
- 仿照
- 头文件:
user/include/lib.h
:增加openat
、fsipc_openat
声明fs/serv.h
:增加file_openat
声明
过程中仍然保留了 openat
字样,实际可以根据情况自由调整。除了 fs.c
中功能具体实现的方式不同之外,基本上面几个文件的修改都是有规律的,和 Lab4 添加系统调用类似。
一种可行的做法
因为比较繁琐,所以就把提示塞代码里了,顺序是按照刚才介绍的顺序来的
user/include/fsreq.h
没什么好说的,直接写就完了
|
user/lib/file.c
供用户进程调用的 openat
函数
int openat(int dirfd, const char *path, int mode) { |
user/lib/fsipc.c
向文件服务进程发送请求的 fsipc_*
函数:
int fsipc_openat(u_int dir_fileid, const char *path, u_int omode, struct Fd *fd) { |
fs/serv.c
类似于 serve_open
的接收请求的 serve_openat
函数,同时也别忘了改 serve
函数
void serve_openat(u_int envid, struct Fsreq_openat *rq) { |
fs/fs.c
底层直接执行文件打开的函数,类似于 walk_path
& file_open
int walk_path_at(struct File *par_dir, char *path, struct File **pdir, |
思考
这个题考察了如何添加新的文件服务请求的种类,这个信息的流动通路是文件系统中的很重要一环。实现文件的相对路径打开也是比较实用的功能,可以参考这一点实现 Lab6 挑战性任务中的支持相对路径功能
Extra - 实现符号链接文件
题目背景
在 MOS 中我们只实现了两种文件类型:常规文件 & 目录文件。现在我们需要实现一种新的文件类型:符号链接文件。这类文件会指向一个存在于文件系统中的文件(也可能是符号链接文件),当使用 open
访问这些文件时,会等效于访问了他们所指向的文件,类似于 Windows 中的快捷方式
小示例
同学们可以在 Linux 终端使用 ln -s <目标文件> <链接文件的路径>
命令来创建一个符号链接。同学们可以在开发机中依次执行下面的命令来了解符号链接的概念。
$ mkdir ~/test |
继续在这个目录下创建一个名为 test_open.c
的文件,写入下面的代码。
|
使用命令 gcc test_open.c && ./a.out
来编译运行该程序,可以看到输出为2023
,即 target
文件的内容。
题目要求
- 符号链接指向的文件一定是使用绝对路径表示,并且文件一定存在;
- 符号链接文件只会指向普通文件和符号链接文件(也就是不会指向目录文件)
- 链接不会出现环
- 文件名长度合法
一种可行的做法
- 在
user/include/fs.h
中,定义新的文件类型 - 修改
tools/fsformat.c
,使得fsformat
工具在写入文件时根据传入的各个文件的类型来决定写入磁盘镜像的文件的类型(FTYPE_LNK
、FTYPE_DIR
或FTYPE_REG
)。以下是一种修改的思路:- 将
main()
函数中调用的stat()
函数改为lstat()
(参数不用改变)。stat()
或lstat()
的作用都是将一个文件的相关信息储存在statbuf
结构体中。它们的区别在于:stat()
函数会跟踪链接,会解析链接最终指向的文件;而lstat()
不会跟踪链接,直接解析链接文件本身。而我们需要读取链接文件本身的信息,所以需要使用lstat()
函数替换stat()
函数(直接改函数名即可,这两个函数都是 Linux 的库函数)。 - 修改
main()
函数,在if else
结构中增加一个分支,调用write_symlink()
函数(下面自行编写),使其不仅支持目录和普通文件的读取,还可以支持符号链接文件的读取。可以使用S_ISLNK(stat_buf.stmode)
判断命令行参数对应的文件是否为符号链接。 - 修改
write_directory()
函数,在if else
结构中增加一个分支,调用你编写的write_symlink()
函数,使其不仅支持目录和普通文件的读取,还可以支持符号链接文件的读取。结构体dirent
的成员变量d_type
可能会用到以下取值:DT_DIR
(目录)、DT_LNK
(符号链接)、DT_REG
(普通文件)。 - 可以仿照
write_file()
函数编写write_symlink()
函数,实现向磁盘镜像写入符号链接文件的功能。你可以调用 Linux 库函数int readlink(char *pathname, char *buf, int bufsiz)
来读取一个链接文件指向的目标路径(这个函数将路径pathname
处的符号链接指向的目标路径写入buf
指向的缓冲区,最多写入bufsiz
个字节,返回值是写入的字节数量。详细说明可以在开发机中使用man 2 readlink
命令查阅)。一种可能的实现框架如下所示:
- 将
void write_symlink(struct File *dirf, const char *path) { |
- 修改文件系统的实现,使其满足:用户程序使用
open()
函数打开一个符号链接文件的时候,实际上打开的是其最终指向的文件,返回最终指向的文件的文件描述符。被打开的文件可以正常地读写,就像直接打开了最终指向的文件一样。
看起来可能步骤很多,实际上就是:
- 实现一个新的文件种类,文件的内容存放的是它指向的实际文件路径(字符串)
- 在
fsformat.c
烧录磁盘文件时需要给这个链接文件分配合适大小的空间,写入合适的内容- 在 Lab5-Probe 里可能写了:我们的磁盘是通过
fsformat.c
通过数组的形式进行保存的,最后再把这个数组烧录为一个.img
文件,分配空间、些内容也都是对这个数组进行操作
- 在 Lab5-Probe 里可能写了:我们的磁盘是通过
- 在 MOS 中需要对链接文件进行处理,获取它内容中(也就是指向的文件)的文件控制块并返回
user/include/fs.h
新建一个文件种类的定义
// File types |
user/lib/file.c
修改 open
函数打开链接文件时的处理方式
while (((struct Filefd *)fd)->f_file.f_type == FTYPE_LNK) { |
tools/fsformat.c
文件烧录的过程
main
: 修改 stat
& if-else
分支
int main(int argc, char **argv) { |
编写 write_symlink
在所给模板的基础上填写内容
void write_symlink(struct File *dirf, const char *path) { |
main
& write_dirctory
:调用 write_symlink
两处都是添加新的 if-else
分支,这就不再去写了
思考
这次 Extra 没从 MOS 的实现出发,而是从文件烧录上下了功夫,从其他的角度考察了对于文件系统逻辑的把控。但说实话感觉有点偏门()