其他分享
首页 > 其他分享> > BUAA操作系统课程lab5-2流程逻辑分析

BUAA操作系统课程lab5-2流程逻辑分析

作者:互联网

BUAA操作系统课程lab5-2流程逻辑分析

一、前言

lab5-2本身的分值小,但是其理解难度和代码阅读量还是十分巨大的。为了便于大家更好的理解,我将自己个人的理解以这篇博客的形式分享出来,希望能够帮到大家!

 

二、lab5-2结构分析

整个lab5-2从内容上可以分为三个部分,第一部分磁盘抽象系统的建立,第二部分文件系统中对磁盘的映射和抽象,第三部分用户进程和文件系统的通信。接下来我将着重分析第一和第三部分的逻辑。

2.1 磁盘抽象系统的建立

真正实际存在的磁盘是lab5-1中所描述的那样仅由磁道、扇区和磁头来进行区分存储。但是为了能够大批量的系统的管理磁盘,我们就需要对磁盘进行抽象。在lab5-2中我们不关心具体抽象的实现(也就是说我们不关心每一个Block究竟如何映射到各个扇区),而只关心应当如何管理抽象出来的Block。

上面这张图就是实际磁盘抽象以后的结构。注意这张结构图很重要,如果不理解将会完全看不懂fsformat.c中的内容。接下来我将具体解释这张图。

首先整个抽象磁盘空间由若干个磁盘块构成(本系统为1024块),其中第0块是启动块,我们所要实现和理解的代码并不涉及该块,第1块是Super块,这一块记录了整个磁盘的块数,识别文件系统的魔数以及整个磁盘文件系统的根目录。第2块开始是位图块,由于我们的磁盘管理采用的是位图法,也就是说每一块是否空闲都会有一个专门的bit来记录。因此BitMap区域的大小由总块数N决定,为[N/(BY2BLK*8)]+1。注意,这里由于bitmap的块数只能是一个整数,而为了能够描述所有的N块所需要的实际块数可能是一个分数。为了达到描述每一块的目的我们只能向上取整(上式向下取整再加一效果一致),但是这样做会使bitmap部分多出来很多bit进行了虚空索敌(bushi。为了避免访问到这一部分,我们在磁盘文件系统初始化时就将这多余的一部分置成空闲状态。位图块之后紧接着便是数据块存储数据。

 

2.2用户进程和文件系统的通信

这一部分的内容指导书上的描述十分的简略,因此在这里着重分析。

首先我们要明确两个结构体——Fd与Filefd的作用和区别。当用户进程想要去对某个文件进行操作时必须要告诉文件系统这个文件的设备名和打开权限等,而为了能够方便的进行这一步我们就构造了Fd结构体用来将这两个信息打包一块传送。而最终文件系统通过一些服务函数完成了我们的要求以后就需要将实际找到的文件的一些详细信息——比如这个文件的控制块返回给我们,为了方便的进行这一步我们就构造了Filefd结构体,将这些信息打包一块传送。所以你可以看到这两个结构体实际的内容如下:

struct Fd {
       u_int fd_dev_id; //设备id
       u_int fd_offset; //该文件已经读了多少,也就是现在的光标在何处
       u_int fd_omode; //访问权限
};
struct Filefd {
       struct Fd f_fd; //该文件的Fd结构体
       u_int f_fileid; //该文件的fileid
       struct File f_file; //该文件的文件控制块c
};

值得注意的是Filefd结构体中有一个f_fileid的元素,这个元素是这个文件在文件系统打开文件中的编号,我们可以通过将这个元素传递给一些函数从而操作一些已经打开的文件。

有了这个基础,接下来我们便可以更好的理解用户进程和文件系统实际的通信过程了。

首先用户进程去访问文件系统的统一接口在user/file.c。这个文件存储了所有的用户进程可以访问文件系统的服务接口,我们通过调用其中的函数来进行文件操作。接着这些接口实际的实现在user/fsipc.c文件中,该文件中fsipc_*一类的函数便是具体的实现。如果仔细阅读代码你会发现,这些具体的函数无一例外都调用了一些奇奇怪怪的结构体——Fsreq_*样式的结构体。这些结构体的定义在include/fs.h中。这里我觉得必须说明一下,这些结构体存在的意义是什么呢?刚才说过用户进程必须告诉文件系统文件的一些基本信息,而这个信息就需要封装在一个地址起始处然后通过ipc机制传递给文件系统。为了不同文件服务的形式同一管理方便,我们就选取fsipcbuf处进行存储这些信息,而为了能够直接简单的访问到该地址处对应的元素内容我们将其封装为结构体的形式。

看到这里,你可能会有点疑惑——为什么不能直接使用Fd结构体呢?首先重新阐述一下Fd结构体体的必要性,由于用户进程需要知道自己打开了哪些文件,所以每一个打开文件都需要有一个数据结构来描述,因此我们需要Fd结构体。也正是因为Fd结构体用于描述每一个文件,因此不同的Fd结构体的存储位置是不同的(事实上他们是存储在FDTABLE起始的一大块区域上),这也就想要将不同文件服务(fsipc_*族函数)从统一的地址传递给文件系统的想法相悖。

处理完这个小插曲,然我们继续回到用户进程与文件系统的交互中。在fsreq_*函数将Fsreq_*结构体信息填写好以后接下来就会调用统一的通话函数fsipc。这个函数首先会发起一次会话将Fsreq_*结构体传递给文件系统,然后便调用ipc_recv函数,等待文件系统进程将填充好的Filefd返回。而文件系统在接收到请求以后便会根据不同的请求执行不同的服务函数,最终将Filefd发送回去。以上过程有几个需要具体说明的点:文件系统如何保证在系统开机以后的无限长时间内时刻快速接收用户进程信息并作出回复、所有的Fsreq族结构体都没有标识该次操作类型的元素,文件系统如何识别不同的操作请求。

要解答这两个问题首先让我们来看一下上述步骤中每一步所在的具体文件。文件系统的启动以及响应处理函数在fs/serv.c中,而文件系统具体执行文件操作的所有函数则在fs/fs.c文件中。

了解了文件的位置接下来便可以解答这两个问题了。

第一,文件系统如何保证在系统开机以后的无限长时间内时刻快速接收用户进程信息并作出回复?

我们阅读fs/serv.c来看看文件系统是如何工作的。仔细阅读后可以发现,文件系统在启动以后便只运行了一个主要函数——serve(),而这个serve函数是一个死循环。这样就可以保证这个进程持续不断的运行,但是无穷的循环会导致cpu压力急速升高最终系统卡死。这里文件系统便通过一个巧妙的方式避免了这一点——for循环的第一句话设置为一个ipc_recv函数。这样的话只有当用户进程向文件系统发起请求时该循环才会遍历一次。真正做到有请求才服务。

第二,所有的Fsreq族结构体都没有标识该次操作类型的元素,文件系统如何识别不同的操作请求?

观察serve函数我们可以发现,在其中处理逻辑的主体是一个switch语句,通过区分req的不同值来进行不同的文件操作。那么req又是个什么东西呢?仔细观察可以发现,req是ipc_recv的返回值也就是在ipc通信过程中传递的value值。看到这里想必你已经明白了。在ipc通信中除了将需要传递的内容通过两个地址进行交互以外发送方还可以向接收方传递一个value,这个存储在Env结构体中的value元素在这里便用于区分请求的类型。(确实很巧妙,由于lab4当时没怎么见到用这个元素我还一直以为是历史遗留的毒瘤,现在一看还是我格局太小了)

分析到这里便基本结束了。最后再附上指导书中的一个图,看完上上面这一部分的分析再看这张图应该就十分的清晰了。

 

 

标签:文件,操作系统,文件系统,BUAA,Fd,lab5,磁盘,结构,函数
来源: https://www.cnblogs.com/TenMao/p/16358389.html