「BUAA OS Lab5」名词解释

Posted by saltyfishyjk on 2022-05-31
Words 5.8k and Reading Time 29 Minutes
Viewed Times

「BUAA OS Lab5」名词解释

变量和类型

Block

磁盘块,虚拟概念,将相邻的扇区组合在一起,减小由于扇区过多带来的寻址困难。

磁盘块控制块

位置:fs/fsformat.c

1
2
3
4
5
struct Block
{
uint8_t data[BY2BLK];
uint32_t type;
};

其中,uint8_tunsigned charuint32_tunsigned

1
2
3
4
5
6
7
8
9
10
enum
{
BLOCK_FREE = 0,
BLOCK_BOOT = 1,
BLOCK_BMAP = 2,
BLOCK_SUPER = 3,
BLOCK_DATA = 4,
BLOCK_FILE = 5,
BLOCK_INDEX = 6,
};

可能的类型以枚举类的形式给出。

// Parameters:
// diskno: disk number.
// secno: start sector number.
// dst: destination for data read from IDE disk.
// nsecs: the number of sectors to read.


disk

位置:fs/fsformat.c

1
2
3
4
5
struct Block
{
uint8_t data[BY2BLK];
uint32_t type;
} disk[NBLOCK];

磁盘块控制块对象,保存了NBLOCKBlock对象,与磁盘块一一对应,方便我们通过disk加上offset快速找到物理磁盘块对应的控制块数据结构。

  • data:用来索引具体的字节
  • type:这个磁盘块所属类型

值得注意的是,为了便于计算,我们不使用间接磁盘块的前10个指针。举例而言:对于一个给定的File结构体,我们通过f_size/BY2BLK计算得到该文件占用磁盘块数i后,如果i > NDIRECT,即,不止使用了直接磁盘块,这时我们要索引j >= NDIRECT的磁盘块的时候就需要先索引f_indirect,得到间接磁盘块的管理块号,通过disk加上f_indirect索引到具体的这个磁盘块的控制块。接着,我们直接用data+j的定位方式检索到对应的磁盘块号。注意,这里检索用的偏移是j,而不是j - NDIRECT,因此,我们简化了计算。稍微多思考一下我们会发现,这里的操作印证了为什么一个文件的最大大小是


Super

超级块Super Block,用来描述文件系统的基本信息

1
2
3
4
5
struct Super {
u_int s_magic; // Magic number: FS_MAGIC
u_int s_nblocks; // Total number of blocks on disk
struct File s_root; // Root directory node
}

s_magic:魔数,用于识别该文件系统,是一个常量

s_nblocks:记录本文件系统有多少个磁盘块,在本文件系统中是1024

s_root:根目录,其f_typeFTYPE_DIRf_name/


super

位置:fs/fs.c

超级块

1
struct Super *super;

nextbno

下一个可用的block块号

位置:fs/fsformat.c


nbitblock

标记磁盘所有块的实用信息,其值为1,即,需要一个磁盘块储存位图管理信息

1
nbitblock = (NBLOCK + BIT2BLK - 1) / BIT2BLK;

或许,你对分子中的BIT2BLK - 1心存疑虑,事实上,其效果为NBLOCK/BIT2BLK的答案向上取整


NBLOCK

位置:fs/fsformat.c

表示文件系统有多少个磁盘块,其值为1024

1
#define NBLOCK 1024 // The number of blocks in the disk.

BY2BLK

位置:fs.h

byte to block ,一块字节数,其值和BY2PG(一页字节数)相同,均为4096

1
#define BY2BLK BY2PG

BIT2BLK

bit to block,一块位数,其值位BY2BLK的8倍(1 byte = 8 bit),32768

1
#define BIT2BLK (BY2BLK * 8)

FILE2BLK

一个block块可以容纳File指针的数量

位置:include/fs.h

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

简单计算可以知道,该宏值为16,即,一个block块可以储存16个File指针


MAXNAMELEN

文件名最大长度,其值为128,即,文件名不可以超过128个字符。事实上,由于最后一个字符必须是\0,因此,“有效”字符串最大长度为127.

位置:include/fs.h

1
2
// Maximum size of a filename (a single path component), including null
#define MAXNAMELEN 128

MAXPATHLEN

位置:include/fs.h

1
#define MAXPATHLEN 1024

NDIRECT

文件控制块中直接块指针的数量,其值为10

位置:include/fs.h

1
2
// Number of (direct) block pointers in a File descriptor
#define NDIRECT 10

NINDIRECT

文件控制块中间接块指针可以管理的块数量上限

位置:include/fs.h

1
#define NINDIRECT (BY2BLK / 4)

FTYPE_REG

regular file, 常规文件

位置:include/fs.h

1
#define FTYPE_REG 0 // Regular file

FTYPE_DIR

directory file,文件夹文件

位置:include/fs.h

1
#define FTYPE_DIR 1 // Directory

FTYPE_BIN

binary file,二进制文件

位置:include/fs.h


File

文件控制块

1
2
3
4
5
6
7
8
9
10
struct File
{
u_char f_name[MAXNAMELEN]; // filename
u_int f_size; // file size in bytes
u_int f_type; // file type
u_int f_direct[NDIRECT];
u_int f_indirect;
struct File *f_dir; // valid only in memory
u_char f_pad[256 - MAXNAMELEN - 4 - 4 - NDIRECT * 4 - 4 - 4];
};

其中,f_type为文件类型,有普通文件FTYPE_REG和文件夹FTYPE_DIR两种

f_direct[NDIRECT]用来记录文件的

数据块在磁盘上的位置,设有十个直接指针,计算可以知道我们的十个直接指针可以表示最大40KB的文件,而当文件大于40KB时,需要用到间接指针。

f_indirect是间接指针,指向一个间接磁盘块,用来存储指向文件内容的磁盘块的指针。为了简化计算,我们不使用简介磁盘块的前十个指针。

f_dir指向文件所属的文件目录。

f_pad是为了使得整个结构体大小是2的幂数,以便于整数个结构体占用一个磁盘块,占用剩下的字节。(利用了char是一个字节的特点)。

对于一个目录型文件,其结构如下:


FS_MAGIC

位置:include/fs.h

魔数,好像是OS课程代码,表示有效性。

1
#define FS_MAGIC 0x68286097 // Everyone's favorite OS class

DISKMAP

位置:fs/fs.h

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

DISKMAX

位置:fs/fs.h

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

Fd

File Descripter,文件描述符

位置:user/fd.h

1
2
3
4
5
6
struct Fd
{
u_int fd_dev_id;
u_int fd_offset;
u_int fd_omode;
};

Filefd

1
2
3
4
5
6
struct Filefd
{
struct Fd f_fd;
u_int f_fileid;
struct File f_file;
};

MAXFD

最大文件控制符数量。我们的MOS能够管理的中最多文件控制符是32个

1
#define MAXFD 32

FILEBASE

文件基地址

1
#define FILEBASE 0x60000000

FDTABLE

fd基地址

1
#define FDTABLE (FILEBASE - PDMAP)

FSREQ_*

file system request相关宏定义

1
2
3
4
5
6
7
8
#define FSREQ_OPEN 1
#define FSREQ_MAP 2
#define FSREQ_SET_SIZE 3
#define FSREQ_CLOSE 4
#define FSREQ_DIRTY 5
#define FSREQ_REMOVE 6
#define FSREQ_SYNC 7
#define FSREQ_CREATE 8

Fsreq_*

file system request相关结构体,保存相应request所需要的信息。如,open需要路径path和模式omode,所以Fsreq_open结构体包含了这两项内容。

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
struct Fsreq_open
{
char req_path[MAXPATHLEN];
u_int req_omode;
};

struct Fsreq_create
{
char req_path[MAXPATHLEN];
int type;
};

struct Fsreq_map
{
int req_fileid;
u_int req_offset;
};

struct Fsreq_set_size
{
int req_fileid;
u_int req_size;
};

struct Fsreq_close
{
int req_fileid;
};

struct Fsreq_dirty
{
int req_fileid;
u_int req_offset;
};

struct Fsreq_remove
{
u_char req_path[MAXPATHLEN];
};

函数和宏函数

fs函数调用关系

这是fs相关函数的调用关系,太抽象了,难蚌。


read_sector

内核态驱动,该函数为示意,不在代码中真实存在

1
2
3
4
5
6
7
8
9
10
11
# read sector at specified offset from the beginning of the disk image. 
LEAF(read_sector)
sw a0, 0xB3000010 # select the IDE id.
sw a1, 0xB3000000 # offset.
li t0, 0
sb t0, 0xB3000020 # start read.
lw v0, 0xB3000030
nop
jr ra
nop
END(read_sector)

sys_write_dev

系统调用写设备

位置:lib/syscall_all.c

dev意味device,设备

1
2
3
4
5
6
7
8
9
10
11
12
int sys_write_dev(int sysno, u_int va, u_int dev, u_int len)
{
// Your code here
if (dev >= 0x10000000 && dev + len <= 0x10000020 ||
dev >= 0x13000000 && dev + len <= 0x13004200 ||
dev >= 0x15000000 && dev + len <= 0x15000200)
{
bcopy(va, 0xa0000000 + dev, len);
return 0;
}
return -E_INVAL;
}

系统调用函数syscall_write_dev的实现,将从va开始长度为len的字节拷贝到0xa000,0000 + dev虚拟地址处(实际上,dev就是设备的物理地址,但得益于我们的虚拟地址机制,这里需要引用的是映射到kseg1的虚拟地址空间的地址)


sys_read_dev

系统调用读设备

位置:lib/syscall_.c

1
2
3
4
5
6
7
8
9
10
11
12
int sys_read_dev(int sysno, u_int va, u_int dev, u_int len)
{
// Your code here
if (dev >= 0x10000000 && dev + len <= 0x10000020 ||
dev >= 0x13000000 && dev + len <= 0x13004200 ||
dev >= 0x15000000 && dev + len <= 0x15000200)
{
bcopy(0xa0000000 + dev, va, len);
return 0;
}
return -E_INVAL;
}

类似sys_write_dev


ide_write

写IDE磁盘,具体地,我们依赖下图,通过在特定位置写入指定内容

image-20220601200917606

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
void
ide_write(u_int diskno, u_int secno, void *src, u_int nsecs)
{
// Your code here
int offset_begin = secno * 0x200;
int offset_end = offset_begin + nsecs * 0x200;
int offset = 0;
int offset_now = 0;
int op_status = 0;
int write = 1;
int can_read = 0;

// DO NOT DELETE WRITEF !!!
writef("diskno: %d\n", diskno);

while (offset_begin + offset < offset_end) {
// copy data from source array to disk buffer.
offset_now = offset_begin + offset;
if (syscall_write_dev(&diskno, 0x13000010, 4) != 0) {
user_panic("write failed!\n");
}
if (syscall_write_dev(&offset_now, 0x13000000, 4) != 0) {
user_panic("write failed!\n");
}
if (syscall_write_dev(src + offset, 0x13004000, 0x200) != 0) {
user_panic("read failed!\n");
}
if (syscall_write_dev(&write, 0x13000020, 4) != 0) {
user_panic("write failed!\n");
}
if (syscall_read_dev(&op_status, 0x13000030, 4) != 0) {
user_panic("write failed!");
}
if (op_status == 0) {
user_panic("read failed!\n");
}

offset += 0x200;
// if error occur, then panic.
}
}

ide_read

读IDE磁盘

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
44
void
ide_read(u_int diskno, u_int secno, void *dst, u_int nsecs)
{
// 0x200: the size of a sector: 512 bytes.
int offset_begin = secno * 0x200;
int offset_end = offset_begin + nsecs * 0x200;
int offset = 0;

u_int zero = 0;
u_int cur_offset = 0;
int op_status = 0;
int read = 0;
int can_read = 0;
while (offset_begin + offset < offset_end) {
// Your code here
cur_offset = offset_begin + offset; // calc current offset
/* set diskno */
if (syscall_write_dev(&diskno, 0x13000010, 4) != 0) {
user_panic("write failed!\n");
}
/* set offset */
if (syscall_write_dev(&cur_offset, 0x13000000, 4) != 0) {
user_panic("write failed!\n");
}
/* set value */
if (syscall_write_dev(&read, 0x13000020, 4) != 0) {
user_panic("write failed!\n");
}

if (syscall_read_dev(&op_status, 0x13000030, 4) != 0) {
user_panic("write failed!\n");
}
if (op_status == 0) {
user_panic("read failed!\n");
}
if (syscall_read_dev(dst + offset, 0x13004000, 0x200) != 0) {
user_panic("read failed!\n");
}

offset += 0x200;

// error occurred, then panic.
}
}

init_disk

初始化磁盘块。具体的代码含义在注释中给出

位置:fs/fsformat.c

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
// Initial the disk. Do some work with bitmap and super block.
void init_disk() {
int i, r, diff;

// Step 1: Mark boot sector block.
// 标记0号块为BLOCK_BOOT
disk[0].type = BLOCK_BOOT;

// Step 2: Initialize boundary.
// 计算使用位图管理法需要多少磁盘块,这里算出来应该是1
nbitblock = (NBLOCK + BIT2BLK - 1) / BIT2BLK;
// 设置下一个空闲块为2 + nbitblock。第一个块我们为boot,第二个块为超级块,后续紧跟的nbitblock个块为bitmap位图管理用的(事实上应该是就一个块)
nextbno = 2 + nbitblock;

// Step 2: Initialize bitmap blocks.
// 设置位图管理用的磁盘块
for(i = 0; i < nbitblock; ++i) {
disk[2+i].type = BLOCK_BMAP;
}
// 将位图管理用的磁盘块的所有位置1,表示所有块都可用
for(i = 0; i < nbitblock; ++i) {
memset(disk[2+i].data, 0xff, BY2BLK);
}
if(NBLOCK != nbitblock * BIT2BLK) {
diff = NBLOCK % BIT2BLK / 8;
memset(disk[2+(nbitblock-1)].data+diff, 0x00, BY2BLK - diff);
}

// Step 3: Initialize super block.
// 设置超级块
disk[1].type = BLOCK_SUPER;
super.s_magic = FS_MAGIC;
super.s_nblocks = NBLOCK;
super.s_root.f_type = FTYPE_DIR;
strcpy(super.s_root.f_name, "/");
}

write_file

dirf下面写入path表示的文件

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
void write_file(struct File *dirf, const char *path) {
int iblk = 0, r = 0, n = sizeof(disk[0].data);
uint8_t buffer[n+1], *dist;
// 调用creat_file,在dirf下新建一个文件索引结构体
struct File *target = create_file(dirf);

/* in case `create_file` is't filled */
if (target == NULL) return;

int fd = open(path, O_RDONLY);

// Get file name with no path prefix.
// 获取文件名,即去掉路径中的dir/subdir/等
const char *fname = strrchr(path, '/');
if(fname)
fname++;
else
fname = path;
strcpy(target->f_name, fname);

// 调用lseek获取文件大小
target->f_size = lseek(fd, 0, SEEK_END);
// 设置文件类型为普通文件
target->f_type = FTYPE_REG;


// Start reading file.
lseek(fd, 0, SEEK_SET);
// 循环向disk[nextbno]写入内容
while((r = read(fd, disk[nextbno].data, n)) > 0) {
// 将块连接到新创建的文件中,iblk指的是文件中存在的引用指针数
save_block_link(target, iblk++, next_block(BLOCK_DATA));
}
close(fd); // Close file descriptor.
}

block_is_free

位置:fs/fs.c

通过位图的特定位来判断指定的磁盘块是否被占用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int block_is_free(u_int blockno)
{
if (super == 0 || blockno >= super->s_nblocks)
{
return 0;
}

if (bitmap[blockno / 32] & (1 << (blockno % 32)))
{
return 1;
}

return 0;
}

create_file

在dirf目录文件下,创建一个新的文件索引

位置:fs/fsformat.c

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
```

***

### `save_block_link`



```C
// Save block link.
void save_block_link(struct File *f, int nblk, int bno)
{
assert(nblk < NINDIRECT); // if not, file is too large !

if (nblk < NDIRECT)
{
f->f_direct[nblk] = bno;
}
else
{
if (f->f_indirect == 0)
{
// create new indirect block.
f->f_indirect = next_block(BLOCK_INDEX);
}
((uint32_t *)(disk[f->f_indirect].data))[nblk] = bno;
}
}

创建相邻块

1
2
3
4
5
6
7
// Make new block contians link to files in a directory.
int make_link_block(struct File *dirf, int nblk)
{
save_block_link(dirf, nblk, nextbno);
dirf->f_size += BY2BLK;
return next_block(BLOCK_FILE);
}

flush_bitmap

把nextbno之前的所有块对应的bitmap标记为0,即,不可用

1
2
3
4
5
6
7
8
// Flush disk block usage to bitmap.
void flush_bitmap() {
int i;
// update bitmap, mark all bit where corresponding block is used.
for(i = 0; i < nextbno; ++i) {
((uint32_t *)disk[2+i/BIT2BLK].data)[(i%BIT2BLK)/32] &= ~(1<<(i%32));
}
}

finish_fs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Finish all work, dump block array into physical file.
void finish_fs(char *name) {
int fd, i, k, n, r;
uint32_t *p;

// Prepare super block.
// 将超级块的内容拷贝到第二块block中
memcpy(disk[1].data, &super, sizeof(super));

// Dump data in `disk` to target image file.
// 用open打开name问年间
fd = open(name, O_RDWR|O_CREAT, 0666);
for(i = 0; i < 1024; ++i) {
// 大小端转换
reverse_block(disk+i);
// 写文件
write(fd, disk[i].data, BY2BLK);
}

// Finish.
close(fd);
}

diskaddr

根据给定磁盘块号计算对应的虚拟地址

位置:fs/fs.c

1
2
3
4
5
6
u_int diskaddr(u_int blockno)
{
if (super != NULL && blockno > super->s_nblocks)
user_panic("diskaddr panic");
return DISKMAP + blockno * BY2BLK;
}

block_is_mapped

判断块是否已经映射到虚拟地址了

1
2
3
4
5
6
7
8
9
u_int block_is_mapped(u_int blockno)
{
u_int va = diskaddr(blockno);
if (va_is_mapped(va))
{
return va;
}
return 0;
}

write_block

写磁盘块。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void write_block(u_int blockno)
{
u_int va;

// Step 1: detect is this block is mapped, if not, can't write it's data to disk.
// 判断磁盘块是否已经映射,没有映射说明错误
if (!block_is_mapped(blockno))
{
user_panic("write unmapped block %08x", blockno);
}

// Step2: write data to IDE disk. (using ide_write, and the diskno is 0)
// 获取磁盘块对应的虚拟地址,将虚拟地址对应的内容写回到磁盘块
va = diskaddr(blockno);
ide_write(0, blockno * SECT2BLK, (void *)va, SECT2BLK);

syscall_mem_map(0, va, 0, va, (PTE_V | PTE_R | PTE_LIBRARY));
}

map_block

检查指定的磁盘块是否已经映射到内存,如果没有,分配一页内存来保存磁盘上的数据

位置:fs/fs.c

1
2
3
4
5
6
7
8
9
10
11
12
int
map_block(u_int blockno)
{
// Step 1: Decide whether this block has already mapped to a page of physical memory.
// 根据给定磁盘块判断是否已经映射到虚拟地址了
if (block_is_mapped(blockno)) {
return 0;
}
// 如果没有映射到虚拟地址,为blockno对应的虚拟地址分配并映射一页
// Step 2: Alloc a page of memory for this block via syscall.
return syscall_mem_alloc(0, diskaddr(blockno), PTE_R | PTE_V);
}

unmap_block

取消磁盘块映射

位置:fs/fs.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void
unmap_block(u_int blockno)
{
int r;
u_int addr;
// Step 1: check if this block is mapped.
// 检查磁盘块是否确实已经被映射
addr = block_is_mapped(blockno);
// Step 2: use block_is_free�~Lblock_is_dirty to check block,
// if this block is used(not free) and dirty, it needs to be synced to disk: write_block
// can't be unmap directly.
// 如果磁盘块非free且已经被改写过,进行写块操作
if (!block_is_free(blockno) && block_is_dirty(blockno)) {
write_block(blockno);
}
// 取消磁盘块对应虚拟地址的映射
// Step 3: use 'syscall_mem_unmap' to unmap corresponding virtual memory.
r = syscall_mem_unmap(0, addr);
if (r < 0) {
return r;
}
// Step 4: validate result of this unmap operation.
user_assert(!block_is_mapped(blockno));
}

va_is_mapped

位置:fs/fs.c

判断虚拟地址是否已经映射到块

1
2
3
4
u_int va_is_mapped(u_int va)
{
return (((*vpd)[PDX(va)] & (PTE_V)) && ((*vpt)[VPN(va)] & (PTE_V)));
}

read_block

读磁盘块。根据给定磁盘块号,判断该块内容是否已经在内存中,如果不在,则分配一页内存并将内容拷贝到该页。

位置:fs/fs.c

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
int read_block(u_int blockno, void **blk, u_int *isnew)
{
u_int va;

// Step 1: validate blockno. Make file the block to read is within the disk.
// 检查super是否为空,非空时blockno是否超过超级块控制的上限,返回对应的错误。
if (super && blockno >= super->s_nblocks)
{
user_panic("reading non-existent block %08x\n", blockno);
}

// Step 2: validate this block is used, not free.
// Hint:
// If the bitmap is NULL, indicate that we haven't read bitmap from disk to memory
// until now. So, before we check if a block is free using `block_is_free`, we must
// ensure that the bitmap blocks are already read from the disk to memory.
// 判断bitmap是否加载,接着判断该块对应是否为空块
if (bitmap && block_is_free(blockno))
{
user_panic("reading free block %08x\n", blockno);
}

// Step 3: transform block number to corresponding virtual address.
// 调用diskaddr,将磁盘块号转为其在当前进程的虚拟地址
va = diskaddr(blockno);

// Step 4: read disk and set *isnew.
// Hint: if this block is already mapped, just set *isnew, else alloc memory and
// read data from IDE disk (use `syscall_mem_alloc` and `ide_read`).
// We have only one IDE disk, so the diskno of ide_read should be 0.
// 如果块已经映射了
if (block_is_mapped(blockno))
{ //the block is in memory
// 如果isnew不是空指针
if (isnew)
{
// 设置isnew为假
*isnew = 0;
}
}
else
{ //the block is not in memory
if (isnew)
{
// 设置isnew为真,即,分配了新内存页
*isnew = 1;
}
// 申请一个新的页
syscall_mem_alloc(0, va, PTE_V | PTE_R);
// 将对应块的内容调到该页上
ide_read(0, blockno * SECT2BLK, (void *)va, SECT2BLK);
}

// Step 5: if blk != NULL, set `va` to *blk.
// 如果blk非空,返回其在内存中挂载的位置
if (blk)
{
*blk = (void *)va;
}
return 0;
}

alloc_block_num

检索位图bitmap寻找空块并申请,如果成功申请则返回块号,否则返回-E_NO_DISK表示块已经用完

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int alloc_block_num(void)
{
int blockno;
// walk through this bitmap, find a free one and mark it as used, then sync
// this block to IDE disk (using `write_block`) from memory.
for (blockno = 3; blockno < super->s_nblocks; blockno++)
{
if (bitmap[blockno / 32] & (1 << (blockno % 32)))
{ //the block is free
bitmap[blockno / 32] &= ~(1 << (blockno % 32));
write_block(blockno / BIT2BLK); // write to disk.
return blockno;
}
}
// no free blocks.
return -E_NO_DISK;
}

alloc_block

申请一个新的block并返回块号

位置:fs/fs.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int alloc_block(void)
{
int r, bno;
// Step 1: find a free block.
if ((r = alloc_block_num()) < 0)
{ // failed.
return r;
}
bno = r;

// Step 2: map this block into memory.
if ((r = map_block(bno)) < 0)
{
free_block(bno);
return r;
}

// Step 3: return block number.
return bno;
}

file_block_walk

在一个File索引中,找到filebno对应的索引指针

位置:fs/fs.c

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
44
45
46
47
48
49
50
51
52
53
int file_block_walk(struct File *f, u_int filebno, u_int **ppdiskbno, u_int alloc)
{
int r;
u_int *ptr;
void *blk;

if (filebno < NDIRECT)
{
// Step 1: if the target block is corresponded to a direct pointer, just return the
// disk block number.
// 如果索引号小于最大直接指针数量,可以直接返回
ptr = &f->f_direct[filebno];
}
else if (filebno < NINDIRECT)
{
// Step 2: if the target block is corresponded to the indirect block, but there's no
// indirect block and `alloc` is set, create the indirect block.
// 如果索引号大于10,说明需要检索间接索引
if (f->f_indirect == 0)
{
// 间接索引块不存在
if (alloc == 0)
{
// 索引块不存在并且参数表示不允许创建一个新块,返回错误值。
return -E_NOT_FOUND;
}
// 试图分配一个间接索引块
if ((r = alloc_block()) < 0)
{
return r;
}
f->f_indirect = r;
}

// Step 3: read the new indirect block to memory.
// 读取对应的磁盘块
if ((r = read_block(f->f_indirect, &blk, 0)) < 0)
{
return r;
}
// 设置索引指针。这里再次强调,我们的间接索引块的前十个索引是空的,因此直接使用filbno作为偏移即可
ptr = (u_int *)blk + filebno;
}
else
{
return -E_INVAL;
}

// 设置索引指针
// Step 4: store the result into *ppdiskbno, and return 0.
*ppdiskbno = ptr;
return 0;
}

file_map_block

找到文件对应的索引块的磁盘号,如果索引块不存在,根据alloc参数,决定是否创建一个新的块。

位置:fs/fs.c

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
int file_map_block(struct File *f, u_int filebno, u_int *diskbno, u_int alloc)
{
int r;
u_int *ptr;

// 尝试寻找文件索引内部对应编号的索引指针
// Step 1: find the pointer for the target block.
if ((r = file_block_walk(f, filebno, &ptr, alloc)) < 0)
{
return r;
}

// 如果索引指针为空,并且alloc有效,分配一个新的block
// Step 2: if the block not exists, and create is set, alloc one.
if (*ptr == 0)
{
if (alloc == 0)
{
return -E_NOT_FOUND;
}

if ((r = alloc_block()) < 0)
{
return r;
}
*ptr = r;
}

// Step 3: set the pointer to the block in *diskbno and return 0.
*diskbno = *ptr;
return 0;
}

file_get_block

获取指定文件对应的磁盘块,并将其读入内存

位置:fs/fs.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int file_get_block(struct File *f, u_int filebno, void **blk)
{
int r;
u_int diskbno;
u_int isnew;

// Step 1: find the disk block number is `f` using `file_map_block`.
// 找到文件对应的索引块并读入内存
if ((r = file_map_block(f, filebno, &diskbno, 1)) < 0)
{
return r;
}

// Step 2: read the data in this disk to blk.
// 将指定磁盘块的内容读到内存中
if ((r = read_block(diskbno, blk, &isnew)) < 0)
{
return r;
}
return 0;
}

dir_lookup


INDEX2FD

根据给定的fd,找到其对应的页

1
#define INDEX2FD(i) (FDTABLE + (i)*BY2PG)

fd_alloc

申请一个文件控制符fd

位置:user/file.c

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
int fd_alloc(struct Fd **fd)
{
// Find the smallest i from 0 to MAXFD-1 that doesn't have
// its fd page mapped. Set *fd to the fd page virtual address.
// (Do not allocate a page. It is up to the caller to allocate
// the page. This means that if someone calls fd_alloc twice
// in a row without allocating the first page we return, we'll
// return the same page the second time.)
// Return 0 on success, or an error code on error.
u_int va;
u_int fdno;

// 从低至高遍历文件控制符,查找其中最小的没有被映射的fd
for (fdno = 0; fdno < MAXFD - 1; fdno++)
{
// 转换为其对应的虚拟地址
va = INDEX2FD(fdno);

// 有效位为0,说明无效,说明这是个可以使用的fd
if (((*vpd)[va / PDMAP] & PTE_V) == 0)
{
*fd = (struct Fd *)va;
return 0;
}

// 有效位为0,说明无效,说明这是个可使用的fd
if (((*vpt)[va / BY2PG] & PTE_V) == 0)
{ //the fd is not used
*fd = (struct Fd *)va;
return 0;
}
// 上述两层判断,含义为只要vpd和vpt有一个无效,就说明无效,就说明fd可用
}

// 没有找到空闲的fd,说明打开文件数量达到上限
return -E_MAX_OPEN;
}

fsipc_open

file send ipc open,利用ipc机制发送open的相关信息,包括路径path,模式omode等

位置:user/fsipc.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Overview:
// Send file-open request to the file server. Includes path and
// omode in request, sets *fileid and *size from reply.
//
// Returns:
// 0 on success,
// < 0 on failure.
int fsipc_open(const char *path, u_int omode, struct Fd *fd)
{
u_int perm;
struct Fsreq_open *req;

req = (struct Fsreq_open *)fsipcbuf;

// The path is too long.
if (strlen(path) >= MAXPATHLEN)
{
return -E_BAD_PATH;
}

strcpy((char *)req->req_path, path);
req->req_omode = omode;
return fsipc(FSREQ_OPEN, req, (u_int)fd, &perm);
}

fsipc

file system ipc,文件系统ipc

位置:user/fsipc.c

1
2
3
4
5
6
7
8
9
// type是FSREQ_*定义中的一种,fsreq传递了Fereq_*结构体对象
static int
fsipc(u_int type, void *fsreq, u_int dstva, u_int *perm)
{
u_int whom;
// NOTEICE: Our file system no.1 process!
ipc_send(envs[1].env_id, type, (u_int)fsreq, PTE_V | PTE_R);
return ipc_recv(&whom, dstva, perm);
}

fd2data

位置:user/fd.c

1
2
3
4
u_int fd2data(struct Fd *fd)
{
return INDEX2DATA(fd2num(fd));
}

fd2name

得到文件描述符的编号

位置:user/fd.c

1
2
3
4
int fd2num(struct Fd *fd)
{
return ((u_int)fd - FDTABLE) / BY2PG;
}

INDEX2DATA

位置:user/fd.h

1
#define INDEX2DATA(i) (FILEBASE + (i)*PDMAP)

open

打开文件

位置:user/file.c

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
44
45
46
47
48
49
50
51
52
53
54
55
// Overview:
// Open a file (or directory).
//
// Returns:
// the file descriptor onsuccess,
// < 0 on failure.
int open(const char *path, int mode)
{
struct Fd *fd;
struct Filefd *ffd;
u_int size, fileid;
int r;
u_int va;
u_int i;

// Step 1: Alloc a new Fd, return error code when fail to alloc.
// Hint: Please use fd_alloc.
// 申请一个新的文件描述符fd
r = fd_alloc(&fd);
if (r)
return r;

// Step 2: Get the file descriptor of the file to open.
// Hint: Read fsipc.c, and choose a function.
// 根据给定路径和模式发送打开文件的ipc
r = fsipc_open(path, mode, fd);
if (r)
return r;

// Step 3: Set the start address storing the file's content. Set size and fileid correctly.
// Hint: Use fd2data to get the start address.
// 根据文件内容设置起始地址
va = fd2data(fd);

// Step 4: Alloc memory, map the file content into memory.
// 申请空间并映射文件内容
ffd = fd;
size = ffd->f_file.f_size;
fileid = ffd->f_fileid;
// Step 5: Return the number of file descriptor.
// 返回文件描述符
for (i = 0; i < size; i += BY2PG)
{
r = syscall_mem_alloc(0, va + i, PTE_R | PTE_V);
if (r)
return r;
r = fsipc_map(fileid, i, va + i);
if (r)
return r;
}
int fdnum = fd2num(fd);
if (mode & O_APPND)
seek(fdnum, size);
return fdnum;
}

fd_lookup

根据给定文件控制符编号fdnum查找fd

位置:user/fd.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int fd_lookup(int fdnum, struct Fd **fd)
{
// Check that fdnum is in range and mapped. If not, return -E_INVAL.
// Set *fd to the fd page virtual address. Return 0.
u_int va;

// 大小超过文件控制符数量上限说明错误
if (fdnum >= MAXFD)
{
return -E_INVAL;
}

va = INDEX2FD(fdnum);

if (((*vpt)[va / BY2PG] & PTE_V) != 0)
{ //the fd is used
// 有效
*fd = (struct Fd *)va;
return 0;
}

return -E_INVAL;
}

dev_lookup

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int dev_lookup(int dev_id, struct Dev **dev)
{
int i;

for (i = 0; devtab[i]; i++)
if (devtab[i]->dev_id == dev_id)
{
*dev = devtab[i];
return 0;
}

writef("[%08x] unknown device type %d\n", env->env_id, dev_id);
return -E_INVAL;
}

write

写文件

位置:user/fd.c

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
int write(int fdnum, const void *buf, u_int n)
{
int r;
struct Dev *dev;
struct Fd *fd;

// 查找文件描述符
if ((r = fd_lookup(fdnum, &fd)) < 0 || (r = dev_lookup(fd->fd_dev_id, &dev)) < 0)
{
return r;
}

if ((fd->fd_omode & O_ACCMODE) == O_RDONLY)
{
writef("[%08x] write %d -- bad mode\n", env->env_id, fdnum);
return -E_INVAL;
}

if (debug)
writef("write %d %p %d via dev %s\n",
fdnum, buf, n, dev->dev_name);

r = (*dev->dev_write)(fd, buf, n, fd->fd_offset);

if (r > 0)
{
fd->fd_offset += r;
}

return r;
}

文件

fsformat.c

位置:fs

从本文件#include <stdio.h>可以知道,这是一个运行在Linux上的程序。


This is copyright.