思考题 1: 文件的数据块使用基数树的形式组织有什么好处? 除此之外还有其他的数据块存储方式吗?
-
用基数树的形式组织的好处
- 减小了inode的大小,inode存储所占用的空间更少
-
其他的数据块存储方式
- Unix V6文件系统:在inode中存储所有direct block和indirect block的block_id
- Ext4:使用了区段树的方法,保存起始块地址以及长度
练习题 2:实现位于
userland/servers/tmpfs/tmpfs.c
的tfs_mknod
和tfs_namex
。
tfs_mknod
- 根据
mkdir
的值去判断创建文件/目录的inode - 创建对应的dentry
- 根据
tfs_namex
- 用
/
分割name的各个部分 - 调用
tfs_lookup
来寻找dentry - 更新相关变量
- 用
练习题 3:实现位于
userland/servers/tmpfs/tmpfs.c
的tfs_file_read
和tfs_file_write
。提示:由于数据块的大小为PAGE_SIZE,因此读写可能会牵涉到多个页面。读取不能超过文件大小,而写入可能会增加文件大小(也可能需要创建新的数据块)。
tfs_file_read
- 首先判断要读取的位置
cur_off+size
是否大于inode的size,若是则更新最大读取size - 在每一个data block中读取数据
- 首先判断要读取的位置
tfs_file_write
- 读取每一个data block中的数据
- 若
cur_off - offset
的值大于inode的size,则更新inode的size
练习题 4:实现位于
userland/servers/tmpfs/tmpfs.c
的tfs_load_image
函数。需要通过之前实现的tmpfs函数进行目录和文件的创建,以及数据的读写。
- 将
dirat
设置为tmpfs_root
,调用tfs_namex
来定位文件 - 调用
tfs_lookup
来获取对应文件的dentry(若不存在则创建对应文件) - 调用
tfs_file_write
将对应的数据写入文件 - 重复上述过程
练习题 5:利用
userland/servers/tmpfs/tmpfs.c
中已经实现的函数,完成在userland/servers/tmpfs/tmpfs_ops.c
中的fs_creat
、tmpfs_unlink
和tmpfs_mkdir
函数,从而使tmpfs_*
函数可以被fs_server_dispatch
调用以提供系统服务。对应关系可以参照userland/servers/tmpfs/tmpfs_ops.c
中server_ops
的设置以及userland/fs_base/fs_wrapper.c
的fs_server_dispatch
函数。
-
fs_creat
调用
tfs_namex
以及tfs_creat
-
tmpfs_unlink
调用
tfs_namex
以及tfs_remove
-
tmpfs_mkdir
调用
tfs_namex
以及tfs_mkdir
练习题 6:补全
libchcore/src/libc/fs.c
与libchcore/include/libc/FILE.h
文件,以实现fopen
,fwrite
,fread
,fclose
,fscanf
,fprintf
五个函数,函数用法应与libc中一致。
fopen
- 填充
fs_request
和ipc_msg
- 调用
ipc_call
发送相应FS_REQ_OPEN
类型的ipc_msg
- 判断返回值
- 和
fr
的new_fd
值相同:不做处理 - 若
fr
的new_fd
值不同:- 模式为"w":发送
FS_REQ_OPEN
类型的ipc_msg
来创建文件,然后再进行操作2 - 否则报错
- 模式为"w":发送
- 和
- 设置FILE,返回
- 填充
fwrite
- 填充
fs_request
和ipc_msg
- 调用
ipc_call
发送相应FS_REQ_WRITE
类型的ipc_msg
- 返回写入的字节数
- 填充
fread
- 填充
fs_request
和ipc_msg
- 调用
ipc_call
发送相应FS_REQ_READ
类型的ipc_msg
- 返回读取的字节数
- 填充
fclose
- 填充
fs_request
和ipc_msg
- 调用
ipc_call
发送相应FS_REQ_CLOSE
类型的ipc_msg
- 填充
fscanf
- 首先调用
fread
来读取文件中的内容 - 分
%s
,%d
还有其他状况分别进行处理:%s
:以字符串形式读出%d
:以数字类型读出- 其它:更新cursor位置
- 首先调用
fprintf
- 分
%s
,%d
还有其他状况分别进行处理:%s
:以字符串形式写入%d
:以数字类型写入- 其它:将原本的字符写入并更新cursor位置
- 最后调用
fwrite
写入文件
- 分
练习题 7:实现在
userland/servers/shell/main.c
中定义的getch
,该函数会每次从标准输入中获取字符,并实现在userland/servers/shell/shell.c
中的readline
,该函数会将按下回车键之前的输入内容存入内存缓冲区。代码中可以使用在libchcore/include/libc/stdio.h
中的定义的I/O函数。
-
getch
调用
__chcore_sys_getc
-
readline
判断字符类型:
- 若为
\t
,调用do_complement
进行补全,并打印 - 若为
\n
,退出循环停止读入,返回 - 其它情况则正常读入
- 若为
练习题 8:根据在
userland/servers/shell/shell.c
中实现好的bultin_cmd
函数,完成shell中内置命令对应的do_*
函数,需要支持的命令包括:ls [dir]
、echo [string]
、cat [filename]
和top
。
print_file_content
- 调用
fopen
打开对应的文件 - 调用
fread
读取文件内容 - 依次打印读出来的内容
- 调用
fs_scan
- 调用
fopen
打开相应的目录 - 仿照
demo_gendents
的方式进行scan并打印
- 调用
do_echo
- 首先跳过"echo"
- 再跳过空白字符
- 打印后续内容
练习题 9:实现在
userland/servers/shell/shell.c
中定义的run_cmd
,以通过输入文件名来运行可执行文件,同时补全do_complement
函数并修改readline
函数,以支持按tab键自动补全根目录(/
)下的文件名。
-
run_cmd
调用
chcore_procm_spawn
,传入fs_server_cap
-
do_complement
- 打开根目录,调用
getdents
读取根目录下的dentry - 根据
complement_time
来遍历,选中相应的文件名
- 打开根目录,调用
练习题 10:FSM需要两种不同的文件系统才能体现其特点,本实验提供了一个fakefs用于模拟部分文件系统的接口,测试代码会默认将tmpfs挂载到路径
/
,并将fakefs挂载在到路径/fakefs
。本练习需要实现userland/server/fsm/main.c
中空缺的部分,使得用户程序将文件系统请求发送给FSM后,FSM根据访问路径向对应文件系统发起请求,并将结果返回给用户程序。实现过程中可以使用userland/server/fsm
目录下已经实现的函数。
将参数中的ipc_msg
拷贝至FSM专有的ipc_msg
中去,再根据mpinfo
调用ipc_call
同时需要进行一定的特殊处理:
FS_REQ_OPEN
需要在获取fd后调用fsm_set_mount_info_withfd
进行fd相关设置FS_REQ_READ
需要在完成ipc_call
之后将读取的文件内容拷贝到参数ipc_msg
中FS_REQ_GETDENT64
需要在完成ipc_call
之后将读取的dentry信息拷贝回参数ipc_msg
中