OS_Lab5_实验报告
Lab5_实验报告
一、思考题
Thinking 5.1
如果通过 kseg0 读写设备,那么对于设备的写入会缓存到 Cache 中。这是 一种错误的行为,在实际编写代码的时候这么做会引发不可预知的问题。请思考:这么做 这会引发什么问题?对于不同种类的设备(如我们提到的串口设备和 IDE 磁盘)、的操作会 有差异吗?可以从缓存的性质和缓存更新的策略来考虑。
1.访问内核时错误读取到设备内容
2.外设可能随时更新,如果在上一次缓存过后,设备的值已经发生了改变,但系统读取外设时先在 Cache 中查找到设备后返回缓存的值,那么通过缓存会访问到落后内容。
串口设备由于即时性与高使用频率,会比IDE磁盘更容易出现这样的错误
Thinking 5.2
查找代码中的相关定义,试回答一个磁盘块中最多能存储多少个文件控制块?一个目录下最多能有多少个文件?我们的文件系统支持的单个文件最大为多大?
1 |
|
(1)每个文件控制块都被数组f_pad
强制对齐为256B
一个磁盘块的容量为4KB,最多能存储16个文件控制块(FILE2BLK
)
NINDIRECT
文件控制块中间接块指针可以管理的块数量上限为 (BLOCK_SIZE / 4) = 1024
f_direct[NDIRECT]
包含10个文件指针,同时f_indirect指向一个间接磁盘块,用来存储指向文件内容的磁盘块的指针,一个指针4B,一个磁盘块大小4KB,可以,存放1024个指针(不使用间接磁盘块的前十个指针)。
(2)一个目录文件最多可以使用1024个磁盘块存储数据,因此一个目录下最多1024*16 = 16384个文件
(3)一个文件最多可以使用1024个磁盘块存储数据,因此一个文件最大容量为1024*4KB = 4MB
Thinking 5.3
请思考,在满足磁盘块缓存的设计的前提下,我们实验使用的内核支持的最大磁盘大小是多少
块缓存所在的地址空间为[0x10000000, 0x50000000)
所以内核能够支持的磁盘大小为0x40000000
,也就是1GB。
Thinking 5.4
在本实验中,fs/serv.h、user/include/fs.h 等文件中出现了许多宏定义, 试列举你认为较为重要的宏定义,同时进行解释,并描述其主要应用之处
DISKMAP 0x10000000 磁盘块映射到内存的起点
fs/serv.h
DISKMAX 0x40000000 最大磁盘大小
fs/serv.h
BLOCK_SIZE 4096
user/include/fs.h
*BLOCK_SIZE_BIT 40968 **
user/include/fs.h
NDIRECT 10
user/include/fs.h
文件控制块中直接块指针的数量,其值为10
- NINDIRECT (BLOCK_SIZE / 4)
user/include/fs.h
文件控制块中间接块指针可以管理的块数量上限(一个指针4B)
- **FILE2BLK **
user/include/fs.h
一个block块可以容纳File指针的数量
1 |
简单计算可以知道,该宏值为16,即,一个block块可以储存16个File指针
Thinking 5.5
在 Lab4“系统调用与 fork”的实验中我们实现了极为重要的 fork 函数。那 么 fork 前后的父子进程是否会共享文件描述符和定位指针呢?请在完成上述练习的基础上编写一个程序进行验证
会。一个进程所有的文件描述符都存储在[FDTABLE, FILEBASE)
这一地址空间中。在fork
函数执行时,会将这父进程页表中映射一部分地址的页表项拷贝到子进程的页表中。
1 | int main(){ |
test文件内容
1 | abcde fghi |
输出
1 | father read: "abcd" |
Thinking 5.6
请解释 File, Fd, Filefd 结构体及其各个域的作用。比如各个结构体会在哪 些过程中被使用,是否对应磁盘上的物理实体还是单纯的内存数据等。说明形式自定,要求简洁明了,可大致勾勒出文件系统数据结构与物理实体的对应关系与设计框架。
- File结构体定义及各个域的作用
1 | struct File { |
对应磁盘的物理实体,文件系统进行实际操作的对象void file_*(struct File **file)
- Fd结构体定义及各个域的作用
1 | struct Fd { |
仅为内存数据,主要是用户使用文件描述符对文件进行操作
- Filefd的结构体定义及各个域的作用
1 | struct Filefd { |
对应磁盘的物理实体,也包含内存数据,将Fd*
强制转换为Filefd*
从而获取到文件控制块,获得更多文件信息
Thinking 5.7
//懒得放图
图中有多种不同形式的箭头,请解释这些不同箭头的差别,并思考我们的操作系统是如何实现对应类型的进程间通信
UML时序图中有以下几种箭头
消息类型 | 表示 |
---|---|
同步消息 | 用黑三角箭头搭配黑实线表示 |
异步消息 | 用两条小线的开箭头和黑色实线表示 |
返回消息 | 用黑三角箭头搭配黑色虚线表示 |
创建消息 | 用开三角箭头搭配黑实线表示,其下面特别注明 <<create>> |
摧毁消息 | 用黑三角箭头搭配黑实线表示,其下面特别注明 <<destroy>> |
Lost and Found Message | 用一个黑色实心的点和黑色实心三角箭头黑实线表示 |
通过IPC来实现进程间通信的,发送方先调用ipc_send
函数,当接收方成功接收到消息时,ipc_send
函数跳出循环结束,这时发送方再调用ipc_recv
函数等待接返回信息。
1 | static int fsipc(u_int type, void *fsreq, void *dstva, u_int *perm) { |
//懒得放图
请求消息,用户向文件系统发送请求
fsipc(FSREQ_*, 0, 0)
,通过发送特定调用号FSREQ_*
的值,使得文件系统可以获取serve_table[req]
中对于请求类型,进入相应处理函数中处理
//懒得放图
返回消息,文件系统对于用户请求提供返回值
二、实验难点
1.用户进程和文件系统服务进程的交互
fs/serve.c
对请求分发处理
fsipc()
—send—>serve()
->serve_*()
处理请求并发送响应
在serve_*()
中调用
file_*()
fs.c中的文件服务函数
ipc_send()
—send—>fsipc()
user/lib/fd.c
有一个static struct Dev *devtab[]
用户接口 close()、write()、read()、fstat()
调用dev->dev_*
1 | //居然是等价的 |
user/lib/file.c
1 | static int file_close(struct Fd *fd); |
file_read、file_write、file_stat
只在内存空间行动
调用用户库函数,读写文件内容或文件信息
open、remove、file_close
用户进程请求文件服务 调用fsipc_*
user/lib/fsipc.c
fsipc_*
会返回调用fsipc(FSREQ_*, req, void *dstva, u_int *perm)
1 | static int fsipc(u_int type, void *fsreq, void *dstva, u_int *perm) { |
2.文件系统的地址空间
文件服务进程的块缓存所在的地址空间为[DISKMAP, DISKMAP + DISKMAX)
用户进程所有文件描述符所在的地址空间为 [FILEBASE - PDMAP, FILEBASE)
用户进程文件描述符对应文件内容整体的映射区间为 [FILEBASE, FILEBASE+1024*PDMAP)
三、实验体会
本单元文件系统内容之多、难度之深、时间之紧让我叹服,还好只上一次机,如果有机会,期末考试结束了会回头再看看的……
宜将剩勇追穷寇,不可沽名学霸王
Lab5上机(5.29)、
航概考试(6.2)、OS理论考试(6.7)、挑战性任务(十八周)哥们来了!!
笑死了extra又挂了!!!!!!!!!!!!!!!!!!!!!!!!!!!!