实验报告
思考题
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吧。