Featured image of post 北航OS Lab5 文件系统

北航OS Lab5 文件系统

实验报告

实验报告

思考题

Thinking 5.1

可能产生的问题: 1.映射到kseg0可能会和重要内核数据或代码段发生地址冲突。

2.外设寄存器的访问需要实时性和强一致性,kseg0使用cache操作延迟较高。

3.外设寄存器的值可能会自发变化,这种变化不会被Cache记录,从而使CPU读取错误数据。

串口设备相对于IDE磁盘有更强的实时性需求,串口信息的交流若使用cache,很容易出现数据一致性问题。

Thinking 5.2

在user/include/fs.h:

1
2
#define BLOCK_SIZE PAGE_SIZE // #define PAGE_SIZE 4096
#define FILE_STRUCT_SIZE 256

还有:

1
#define FILE2BLK (BLOCK_SIZE / sizeof(struct File))

所以一个磁盘块可存储 4096/256 = 16 个文件控制块。

对于一个目录文件来说,其指向的磁盘块存储着目录项,目录项为文件所在的磁盘块号。

因此其包含1024个目录项,对应16 * 1024 = 16K 个文件。

单个文件最大时,10个直接索引对应10个磁盘块,1个间接索引对应1024个磁盘块号(前10个不用)。

也就是一个文件的内容最多共占用1024个磁盘块。

也可根据以下宏定义推算。

1
2
#define NINDIRECT (BLOCK_SIZE / 4)
#define MAXFILESIZE (NINDIRECT * BLOCK_SIZE)

即一共1024*4096 = 4MB大小。

Thinking 5.3

在fs/serv.h:

1
2
/* Maximum disk size we can handle (1GB) */
#define DISKMAX 0x40000000

注释中已经表明,我们可以处理的最大磁盘大小为1GB。

Thinking 5.4

在Thinking 5.2 与 Thinking 5.3 我给出的两段宏定义便分别出自这两个文件。 除此之外,还有许多重要的内容在这两个文件中。

在user/include/fs.h:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// 有关	block 与 file
#define BLOCK_SIZE PAGE_SIZE
#define BLOCK_SIZE_BIT (BLOCK_SIZE * 8)
#define FILE_STRUCT_SIZE 256
#define FILE2BLK (BLOCK_SIZE / sizeof(struct File))

// 关于 file
struct File {
	char f_name[MAXNAMELEN]; // filename
	uint32_t f_size;	 // file size in bytes
	uint32_t f_type;	 // file type
	uint32_t f_direct[NDIRECT];
	uint32_t f_indirect;

	struct File *f_dir; // the pointer to the dir where this file is in, valid only in memory.
	char f_pad[FILE_STRUCT_SIZE - MAXNAMELEN - (3 + NDIRECT) * 4 - sizeof(void *)];
} __attribute__((aligned(4), packed));

文件控制块的结构还是很需要熟悉的。

在fs/serv.h:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// 用于dirty
#define PTE_DIRTY 0x0004 // file system block cache is dirty
// PTE_D是可写位 PTE_DIRTY才是脏位

// 有关 sector
#define SECT_SIZE 512			  /* Bytes per disk sector */
#define SECT2BLK (BLOCK_SIZE / SECT_SIZE) /* sectors to a block */

/* Disk block n, when in memory, is mapped into the file system
 * server's address space at DISKMAP+(n*BLOCK_SIZE). */
#define DISKMAP 0x10000000

/* Maximum disk size we can handle (1GB) */
#define DISKMAX 0x40000000

extern uint32_t *bitmap; // 位图法记录空间占用

Thinking 5.5

会共享。

我并没有深研Makefile,也不知道这些xxxtest程序是如何变成user_xxx进程从而直接被init调用的,因此我选择了改装已有的程序fstest.c,至于/newmotd到底在哪,我也不得而知,但我知道它的内容是: This is a different massage of the day!\n

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <lib.h>

int main() {
	int r;
	int fdnum;
	char buf[512];
	int n;

	if ((r = open("/newmotd", O_RDWR)) < 0) {
		user_panic("wrong when open /newmotd: %d", r);
	}
	fdnum = r;
	debugf("fdnum == %d\n", fdnum); // 拿到fdnum号
	if ((n = read(fdnum, buf, 5)) < 0) {
		user_panic("wrong when read /newmotd: %d", r);
	} // 读5个字符
	debugf("read: %s\n", buf);
    struct Fd *fdd;
    fd_lookup(r, &fdd);
	debugf("fd == %x\n", fdd); // fdd的值
    debugf("now offset == %d\n",fdd->fd_offset); // 偏移量
    int id;
    if ((id = fork()) == 0) {
        if ((n = read(fdnum, buf, 5)) < 0) {
            user_panic("child: wrong when read /newmotd: %d", r);
        }
		debugf("child read: %s\n", buf);
        struct Fd *fdd;
        fd_lookup(r, &fdd);
		debugf("child: fd == %x\n", fdd);
        debugf("child: now offset == %d\n",fdd->fd_offset);
    }
    else {
        if((n = read(fdnum, buf, 5)) < 0) {
            user_panic("father: wrong when read /newmotd: %d", r);
        }
		debugf("father read: %s\n", buf);
        struct Fd *fdd;
        fd_lookup(r, &fdd);
		debugf("father: fd == %x\n", fdd);
        debugf("father: now offset == %d\n",fdd->fd_offset);
    }
}

运行结果:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
init.c: mips_init() is called
Memory size: 65536 KiB, number of pages: 16384
to memory 80430000 for struct Pages.
pmap.c:  mips vm init success
FS is running
superblock is good
read_bitmap is good
fdnum == 0    // 可能是因为第一个文件,分配了号码0
read: This    // 读到'T','h','i','s',' '
fd == 5fc00000 // 文件描述符的地址
now offset == 5 // 当前的偏移量 为 5
father read: is a  // 读到'i','s',' ','a',' '
father: fd == 5fc00000
father: now offset == 10 // 当前的偏移量 为 10
child read: diffe // 读到'd','i','f','f','e'
child: fd == 5fc00000
child: now offset == 15 // 当前的偏移量 为 15
[00000800] destroying 00000800
[00000800] free env 00000800
i am killed ... 
[00001802] destroying 00001802
[00001802] free env 00001802
i am killed ... 
panic at sched.c:45 (schedule): schedule: no runnable envs

ra:    800260a0  sp:  803ffe80  Status: 00008000
Cause: 00000420  EPC: 004002c0  BadVA:  7fd7f004
curenv:    NULL
cur_pgdir: 83fd1000

由此可以看出fork前后,父子进程会共享文件描述符和定位指针。

Thinking 5.6

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct File { // 文件控制块(元数据)
	char f_name[MAXNAMELEN]; // filename 且最大长度为128
	uint32_t f_size;	 // 文件大小的字节数
	uint32_t f_type;	 // 文件类型:普通文件FTYPE_REG与目录FTYPE_DIR
	uint32_t f_direct[NDIRECT]; // 直接索引,放着NDIRECT个磁盘块号		
	uint32_t f_indirect; // 间接索引,指向放着许多磁盘块号的磁盘块

	struct File *f_dir; // 指向所在的目录文件
	char f_pad[FILE_STRUCT_SIZE - MAXNAMELEN - (3 + NDIRECT) * 4 - sizeof(void *)];
	// 字节对齐,保证 #define FILE_STRUCT_SIZE 256
} __attribute__((aligned(4), packed));

struct Fd { // 用于描述文件操作
	u_int fd_dev_id; // 文件对应的设备id
	u_int fd_offset; // 文件当前读写的偏移量,在read/write作用较大
	u_int fd_omode; // 文件的打开方式
};

struct Filefd { // 文件操作所需的整体信息
	struct Fd f_fd; // 文件的相关描述
	u_int f_fileid;
	struct File f_file; // 文件控制块
};

Thinking 5.7

文件系统服务时序图

不同箭头的意义各不相同。

先是由init实现user_env与fs_serv进程的启动的两个箭头。 然后fs_serv进行初始化,执行serve(),开始等待用户进程提出的请求。

user_env进行fsipc,并通过IPC向fs_serv通信,fs_serv调用serv相关函数,并通过IPC返回信号给用户进程。fs_serv继续等待请求。

难点分析

lab5内容十分复杂,涉及到前面lab的很多知识,也包含大量的全新知识。 其次它的模块又大又多:

磁盘IDE的读写操作

ide_read,ide_write

磁盘上的文件系统架构

disk与block的关系,block与va的关系,block与file的关系,file与文件的关系

用户发起文件系统操作

fd与ffd的作用,file.c调用fsipc.c

serv实现文件系统操作

serv.c实现具体的文件操作

博客阅读随笔

参考博客:Wokron: 点击跳转阅读

参考博客:杨导: 点击跳转阅读


磁盘块号为bno, 则block为 disk[bno]

(disk是一个struct block数组)

struct block就是data与type两个属性


目录文件与普通文件都在BLOCK_FILE的16个FILE中

目录文件的direct与indirect指向的block都是BLOCK_FILE

普通文件的direct与indirect指向的block都是BLOCK_DATA

目录项是FILE文件,也就是该目录中的文件的意思。


文件管理系统必须是第二个进程(envs[1])。

super块必须是第二个磁盘块(disk[1])。


fsipc.c: ipc_send(envs[1].env_id, type, fsreq, PTE_D);

fsreq存处理参数:(struct Fsreq_xxx *)并页面共享

type存储value,直接到serv.c的req

serv.c: req = ipc_recv(&whom, (void *)REQVA, &perm);

实验体会

文件系统的内容确实较大,完全可以拆成文件系统的架构搭建、文件操作处理流程两个Lab进行实现。

整体的感受是任务大,又不知从何看起。跟着博客走一圈,再跟着指导书走一圈,应该需要花上不少时间才能够对这个Lab有一个比较系统的认识。

15周周二就要OS期末。可是13、14周的OO还要照旧,14周还有lab5上机等着自己。15周周末要考航概,可能乒乓也要考试了,挑战性任务也不知道是什么时候。虽然课少了好多,但是临近期末的任务量只会不减反增。

这种情况下,如果lab5的exam容易拿到分数,放弃深研lab5可能是一种选择。(虽然之前有所研究的lab也没很好的做出extra,或者说exam和extra的核心考点并不在整体架构的认识上(当然笔者对每个lab的架构也并没有多深刻的见解))

但是果然还是想要知道lab5是怎么实现的。无论如何,还有很多东西不清楚不明白,就先这样。把OO的UML先看一看,再来弥补一下lab5吧。

comments powered by Disqus
Easy Life and Easy Learning
使用 Hugo 构建
主题 StackJimmy 设计