第十四章 文件系统 最后的挑战(上)

本文最后更新于:1 年前

第十四章 文件系统 最后的挑战(上)

写在前面

看了下这一章应该是这本书内容最多的一章,不知道得什么时候才能完成。最近看到很多互联网行业饱和的例子,打消了我很大的积极性,还是两手准备,一边找实习,一边考研比较安心些。

创建文件系统

本次实验创建了1个主分区,5个逻辑分区的文件系统,也就是在内存写好了超级块结构体所有信息,空闲块位图出了第一个位”置1“用作根目录,最后一个扇区多余的部分全部置1,其余全部置0,inode位图只占一个扇区,共4096个inode结点,所以只需要第一位置1表0号inode结点指向根目录即可,inode数组写好0号inode结点即可,最后依次写入磁盘,完成创建文件系统。

fs/super_block.h创建

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
#ifndef __FS_SUPER_BLOCK_H
#define __FS_SUPER_BLOCK_H
#include "stdint.h"

/* 超级块 */
struct super_block {
uint32_t magic; // 用来标识文件系统类型,支持多文件系统的操作系统通过此标志来识别文件系统类型
uint32_t sec_cnt; // 本分区总共的扇区数
uint32_t inode_cnt; // 本分区中inode数量
uint32_t part_lba_base; // 本分区的起始lba地址

uint32_t block_bitmap_lba; // 块位图本身起始扇区地址
uint32_t block_bitmap_sects; // 扇区位图本身占用的扇区数量

uint32_t inode_bitmap_lba; // i结点位图起始扇区lba地址
uint32_t inode_bitmap_sects; // i结点位图占用的扇区数量

uint32_t inode_table_lba; // i结点表起始扇区lba地址
uint32_t inode_table_sects; // i结点表占用的扇区数量

uint32_t data_start_lba; // 数据区开始的第一个扇区号
uint32_t root_inode_no; // 根目录所在的I结点号
uint32_t dir_entry_size; // 目录项大小

uint8_t pad[460]; // 加上460字节,凑够512字节1扇区大小
} __attribute__ ((packed));
#endif

fs/inode.h创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#ifndef __FS_INODE_H
#define __FS_INODE_H
#include "stdint.h"
#include "list.h"
#include "../device/ide.h"

/* inode结构 */
struct inode {
uint32_t i_no; // inode编号

/* 当此inode是文件时,i_size是指文件大小,
若此inode是目录,i_size是指该目录下所有目录项大小之和*/
uint32_t i_size;

uint32_t i_open_cnts; // 记录此文件被打开的次数
bool write_deny; // 写文件不能并行,进程写文件前检查此标识

/* i_sectors[0-11]是直接块, i_sectors[12]用来存储一级间接块指针 */
uint32_t i_sectors[13];
struct list_elem inode_tag;
};


#endif

fs/dir.h创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#ifndef __FS_DIR_H
#define __FS_DIR_H
#include "stdint.h"
#include "inode.h"
#include "fs.h"
#include "ide.h"
#include "global.h"

#define MAX_FILE_NAME_LEN 16 // 最大文件名长度

/* 目录结构 */
struct dir {
struct inode* inode;
uint32_t dir_pos; // 记录在目录内的偏移
uint8_t dir_buf[512]; // 目录的数据缓存
};

/* 目录项结构 */
struct dir_entry {
char filename[MAX_FILE_NAME_LEN]; // 普通文件或目录名称
uint32_t i_no; // 普通文件或目录对应的inode编号
enum file_types f_type; // 文件类型
};
#endif

fs/fs.h创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#ifndef __FS_FS_H
#define __FS_FS_H
#include "ide.h"


#define MAX_FILES_PER_PART 4096 //每个扇区最大支持文件数
#define BITS_PER_SECTOR 4096 //每扇区的位数 512字节 * 8
#define SECTOR_SIZE 512 //每扇区的字节数 512字节
#define BLOCK_SIZE SECTOR_SIZE //块字节大小 我们这里一块 == 一扇区

enum file_types
{
FT_UNKNOWN, //未知文件类型
FT_REGULAR, //普通文件类型
FT_DIRECTORY //目录文件类型
};

void partition_format(struct disk* hd,struct partition* part);
bool mount_partition(struct list_elem* pelem,int arg);
void filesys_init(void);

#endif

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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
#include "fs.h"
#include "stdint.h"
#include "global.h"
#include "../device/ide.h"
#include "inode.h"
#include "dir.h"
#include "super_block.h"
#include "stdio-kernel.h"
#include "string.h"
#include "debug.h"
#include "list.h"

struct partition* cur_part; //默认操作分区

void partition_format(struct disk* hd,struct partition* part)
{
uint32_t boot_sector_sects = 1; //引导块一个块
uint32_t super_block_sects = 1; //超级块一个块
uint32_t inode_bitmap_sects = DIV_ROUND_UP(MAX_FILES_PER_PART,BITS_PER_SECTOR); //inode位图占的块数
//inode数组所占的块数
uint32_t inode_table_sects = DIV_ROUND_UP((sizeof(struct inode) * MAX_FILES_PER_PART),SECTOR_SIZE);

//注意这里的used_sects 肯定是不准确 差了那么一点点的 因为还没有包含block_bitmap_sects 但是为了简单处理 要先得到free_sects才能推 所以到后面block_bitmap_sects 要除两次
uint32_t used_sects = boot_sector_sects + super_block_sects + inode_bitmap_sects + inode_table_sects;
uint32_t free_sects = part->sec_cnt - used_sects;

uint32_t block_bitmap_sects = DIV_ROUND_UP(free_sects,BITS_PER_SECTOR); //一位一块
uint32_t block_bitmap_bit_len = free_sects - block_bitmap_sects; //再减去block_bitmap的
block_bitmap_sects = DIV_ROUND_UP(block_bitmap_bit_len,BITS_PER_SECTOR);

struct super_block sb; //利用栈来初始化超级块 我们的栈此刻在
sb.magic = 0x23333333; //魔数
sb.sec_cnt = part->sec_cnt; //该分区总扇区数
sb.inode_cnt = MAX_FILES_PER_PART; //该分区总inode数
sb.part_lba_base = part->start_lba; //该分区lba起始扇区位置

// 引导块 超级块 空闲块位图 inode位图 inode数组 根目录 空闲块区域
//挨着挨着顺序赋值即可
sb.block_bitmap_lba = part->start_lba + boot_sector_sects + super_block_sects;
sb.block_bitmap_sects = block_bitmap_sects;

sb.inode_bitmap_lba = sb.block_bitmap_lba + block_bitmap_sects;
sb.inode_bitmap_sects = inode_bitmap_sects;

sb.inode_table_lba = sb.inode_bitmap_lba + inode_bitmap_sects;
sb.inode_table_sects = inode_table_sects;

sb.data_start_lba = sb.inode_table_lba + inode_table_sects;
sb.root_inode_no = 0; //根目录inode起始编号 0
sb.dir_entry_size = sizeof(struct dir_entry); //目录项大小

printk("%s info:\n",part->name);
printk(" magic:0x%x\n part_lba_base:0x%x\n all_sectors:0x%x\n \
inode_cnt:0x%x\n block_bitmap_lba:0x%x\n block_bitmap_sectors:0x%x\n \
inode_bitmap_lba:0x%x\n inode_bitmap_sectors:0x%x\n \
inode_table_lba:0x%x\n inode_table_sectors:0x%x\n \
data_start_lba:0x%x\n", \
sb.magic,sb.part_lba_base,sb.sec_cnt,sb.inode_cnt,sb.block_bitmap_lba,sb.block_bitmap_sects,\
sb.inode_bitmap_lba,sb.inode_bitmap_sects,sb.inode_table_lba,\
sb.inode_table_sects,sb.data_start_lba);

//把元信息挨个挨个写进硬盘
ide_write(hd,part->start_lba + boot_sector_sects,&sb,super_block_sects);
printk(" super_block_lba:0x%x\n",part->start_lba + 1);

//找一个最大的数据缓冲区 我们的栈已经不足以满足我们的各种信息的储存了 之后还要把元信息给腾到硬盘中
uint32_t buf_size = (sb.block_bitmap_sects >= sb.inode_bitmap_sects) ? sb.block_bitmap_sects : sb.inode_bitmap_sects;
buf_size = ((buf_size >= inode_table_sects) ? buf_size : inode_table_sects) * SECTOR_SIZE;
//申请缓冲空间 给元信息腾空间 设置成uint8_t* 原因是 先弄块位图的初始化
uint8_t* buf = (uint8_t*)sys_malloc(buf_size);

/* 初始化块位图了 */
buf[0] |= 0x1;
uint32_t block_bitmap_last_byte = block_bitmap_bit_len / 8; //先算算占用多少字节
uint8_t block_bitmap_last_bit = block_bitmap_bit_len % 8; //最后还有剩余多少位
uint32_t last_size = SECTOR_SIZE - (block_bitmap_last_byte % SECTOR_SIZE); //先除余数 算出来多少字节空的

//处理字节 把可能多的一字节全部置成1 这几步处理的很细节阿
memset(&buf[block_bitmap_last_byte],0xff,last_size); //全部置1 保证不会被使用

//处理最后的位 有效位变成0 用~来处理 真的很妙
uint8_t bit_idx = 0;
while(bit_idx <= block_bitmap_last_bit)
buf[block_bitmap_last_byte] &= ~(1 << (bit_idx++)); //有效位

//把位图元信息给写到硬盘中 块位图的部分就结束了 还有inode位图 inode数组等着我们
ide_write(hd,sb.block_bitmap_lba,buf,sb.block_bitmap_sects);

/*初始化inode位图了*/
memset(buf,0,buf_size);
buf[0] |= 0x1; //第一个inode用于存根目录
ide_write(hd,sb.inode_bitmap_lba,buf,sb.inode_bitmap_sects); //第一个inode初始化在后面

/*初始化inode数组了*/
memset(buf,0,buf_size);
struct inode* i = (struct inode*)buf; //先初始化第一个inode 根目录所在的
i->i_size = sb.dir_entry_size * 2; //. 和 ..
i->i_no = 0;
i->i_sectors[0] = sb.data_start_lba; //根目录所在扇区就是最开始的第一个扇区

ide_write(hd,sb.inode_table_lba,buf,sb.inode_table_sects);

/*写根目录文件进入 第一个扇区了*/
memset(buf,0,buf_size);
struct dir_entry* p_de = (struct dir_entry*)buf;

memcpy(p_de->filename,".",1); //名称
p_de->i_no = 0; //根目录. inode仍然是自己
p_de->f_type = FT_DIRECTORY;
p_de++; //移动到下一条目录项

memcpy(p_de->filename,"..",2);
p_de->i_no = 0; //根目录的父目录仍然是自己 因为自己是固定好的 根基
p_de->f_type = FT_DIRECTORY;

ide_write(hd,sb.data_start_lba,buf,1); //把根目录文件写到第一个扇区中

printk(" root_dir_lba:0x%x\n",sb.data_start_lba);
printk("%s format done\n",part->name);
sys_free(buf); //临时借用的 现在得还回去了
}

//除了挂载 还需要在内存中把超级块指针 块位图 i结点位图 i结点指针给初始化赋值了 方便使用
bool mount_partition(struct list_elem* pelem,int arg)
{
char* part_name = (char*)arg;
struct partition* part = elem2entry(struct partition,part_tag,pelem);//得到分区指针 partition*
if(!strcmp(part->name,part_name)) //字符串相匹配
{
cur_part = part; //赋值指针
struct disk* hd = cur_part->my_disk;

struct super_block* sb_buf = (struct super_block*)sys_malloc(SECTOR_SIZE);
if(sb_buf == NULL)
PANIC("alloc memory failed!");

memset(sb_buf,0,SECTOR_SIZE);
ide_read(hd,cur_part->start_lba + 1,sb_buf,1);

cur_part->sb = sb_buf;

cur_part->block_bitmap.bits = (uint8_t*)sys_malloc(sb_buf->block_bitmap_sects * SECTOR_SIZE);
if(cur_part->block_bitmap.bits == NULL)
PANIC("alloc memory failed!");
cur_part->block_bitmap.btmp_bytes_len = sb_buf->block_bitmap_sects * SECTOR_SIZE;
ide_read(hd,sb_buf->block_bitmap_lba,cur_part->block_bitmap.bits,sb_buf->block_bitmap_sects);

cur_part->inode_bitmap.bits = (uint8_t*)sys_malloc(sb_buf->inode_bitmap_sects * SECTOR_SIZE);
if(cur_part->inode_bitmap.bits == NULL)
PANIC("alloc memory failed!");
cur_part->inode_bitmap.btmp_bytes_len = sb_buf->inode_bitmap_sects * SECTOR_SIZE;
ide_read(hd,sb_buf->inode_bitmap_lba,cur_part->inode_bitmap.bits,sb_buf->inode_bitmap_sects);

list_init(&cur_part->open_inodes);
printk("mount %s done!\n",part->name);
return true; //停止循环

}
return false; //继续循环
}

//文件系统初始化 磁盘上搜索 如果没有则格式化分区 并创建文件系统
void filesys_init(void)
{
uint8_t channel_no = 0,dev_no,part_idx = 0;
struct super_block* sb_buf = (struct super_block*)sys_malloc(SECTOR_SIZE);

if(sb_buf == NULL) PANIC("alloc memory failed!");
printk("searching filesysteam......\n");
while(channel_no < channel_cnt)
{
dev_no = 1;
while(dev_no < 2)
{
if(!dev_no) //跳过hd60M.img主盘
{
++dev_no;
continue;
}
struct disk* hd = &channels[0].devices[1]; //得到硬盘指针
struct partition* part = hd->prim_parts; //先为主区创建文件系统
while(part_idx < 12) //4个主区 + 8个逻辑分区
{
if(part_idx == 4)
part = hd->logic_parts;
if(part->sec_cnt != 0) //分区存在 如果没有初始化 即所有成员都为0
{
memset(sb_buf,0,SECTOR_SIZE);
ide_read(hd,part->start_lba +1,sb_buf,1); //读取超级块的扇区

if(sb_buf->magic != 0x23333333) //还没有创建文件系统
{
printk("formatting %s's partition %s......\n",\
hd->name,part->name);
partition_format(hd,part);
}
else
printk("%s has filesystem\n",part->name);
}
++part_idx;
++part; //到下一个分区看
}
++dev_no; //切换盘号
}
++channel_no; //增加ide通道号
}
sys_free(sb_buf);
char default_part[8] = "sdb1"; //参数为int 4字节字符串指针传的进去
list_traversal(&partition_list,mount_partition,(int)default_part);
}

kernel/init.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
#include "init.h"
#include "print.h"
#include "interrupt.h"
#include "../device/timer.h"
#include "memory.h"
#include "../thread/thread.h"
#include "../device/console.h"
#include "../device/keyboard.h"
#include "../userprog/tss.h"
#include "../userprog/syscall-init.h"
#include "../device/ide.h"
#include "../fs/fs.h"

/*负责初始化所有模块 */
void init_all() {
put_str("init_all\n");
idt_init(); // 初始化中断
mem_init();
timer_init();
thread_init();
console_init();
keyboard_init();
tss_init();
syscall_init();
ide_init();
filesys_init();
}

运行结果

图为bochs运行界面
图为bochs运行界面

实现文件操作基本函数

fs/inode.h修改

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
#ifndef __FS_INODE_H
#define __FS_INODE_H

#include "list.h"
#include "global.h"
#include "stdint.h"
#include "ide.h"

struct inode
{
uint32_t i_no; //inode 编号
uint32_t i_size; //文件大小 或者 目录项总大小 inode不管是什么文件的

uint32_t i_open_cnts; //记录此文件被打开的次数
bool write_deny; //写文件不能并行

uint32_t i_sectors[13]; //这里只实现了一级简介块 12为一级简介块指针 0-11直接是inode编号
struct list_elem inode_tag; //从硬盘读取速率太慢 此list做缓冲用 当第二次使用时如果list中有
//直接通过list_elem得到inode而不用再读取硬盘
};

struct inode_position
{
bool two_sec; //是否inode存储位置在两个扇区间
uint32_t sec_lba; //inode所在的扇区号
uint32_t off_size; //在所存储的扇区的偏移位置
};

static void inode_locate(struct partition* part,uint32_t inode_no,struct inode_position* inode_pos);
void inode_sync(struct partition* part,struct inode* inode,void* io_buf);
struct inode* inode_open(struct partition* part,uint32_t inode_no);
void inode_close(struct inode* inode);
void inode_init(uint32_t inode_no,struct inode* new_inode);

#endif

fs/inode.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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#include "inode.h"
#include "fs.h"
#include "file.h"
#include "global.h"
#include "debug.h"
#include "memory.h"
#include "interrupt.h"
#include "list.h"
#include "stdio-kernel.h"
#include "string.h"
#include "super_block.h"

/* 获取inode所在的扇区和扇区内的偏移量 */
static void inode_locate(struct partition* part, uint32_t inode_no, struct inode_position* inode_pos) {
/* inode_table在硬盘上是连续的 */
ASSERT(inode_no < 4096);
uint32_t inode_table_lba = part->sb->inode_table_lba;

uint32_t inode_size = sizeof(struct inode);
uint32_t off_size = inode_no * inode_size; // 第inode_no号I结点相对于inode_table_lba的字节偏移量
uint32_t off_sec = off_size / 512; // 第inode_no号I结点相对于inode_table_lba的扇区偏移量
uint32_t off_size_in_sec = off_size % 512; // 待查找的inode所在扇区中的起始地址

/* 判断此i结点是否跨越2个扇区 */
uint32_t left_in_sec = 512 - off_size_in_sec;
if (left_in_sec < inode_size ) { // 若扇区内剩下的空间不足以容纳一个inode,必然是I结点跨越了2个扇区
inode_pos->two_sec = true;
} else { // 否则,所查找的inode未跨扇区
inode_pos->two_sec = false;
}
inode_pos->sec_lba = inode_table_lba + off_sec;
inode_pos->off_size = off_size_in_sec;
}

/* 将inode写入到分区part */
void inode_sync(struct partition* part, struct inode* inode, void* io_buf) { // io_buf是用于硬盘io的缓冲区
uint8_t inode_no = inode->i_no;
struct inode_position inode_pos;
inode_locate(part, inode_no, &inode_pos); // inode位置信息会存入inode_pos
ASSERT(inode_pos.sec_lba <= (part->start_lba + part->sec_cnt));

/* 硬盘中的inode中的成员inode_tag和i_open_cnts是不需要的,
* 它们只在内存中记录链表位置和被多少进程共享 */
struct inode pure_inode;
memcpy(&pure_inode, inode, sizeof(struct inode));

/* 以下inode的三个成员只存在于内存中,现在将inode同步到硬盘,清掉这三项即可 */
pure_inode.i_open_cnts = 0;
pure_inode.write_deny = false; // 置为false,以保证在硬盘中读出时为可写
pure_inode.inode_tag.prev = pure_inode.inode_tag.next = NULL;

char* inode_buf = (char*)io_buf;
if (inode_pos.two_sec) { // 若是跨了两个扇区,就要读出两个扇区再写入两个扇区
/* 读写硬盘是以扇区为单位,若写入的数据小于一扇区,要将原硬盘上的内容先读出来再和新数据拼成一扇区后再写入 */
ide_read(part->my_disk, inode_pos.sec_lba, inode_buf, 2); // inode在format中写入硬盘时是连续写入的,所以读入2块扇区

/* 开始将待写入的inode拼入到这2个扇区中的相应位置 */
memcpy((inode_buf + inode_pos.off_size), &pure_inode, sizeof(struct inode));

/* 将拼接好的数据再写入磁盘 */
ide_write(part->my_disk, inode_pos.sec_lba, inode_buf, 2);
} else { // 若只是一个扇区
ide_read(part->my_disk, inode_pos.sec_lba, inode_buf, 1);
memcpy((inode_buf + inode_pos.off_size), &pure_inode, sizeof(struct inode));
ide_write(part->my_disk, inode_pos.sec_lba, inode_buf, 1);
}
}

/* 根据i结点号返回相应的i结点 */
struct inode* inode_open(struct partition* part, uint32_t inode_no) {
/* 先在已打开inode链表中找inode,此链表是为提速创建的缓冲区 */
struct list_elem* elem = part->open_inodes.head.next;
struct inode* inode_found;
while (elem != &part->open_inodes.tail) {
inode_found = elem2entry(struct inode, inode_tag, elem);
if (inode_found->i_no == inode_no) {
inode_found->i_open_cnts++;
return inode_found;
}
elem = elem->next;
}

/*由于open_inodes链表中找不到,下面从硬盘上读入此inode并加入到此链表 */
struct inode_position inode_pos;

/* inode位置信息会存入inode_pos, 包括inode所在扇区地址和扇区内的字节偏移量 */
inode_locate(part, inode_no, &inode_pos);

/* 为使通过sys_malloc创建的新inode被所有任务共享,
* 需要将inode置于内核空间,故需要临时
* 将cur_pbc->pgdir置为NULL */
struct task_struct* cur = running_thread();
uint32_t* cur_pagedir_bak = cur->pgdir;
cur->pgdir = NULL;
/* 以上三行代码完成后下面分配的内存将位于内核区 */
inode_found = (struct inode*)sys_malloc(sizeof(struct inode));
/* 恢复pgdir */
cur->pgdir = cur_pagedir_bak;

char* inode_buf;
if (inode_pos.two_sec) { // 考虑跨扇区的情况
inode_buf = (char*)sys_malloc(1024);

/* i结点表是被partition_format函数连续写入扇区的,
* 所以下面可以连续读出来 */
ide_read(part->my_disk, inode_pos.sec_lba, inode_buf, 2);
} else { // 否则,所查找的inode未跨扇区,一个扇区大小的缓冲区足够
inode_buf = (char*)sys_malloc(512);
ide_read(part->my_disk, inode_pos.sec_lba, inode_buf, 1);
}
memcpy(inode_found, inode_buf + inode_pos.off_size, sizeof(struct inode));

/* 因为一会很可能要用到此inode,故将其插入到队首便于提前检索到 */
list_push(&part->open_inodes, &inode_found->inode_tag);
inode_found->i_open_cnts = 1;

sys_free(inode_buf);
return inode_found;
}

/* 关闭inode或减少inode的打开数 */
void inode_close(struct inode* inode) {
/* 若没有进程再打开此文件,将此inode去掉并释放空间 */
enum intr_status old_status = intr_disable();
if (--inode->i_open_cnts == 0) {
list_remove(&inode->inode_tag); // 将I结点从part->open_inodes中去掉

/* inode_open时为实现inode被所有进程共享,已经在sys_malloc为inode分配了内核空间 */
struct task_struct* cur = running_thread();
uint32_t* cur_pagedir_bak = cur->pgdir;
cur->pgdir = NULL;
sys_free(inode); // 释放inode的内核空间
cur->pgdir = cur_pagedir_bak;
}
intr_set_status(old_status);
}


/* 初始化new_inode */
void inode_init(uint32_t inode_no, struct inode* new_inode) {
new_inode->i_no = inode_no;
new_inode->i_size = 0;
new_inode->i_open_cnts = 0;
new_inode->write_deny = false;

/* 初始化块索引数组i_sector */
uint8_t sec_idx = 0;
while (sec_idx < 13) {
/* i_sectors[12]为一级间接块地址 */
new_inode->i_sectors[sec_idx] = 0;
sec_idx++;
}
}

thread/thread.h修改

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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#ifndef __THREAD_THREAD_H
#define __THREAD_THREAD_H
#include "stdint.h"
#include "list.h"
#include "bitmap.h"
#include "../kernel/memory.h"

#define TASK_NAME_LEN 16
#define MAX_FILES_OPEN_PER_PROC 8
/* 自定义通用函数类型,它将在很多线程函数中做为形参类型 */
typedef void thread_func(void*);
typedef int16_t pid_t;

/* 进程或线程的状态 */
enum task_status
{
TASK_RUNNING,
TASK_READY,
TASK_BLOCKED,
TASK_WAITING,
TASK_HANGING,
TASK_DIED
};

/*********** 中断栈intr_stack ***********
* 此结构用于中断发生时保护程序(线程或进程)的上下文环境:
* 进程或线程被外部中断或软中断打断时,会按照此结构压入上下文
* 寄存器, intr_exit中的出栈操作是此结构的逆操作
* 此栈在线程自己的内核栈中位置固定,所在页的最顶端
********************************************/
struct intr_stack
{
uint32_t vec_no; // kernel.S 宏VECTOR中push %1压入的中断号
uint32_t edi;
uint32_t esi;
uint32_t ebp;
uint32_t esp_dummy; // 虽然pushad把esp也压入,但esp是不断变化的,所以会被popad忽略
uint32_t ebx;
uint32_t edx;
uint32_t ecx;
uint32_t eax;
uint32_t gs;
uint32_t fs;
uint32_t es;
uint32_t ds;

/* 以下由cpu从低特权级进入高特权级时压入 */
uint32_t err_code; // err_code会被压入在eip之后
void (*eip) (void);
uint32_t cs;
uint32_t eflags;
void* esp;
uint32_t ss;
};

/*********** 线程栈thread_stack ***********
* 线程自己的栈,用于存储线程中待执行的函数
* 此结构在线程自己的内核栈中位置不固定,
* 用在switch_to时保存线程环境。
* 实际位置取决于实际运行情况。
******************************************/
struct thread_stack
{
uint32_t ebp;
uint32_t ebx;
uint32_t edi;
uint32_t esi;

/* 线程第一次执行时,eip指向待调用的函数kernel_thread
其它时候,eip是指向switch_to的返回地址*/
void (*eip) (thread_func* func, void* func_arg);

/***** 以下仅供第一次被调度上cpu时使用 ****/

/* 参数unused_ret只为占位置充数为返回地址 */
void (*unused_retaddr);
thread_func* function; // 由Kernel_thread所调用的函数名
void* func_arg; // 由Kernel_thread所调用的函数所需的参数
};

/* 进程或线程的pcb,程序控制块 */
struct task_struct
{
uint32_t* self_kstack; // 各内核线程都用自己的内核栈
pid_t pid;
enum task_status status;
char name[TASK_NAME_LEN];
uint8_t priority;
uint8_t ticks; // 每次在处理器上执行的时间嘀嗒数
/* 此任务自上cpu运行后至今占用了多少cpu嘀嗒数,
* 也就是此任务执行了多久*/
uint32_t elapsed_ticks;
/* general_tag的作用是用于线程在一般的队列中的结点 */
struct list_elem general_tag;
/* all_list_tag的作用是用于线程队列thread_all_list中的结点 */
struct list_elem all_list_tag;
uint32_t* pgdir; // 进程自己页表的虚拟地址
struct virtual_addr userprog_vaddr; // 用户进程的虚拟地址
struct mem_block_desc u_block_desc[DESC_CNT]; // 用户进程内存块描述符
uint32_t stack_magic; // 用这串数字做栈的边界标记,用于检测栈的溢出
int32_t fd_table[MAX_FILES_OPEN_PER_PROC]; // 已打开文件数组
};

extern struct list thread_ready_list;
extern struct list thread_all_list;

struct task_struct* running_thread(void);
static void kernel_thread(thread_func* function,void* func_arg);
void thread_create(struct task_struct* pthread,thread_func function,void* func_arg);
void init_thread(struct task_struct* pthread,char* name,int prio);
struct task_struct* thread_start(char* name,int prio,thread_func function,void* func_arg);
static void make_main_thread(void);
void schedule(void);
void thread_init(void);
void thread_block(enum task_status stat);
void thread_unblock(struct task_struct* pthread);
static pid_t allocate_pid(void);
void thread_yield(void);
static void idle(void);

#endif

thread/thread.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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
#include "thread.h"   //函数声明 各种结构体
#include "stdint.h" //前缀
#include "string.h" //memset
#include "global.h" //不清楚
#include "memory.h" //分配页需要
#include "debug.h"
#include "interrupt.h"
#include "print.h"
#include "../userprog/process.h"
#include "../thread/sync.h"


#define PG_SIZE 4096

struct task_struct* main_thread; // 主线程PCB
struct task_struct* idle_thread; // idle线程
struct list thread_ready_list; // 就绪队列
struct list thread_all_list; // 所有任务队列
static struct list_elem* thread_tag;// 用于保存队列中的线程结点
struct lock pid_lock;


/* 获取当前线程pcb指针 */
struct task_struct* running_thread()
{
uint32_t esp;
asm ("mov %%esp, %0" : "=g" (esp));
/* 取esp整数部分即pcb起始地址 */
return (struct task_struct*)(esp & 0xfffff000);
}


/* 由kernel_thread去执行function(func_arg) */
static void kernel_thread(thread_func* function, void* func_arg)
{
/* 执行function前要开中断,避免后面的时钟中断被屏蔽,而无法调度其它线程 */
intr_enable();
function(func_arg);
}


/* 初始化线程栈thread_stack,将待执行的函数和参数放到thread_stack中相应的位置 */
void thread_create(struct task_struct* pthread, thread_func function, void* func_arg)
{
/* 先预留中断使用栈的空间,可见thread.h中定义的结构 */
pthread->self_kstack -= sizeof(struct intr_stack);

/* 再留出线程栈空间,可见thread.h中定义 */
pthread->self_kstack -= sizeof(struct thread_stack);
struct thread_stack* kthread_stack = (struct thread_stack*)pthread->self_kstack;
kthread_stack->eip = kernel_thread;
kthread_stack->function = function;
kthread_stack->func_arg = func_arg;
kthread_stack->ebp = kthread_stack->ebx = kthread_stack->esi = kthread_stack->edi = 0;
}


/* 初始化线程基本信息 */
void init_thread(struct task_struct* pthread, char* name, int prio)
{
memset(pthread, 0, sizeof(*pthread));
pthread->pid = allocate_pid();
strcpy(pthread->name, name);

if (pthread == main_thread)
{
/* 由于把main函数也封装成一个线程,并且它一直是运行的,故将其直接设为TASK_RUNNING */
pthread->status = TASK_RUNNING;
}
else
{
pthread->status = TASK_READY;
}

/* self_kstack是线程自己在内核态下使用的栈顶地址 */
pthread->self_kstack = (uint32_t*)((uint32_t)pthread + PG_SIZE);
pthread->priority = prio;
pthread->ticks = prio;
pthread->elapsed_ticks = 0;
pthread->pgdir = NULL;
pthread->stack_magic = 0x19870916; // 自定义的魔数
/* 标准输入输出先空出来 */
pthread->fd_table[0] = 0;
pthread->fd_table[1] = 1;
pthread->fd_table[2] = 2;
/* 其余的全置为-1 */
uint8_t fd_idx = 3;
while (fd_idx < MAX_FILES_OPEN_PER_PROC) {
pthread->fd_table[fd_idx] = -1;
fd_idx++;
}
}


/* 创建一优先级为prio的线程,线程名为name,线程所执行的函数是function(func_arg) */
struct task_struct* thread_start(char* name, int prio, thread_func function, void* func_arg)
{
/* pcb都位于内核空间,包括用户进程的pcb也是在内核空间 */
struct task_struct* thread = get_kernel_pages(1);
init_thread(thread, name, prio);
thread_create(thread, function, func_arg);

/* 确保之前不在队列中 */
ASSERT(!elem_find(&thread_ready_list, &thread->general_tag));
/* 加入就绪线程队列 */
list_append(&thread_ready_list, &thread->general_tag);

/* 确保之前不在队列中 */
ASSERT(!elem_find(&thread_all_list, &thread->all_list_tag));
/* 加入全部线程队列 */
list_append(&thread_all_list, &thread->all_list_tag);

return thread;
}


/* 将kernel中的main函数完善为主线程 */
static void make_main_thread(void)
{
/* 因为main线程早已运行,咱们在loader.S中进入内核时的mov esp,0xc009f000,
就是为其预留了tcb,地址为0xc009e000,因此不需要通过get_kernel_page另分配一页*/
main_thread = running_thread();
init_thread(main_thread, "main", 31);

/* main函数是当前线程,当前线程不在thread_ready_list中,
* 所以只将其加在thread_all_list中. */
ASSERT(!elem_find(&thread_all_list, &main_thread->all_list_tag));
list_append(&thread_all_list, &main_thread->all_list_tag);
}


/* 实现任务调度 */
void schedule()
{
ASSERT(intr_get_status() == INTR_OFF);

struct task_struct* cur = running_thread();
if (cur->status == TASK_RUNNING) // 若此线程只是cpu时间片到了,将其加入到就绪队列尾
{
ASSERT(!elem_find(&thread_ready_list, &cur->general_tag));
list_append(&thread_ready_list, &cur->general_tag);
cur->ticks = cur->priority; // 重新将当前线程的ticks再重置为其priority;
cur->status = TASK_READY;
}
else
{
/* 若此线程需要某事件发生后才能继续上cpu运行,
不需要将其加入队列,因为当前线程不在就绪队列中。*/
}

/* 如果就绪队列中没有可运行的任务,就唤醒idle */
if (list_empty(&thread_ready_list))
{
thread_unblock(idle_thread);
}

ASSERT(!list_empty(&thread_ready_list));
thread_tag = NULL; // thread_tag清空
/* 将thread_ready_list队列中的第一个就绪线程弹出,准备将其调度上cpu. */
thread_tag = list_pop(&thread_ready_list);
struct task_struct* next = elem2entry(struct task_struct, general_tag, thread_tag);
next->status = TASK_RUNNING;

process_activate(next);
switch_to(cur, next);
}


/* 初始化线程环境 */
void thread_init(void)
{
put_str("thread_init start\n");

list_init(&thread_ready_list);
list_init(&thread_all_list);
lock_init(&pid_lock);
/* 将当前main函数创建为线程 */
make_main_thread();
/* 创建idle线程 */
idle_thread = thread_start("idle", 10, idle, NULL);
put_str("thread_init done\n");
}


/* 当前线程将自己阻塞,标志其状态为stat. */
void thread_block(enum task_status stat)
{
/* stat取值为TASK_BLOCKED,TASK_WAITING,TASK_HANGING,也就是只有这三种状态才不会被调度*/
ASSERT(((stat == TASK_BLOCKED) || (stat == TASK_WAITING) || (stat == TASK_HANGING)));
enum intr_status old_status = intr_disable();
struct task_struct* cur_thread = running_thread();
cur_thread->status = stat; // 置其状态为stat
schedule(); // 将当前线程换下处理器
/* 待当前线程被解除阻塞后才继续运行下面的intr_set_status */
intr_set_status(old_status);
}


/* 将线程pthread解除阻塞 */
void thread_unblock(struct task_struct* pthread)
{
enum intr_status old_status = intr_disable();
ASSERT(((pthread->status == TASK_BLOCKED) || (pthread->status == TASK_WAITING) || (pthread->status == TASK_HANGING)));
if (pthread->status != TASK_READY)
{
ASSERT(!elem_find(&thread_ready_list, &pthread->general_tag));
if (elem_find(&thread_ready_list, &pthread->general_tag))
{
PANIC("thread_unblock: blocked thread in ready_list\n");
}
list_push(&thread_ready_list, &pthread->general_tag); // 放到队列的最前面,使其尽快得到调度
pthread->status = TASK_READY;
}
intr_set_status(old_status);
}


/* 分配pid */
static pid_t allocate_pid(void)
{
static pid_t next_pid = 0;
lock_acquire(&pid_lock);
next_pid++;
lock_release(&pid_lock);
return next_pid;
}


/* 系统空闲时运行的线程 */
static void idle(void)
{
while(1)
{
thread_block(TASK_BLOCKED);
//执行hlt时必须要保证目前处在开中断的情况下
asm volatile ("sti; hlt" : : : "memory");
}
}


/* 主动让出cpu,换其它线程运行 */
void thread_yield(void)
{
struct task_struct* cur = running_thread();
enum intr_status old_status = intr_disable();
ASSERT(!elem_find(&thread_ready_list, &cur->general_tag));
list_append(&thread_ready_list, &cur->general_tag);
cur->status = TASK_READY;
schedule();
intr_set_status(old_status);
}

fs/file.h创建

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
#ifndef __FS_FILE_H
#define __FS_FILE_H
#include "stdint.h"
#include "ide.h"
#include "dir.h"
#include "global.h"

/* 文件结构 */
struct file {
uint32_t fd_pos; // 记录当前文件操作的偏移地址,以0为起始,最大为文件大小-1
uint32_t fd_flag;
struct inode* fd_inode;
};

/* 标准输入输出描述符 */
enum std_fd {
stdin_no, // 0 标准输入
stdout_no, // 1 标准输出
stderr_no // 2 标准错误
};

/* 位图类型 */
enum bitmap_type {
INODE_BITMAP, // inode位图
BLOCK_BITMAP // 块位图
};

#define MAX_FILE_OPEN 32 // 系统可打开的最大文件数

extern struct file file_table[MAX_FILE_OPEN];
int32_t inode_bitmap_alloc(struct partition* part);
int32_t block_bitmap_alloc(struct partition* part);
int32_t file_create(struct dir* parent_dir, char* filename, uint8_t flag);
void bitmap_sync(struct partition* part, uint32_t bit_idx, uint8_t btmp);
int32_t get_free_slot_in_global(void);
int32_t pcb_fd_install(int32_t globa_fd_idx);
#endif

fs/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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#include "file.h"
#include "fs.h"
#include "super_block.h"
#include "inode.h"
#include "stdio-kernel.h"
#include "memory.h"
#include "debug.h"
#include "interrupt.h"
#include "string.h"
#include "../thread/thread.h"
#include "global.h"
#include "ioqueue.h"

#define DEFAULT_SECS 1

/* 文件表 */
struct file file_table[MAX_FILE_OPEN];

/* 从文件表file_table中获取一个空闲位,成功返回下标,失败返回-1 */
int32_t get_free_slot_in_global(void) {
uint32_t fd_idx = 3;
while (fd_idx < MAX_FILE_OPEN) {
if (file_table[fd_idx].fd_inode == NULL) {
break;
}
fd_idx++;
}
if (fd_idx == MAX_FILE_OPEN) {
printk("exceed max open files\n");
return -1;
}
return fd_idx;
}

/* 将全局描述符下标安装到进程或线程自己的文件描述符数组fd_table中,
* 成功返回下标,失败返回-1 */
int32_t pcb_fd_install(int32_t globa_fd_idx) {
struct task_struct* cur = running_thread();
uint8_t local_fd_idx = 3; // 跨过stdin,stdout,stderr
while (local_fd_idx < MAX_FILES_OPEN_PER_PROC) {
if (cur->fd_table[local_fd_idx] == -1) { // -1表示free_slot,可用
cur->fd_table[local_fd_idx] = globa_fd_idx;
break;
}
local_fd_idx++;
}
if (local_fd_idx == MAX_FILES_OPEN_PER_PROC) {
printk("exceed max open files_per_proc\n");
return -1;
}
return local_fd_idx;
}

/* 分配一个i结点,返回i结点号 */
int32_t inode_bitmap_alloc(struct partition* part) {
int32_t bit_idx = bitmap_scan(&part->inode_bitmap, 1);
if (bit_idx == -1) {
return -1;
}
bitmap_set(&part->inode_bitmap, bit_idx, 1);
return bit_idx;
}

/* 分配1个扇区,返回其扇区地址 */
int32_t block_bitmap_alloc(struct partition* part) {
int32_t bit_idx = bitmap_scan(&part->block_bitmap, 1);
if (bit_idx == -1) {
return -1;
}
bitmap_set(&part->block_bitmap, bit_idx, 1);
/* 和inode_bitmap_malloc不同,此处返回的不是位图索引,而是具体可用的扇区地址 */
return (part->sb->data_start_lba + bit_idx);
}

/* 将内存中bitmap第bit_idx位所在的512字节同步到硬盘 */
void bitmap_sync(struct partition* part, uint32_t bit_idx, uint8_t btmp_type) {
uint32_t off_sec = bit_idx / 4096; // 本i结点索引相对于位图的扇区偏移量
uint32_t off_size = off_sec * BLOCK_SIZE; // 本i结点索引相对于位图的字节偏移量
uint32_t sec_lba;
uint8_t* bitmap_off;

/* 需要被同步到硬盘的位图只有inode_bitmap和block_bitmap */
switch (btmp_type) {
case INODE_BITMAP:
sec_lba = part->sb->inode_bitmap_lba + off_sec;
bitmap_off = part->inode_bitmap.bits + off_size;
break;

case BLOCK_BITMAP:
sec_lba = part->sb->block_bitmap_lba + off_sec;
bitmap_off = part->block_bitmap.bits + off_size;
break;
}
ide_write(part->my_disk, sec_lba, bitmap_off, 1);
}

fs/dir.h创建

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
#ifndef __FS_DIR_H
#define __FS_DIR_H
#include "stdint.h"
#include "inode.h"
#include "fs.h"
#include "ide.h"
#include "global.h"

#define MAX_FILE_NAME_LEN 16 // 最大文件名长度

/* 目录结构 */
struct dir {
struct inode* inode;
uint32_t dir_pos; // 记录在目录内的偏移
uint8_t dir_buf[512]; // 目录的数据缓存
};

/* 目录项结构 */
struct dir_entry {
char filename[MAX_FILE_NAME_LEN]; // 普通文件或目录名称
uint32_t i_no; // 普通文件或目录对应的inode编号
enum file_types f_type; // 文件类型
};
extern struct dir root_dir; // 根目录
void open_root_dir(struct partition* part);
struct dir* dir_open(struct partition* part, uint32_t inode_no);
void dir_close(struct dir* dir);
bool search_dir_entry(struct partition* part, struct dir* pdir, const char* name, struct dir_entry* dir_e);
void create_dir_entry(char* filename, uint32_t inode_no, uint8_t file_type, struct dir_entry* p_de);
bool sync_dir_entry(struct dir* parent_dir, struct dir_entry* p_de, void* io_buf);

#endif

fs/dir.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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
#include "dir.h"
#include "ide.h"
#include "stdint.h"
#include "inode.h"
#include "file.h"
#include "fs.h"
#include "stdio-kernel.h"
#include "global.h"
#include "debug.h"
#include "memory.h"
#include "string.h"
#include "interrupt.h"
#include "super_block.h"


struct dir root_dir; // 根目录

/* 打开根目录 */
void open_root_dir(struct partition* part) {
root_dir.inode = inode_open(part, part->sb->root_inode_no);
root_dir.dir_pos = 0;
}

/* 在分区part上打开i结点为inode_no的目录并返回目录指针 */
struct dir* dir_open(struct partition* part, uint32_t inode_no) {
struct dir* pdir = (struct dir*)sys_malloc(sizeof(struct dir));
pdir->inode = inode_open(part, inode_no);
pdir->dir_pos = 0;
return pdir;
}

/* 在part分区内的pdir目录内寻找名为name的文件或目录,
* 找到后返回true并将其目录项存入dir_e,否则返回false */
bool search_dir_entry(struct partition* part, struct dir* pdir, \
const char* name, struct dir_entry* dir_e) {
uint32_t block_cnt = 140; // 12个直接块+128个一级间接块=140块

/* 12个直接块大小+128个间接块,共560字节 */
uint32_t* all_blocks = (uint32_t*)sys_malloc(48 + 512);
if (all_blocks == NULL) {
printk("search_dir_entry: sys_malloc for all_blocks failed");
return false;
}

uint32_t block_idx = 0;
while (block_idx < 12) {
all_blocks[block_idx] = pdir->inode->i_sectors[block_idx];
block_idx++;
}
block_idx = 0;

if (pdir->inode->i_sectors[12] != 0) { // 若含有一级间接块表
ide_read(part->my_disk, pdir->inode->i_sectors[12], all_blocks + 12, 1);
}
/* 至此,all_blocks存储的是该文件或目录的所有扇区地址 */

/* 写目录项的时候已保证目录项不跨扇区,
* 这样读目录项时容易处理, 只申请容纳1个扇区的内存 */
uint8_t* buf = (uint8_t*)sys_malloc(SECTOR_SIZE);
struct dir_entry* p_de = (struct dir_entry*)buf; // p_de为指向目录项的指针,值为buf起始地址
uint32_t dir_entry_size = part->sb->dir_entry_size;
uint32_t dir_entry_cnt = SECTOR_SIZE / dir_entry_size; // 1扇区内可容纳的目录项个数

/* 开始在所有块中查找目录项 */
while (block_idx < block_cnt) {
/* 块地址为0时表示该块中无数据,继续在其它块中找 */
if (all_blocks[block_idx] == 0) {
block_idx++;
continue;
}
ide_read(part->my_disk, all_blocks[block_idx], buf, 1);

uint32_t dir_entry_idx = 0;
/* 遍历扇区中所有目录项 */
while (dir_entry_idx < dir_entry_cnt) {
/* 若找到了,就直接复制整个目录项 */
if (!strcmp(p_de->filename, name)) {
memcpy(dir_e, p_de, dir_entry_size);
sys_free(buf);
sys_free(all_blocks);
return true;
}
dir_entry_idx++;
p_de++;
}
block_idx++;
p_de = (struct dir_entry*)buf; // 此时p_de已经指向扇区内最后一个完整目录项了,需要恢复p_de指向为buf
memset(buf, 0, SECTOR_SIZE); // 将buf清0,下次再用
}
sys_free(buf);
sys_free(all_blocks);
return false;
}

/* 关闭目录 */
void dir_close(struct dir* dir) {
/************* 根目录不能关闭 ***************
*1 根目录自打开后就不应该关闭,否则还需要再次open_root_dir();
*2 root_dir所在的内存是低端1M之内,并非在堆中,free会出问题 */
if (dir == &root_dir) {
/* 不做任何处理直接返回*/
return;
}
inode_close(dir->inode);
sys_free(dir);
}

/* 在内存中初始化目录项p_de */
void create_dir_entry(char* filename, uint32_t inode_no, uint8_t file_type, struct dir_entry* p_de) {
ASSERT(strlen(filename) <= MAX_FILE_NAME_LEN);

/* 初始化目录项 */
memcpy(p_de->filename, filename, strlen(filename));
p_de->i_no = inode_no;
p_de->f_type = file_type;
}

/* 将目录项p_de写入父目录parent_dir中,io_buf由主调函数提供 */
bool sync_dir_entry(struct dir* parent_dir, struct dir_entry* p_de, void* io_buf) {
struct inode* dir_inode = parent_dir->inode;
uint32_t dir_size = dir_inode->i_size;
uint32_t dir_entry_size = cur_part->sb->dir_entry_size;

ASSERT(dir_size % dir_entry_size == 0); // dir_size应该是dir_entry_size的整数倍

uint32_t dir_entrys_per_sec = (512 / dir_entry_size); // 每扇区最大的目录项数目
int32_t block_lba = -1;

/* 将该目录的所有扇区地址(12个直接块+ 128个间接块)存入all_blocks */
uint8_t block_idx = 0;
uint32_t all_blocks[140] = {0}; // all_blocks保存目录所有的块

/* 将12个直接块存入all_blocks */
while (block_idx < 12) {
all_blocks[block_idx] = dir_inode->i_sectors[block_idx];
block_idx++;
}

struct dir_entry* dir_e = (struct dir_entry*)io_buf; // dir_e用来在io_buf中遍历目录项
int32_t block_bitmap_idx = -1;

/* 开始遍历所有块以寻找目录项空位,若已有扇区中没有空闲位,
* 在不超过文件大小的情况下申请新扇区来存储新目录项 */
block_idx = 0;
while (block_idx < 140) { // 文件(包括目录)最大支持12个直接块+128个间接块=140个块
block_bitmap_idx = -1;
if (all_blocks[block_idx] == 0) { // 在三种情况下分配块
block_lba = block_bitmap_alloc(cur_part);
if (block_lba == -1) {
printk("alloc block bitmap for sync_dir_entry failed\n");
return false;
}

/* 每分配一个块就同步一次block_bitmap */
block_bitmap_idx = block_lba - cur_part->sb->data_start_lba;
ASSERT(block_bitmap_idx != -1);
bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP);

block_bitmap_idx = -1;
if (block_idx < 12) { // 若是直接块
dir_inode->i_sectors[block_idx] = all_blocks[block_idx] = block_lba;
} else if (block_idx == 12) { // 若是尚未分配一级间接块表(block_idx等于12表示第0个间接块地址为0)
dir_inode->i_sectors[12] = block_lba; // 将上面分配的块做为一级间接块表地址
block_lba = -1;
block_lba = block_bitmap_alloc(cur_part); // 再分配一个块做为第0个间接块
if (block_lba == -1) {
block_bitmap_idx = dir_inode->i_sectors[12] - cur_part->sb->data_start_lba;
bitmap_set(&cur_part->block_bitmap, block_bitmap_idx, 0);
dir_inode->i_sectors[12] = 0;
printk("alloc block bitmap for sync_dir_entry failed\n");
return false;
}

/* 每分配一个块就同步一次block_bitmap */
block_bitmap_idx = block_lba - cur_part->sb->data_start_lba;
ASSERT(block_bitmap_idx != -1);
bitmap_sync(cur_part, block_bitmap_idx, BLOCK_BITMAP);

all_blocks[12] = block_lba;
/* 把新分配的第0个间接块地址写入一级间接块表 */
ide_write(cur_part->my_disk, dir_inode->i_sectors[12], all_blocks + 12, 1);
} else { // 若是间接块未分配
all_blocks[block_idx] = block_lba;
/* 把新分配的第(block_idx-12)个间接块地址写入一级间接块表 */
ide_write(cur_part->my_disk, dir_inode->i_sectors[12], all_blocks + 12, 1);
}

/* 再将新目录项p_de写入新分配的间接块 */
memset(io_buf, 0, 512);
memcpy(io_buf, p_de, dir_entry_size);
ide_write(cur_part->my_disk, all_blocks[block_idx], io_buf, 1);
dir_inode->i_size += dir_entry_size;
return true;
}

/* 若第block_idx块已存在,将其读进内存,然后在该块中查找空目录项 */
ide_read(cur_part->my_disk, all_blocks[block_idx], io_buf, 1);
/* 在扇区内查找空目录项 */
uint8_t dir_entry_idx = 0;
while (dir_entry_idx < dir_entrys_per_sec) {
if ((dir_e + dir_entry_idx)->f_type == FT_UNKNOWN) { // FT_UNKNOWN为0,无论是初始化或是删除文件后,都会将f_type置为FT_UNKNOWN.
memcpy(dir_e + dir_entry_idx, p_de, dir_entry_size);
ide_write(cur_part->my_disk, all_blocks[block_idx], io_buf, 1);

dir_inode->i_size += dir_entry_size;
return true;
}
dir_entry_idx++;
}
block_idx++;
}
printk("directory is full!\n");
return false;
}

创建文件

fs/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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
#include "file.h"
#include "fs.h"
#include "super_block.h"
#include "inode.h"
#include "stdio-kernel.h"
#include "memory.h"
#include "debug.h"
#include "interrupt.h"
#include "string.h"
#include "../thread/thread.h"
#include "global.h"
#include "ioqueue.h"

#define DEFAULT_SECS 1

/* 文件表 */
struct file file_table[MAX_FILE_OPEN];

/* 从文件表file_table中获取一个空闲位,成功返回下标,失败返回-1 */
int32_t get_free_slot_in_global(void) {
uint32_t fd_idx = 3;
while (fd_idx < MAX_FILE_OPEN) {
if (file_table[fd_idx].fd_inode == NULL) {
break;
}
fd_idx++;
}
if (fd_idx == MAX_FILE_OPEN) {
printk("exceed max open files\n");
return -1;
}
return fd_idx;
}

/* 将全局描述符下标安装到进程或线程自己的文件描述符数组fd_table中,
* 成功返回下标,失败返回-1 */
int32_t pcb_fd_install(int32_t globa_fd_idx) {
struct task_struct* cur = running_thread();
uint8_t local_fd_idx = 3; // 跨过stdin,stdout,stderr
while (local_fd_idx < MAX_FILES_OPEN_PER_PROC) {
if (cur->fd_table[local_fd_idx] == -1) { // -1表示free_slot,可用
cur->fd_table[local_fd_idx] = globa_fd_idx;
break;
}
local_fd_idx++;
}
if (local_fd_idx == MAX_FILES_OPEN_PER_PROC) {
printk("exceed max open files_per_proc\n");
return -1;
}
return local_fd_idx;
}

/* 分配一个i结点,返回i结点号 */
int32_t inode_bitmap_alloc(struct partition* part) {
int32_t bit_idx = bitmap_scan(&part->inode_bitmap, 1);
if (bit_idx == -1) {
return -1;
}
bitmap_set(&part->inode_bitmap, bit_idx, 1);
return bit_idx;
}

/* 分配1个扇区,返回其扇区地址 */
int32_t block_bitmap_alloc(struct partition* part) {
int32_t bit_idx = bitmap_scan(&part->block_bitmap, 1);
if (bit_idx == -1) {
return -1;
}
bitmap_set(&part->block_bitmap, bit_idx, 1);
/* 和inode_bitmap_malloc不同,此处返回的不是位图索引,而是具体可用的扇区地址 */
return (part->sb->data_start_lba + bit_idx);
}

/* 将内存中bitmap第bit_idx位所在的512字节同步到硬盘 */
void bitmap_sync(struct partition* part, uint32_t bit_idx, uint8_t btmp_type) {
uint32_t off_sec = bit_idx / 4096; // 本i结点索引相对于位图的扇区偏移量
uint32_t off_size = off_sec * BLOCK_SIZE; // 本i结点索引相对于位图的字节偏移量
uint32_t sec_lba;
uint8_t* bitmap_off;

/* 需要被同步到硬盘的位图只有inode_bitmap和block_bitmap */
switch (btmp_type) {
case INODE_BITMAP:
sec_lba = part->sb->inode_bitmap_lba + off_sec;
bitmap_off = part->inode_bitmap.bits + off_size;
break;

case BLOCK_BITMAP:
sec_lba = part->sb->block_bitmap_lba + off_sec;
bitmap_off = part->block_bitmap.bits + off_size;
break;
}
ide_write(part->my_disk, sec_lba, bitmap_off, 1);
}

/* 创建文件,若成功则返回文件描述符,否则返回-1 */
int32_t file_create(struct dir* parent_dir, char* filename, uint8_t flag) {
/* 后续操作的公共缓冲区 */
void* io_buf = sys_malloc(1024);
if (io_buf == NULL) {
printk("in file_creat: sys_malloc for io_buf failed\n");
return -1;
}

uint8_t rollback_step = 0; // 用于操作失败时回滚各资源状态

/* 为新文件分配inode */
int32_t inode_no = inode_bitmap_alloc(cur_part);
if (inode_no == -1) {
printk("in file_creat: allocate inode failed\n");
return -1;
}

/* 此inode要从堆中申请内存,不可生成局部变量(函数退出时会释放)
* 因为file_table数组中的文件描述符的inode指针要指向它.*/
struct inode* new_file_inode = (struct inode*)sys_malloc(sizeof(struct inode));
if (new_file_inode == NULL) {
printk("file_create: sys_malloc for inode failded\n");
rollback_step = 1;
goto rollback;
}
inode_init(inode_no, new_file_inode); // 初始化i结点

/* 返回的是file_table数组的下标 */
int fd_idx = get_free_slot_in_global();
if (fd_idx == -1) {
printk("exceed max open files\n");
rollback_step = 2;
goto rollback;
}

file_table[fd_idx].fd_inode = new_file_inode;
file_table[fd_idx].fd_pos = 0;
file_table[fd_idx].fd_flag = flag;
file_table[fd_idx].fd_inode->write_deny = false;

struct dir_entry new_dir_entry;
memset(&new_dir_entry, 0, sizeof(struct dir_entry));

create_dir_entry(filename, inode_no, FT_REGULAR, &new_dir_entry); // create_dir_entry只是内存操作不出意外,不会返回失败

/* 同步内存数据到硬盘 */
/* a 在目录parent_dir下安装目录项new_dir_entry, 写入硬盘后返回true,否则false */
if (!sync_dir_entry(parent_dir, &new_dir_entry, io_buf)) {
printk("sync dir_entry to disk failed\n");
rollback_step = 3;
goto rollback;
}

memset(io_buf, 0, 1024);
/* b 将父目录i结点的内容同步到硬盘 */
inode_sync(cur_part, parent_dir->inode, io_buf);

memset(io_buf, 0, 1024);
/* c 将新创建文件的i结点内容同步到硬盘 */
inode_sync(cur_part, new_file_inode, io_buf);

/* d 将inode_bitmap位图同步到硬盘 */
bitmap_sync(cur_part, inode_no, INODE_BITMAP);

/* e 将创建的文件i结点添加到open_inodes链表 */
list_push(&cur_part->open_inodes, &new_file_inode->inode_tag);
new_file_inode->i_open_cnts = 1;

sys_free(io_buf);
return pcb_fd_install(fd_idx);

/*创建文件需要创建相关的多个资源,若某步失败则会执行到下面的回滚步骤 */
rollback:
switch (rollback_step) {
case 3:
/* 失败时,将file_table中的相应位清空 */
memset(&file_table[fd_idx], 0, sizeof(struct file));
case 2:
sys_free(new_file_inode);
case 1:
/* 如果新文件的i结点创建失败,之前位图中分配的inode_no也要恢复 */
bitmap_set(&cur_part->inode_bitmap, inode_no, 0);
break;
}
sys_free(io_buf);
return -1;
}

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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
#include "fs.h"
#include "stdint.h"
#include "global.h"
#include "../device/ide.h"
#include "inode.h"
#include "dir.h"
#include "super_block.h"
#include "stdio-kernel.h"
#include "string.h"
#include "debug.h"
#include "list.h"
#include "file.h"

struct partition* cur_part; //默认操作分区

static void partition_format(struct partition* part) {
/* 为方便实现,一个块大小是一扇区 */
uint32_t boot_sector_sects = 1;
uint32_t super_block_sects = 1;
uint32_t inode_bitmap_sects = DIV_ROUND_UP(MAX_FILES_PER_PART, BITS_PER_SECTOR); // I结点位图占用的扇区数.最多支持4096个文件
uint32_t inode_table_sects = DIV_ROUND_UP(((sizeof(struct inode) * MAX_FILES_PER_PART)), SECTOR_SIZE);
uint32_t used_sects = boot_sector_sects + super_block_sects + inode_bitmap_sects + inode_table_sects;
uint32_t free_sects = part->sec_cnt - used_sects;

/************** 简单处理块位图占据的扇区数 ***************/
uint32_t block_bitmap_sects;
block_bitmap_sects = DIV_ROUND_UP(free_sects, BITS_PER_SECTOR);
/* block_bitmap_bit_len是位图中位的长度,也是可用块的数量 */
uint32_t block_bitmap_bit_len = free_sects - block_bitmap_sects;
block_bitmap_sects = DIV_ROUND_UP(block_bitmap_bit_len, BITS_PER_SECTOR);
/*********************************************************/

/* 超级块初始化 */
struct super_block sb;
sb.magic = 0x19590318;
sb.sec_cnt = part->sec_cnt;
sb.inode_cnt = MAX_FILES_PER_PART;
sb.part_lba_base = part->start_lba;

sb.block_bitmap_lba = sb.part_lba_base + 2; // 第0块是引导块,第1块是超级块
sb.block_bitmap_sects = block_bitmap_sects;

sb.inode_bitmap_lba = sb.block_bitmap_lba + sb.block_bitmap_sects;
sb.inode_bitmap_sects = inode_bitmap_sects;

sb.inode_table_lba = sb.inode_bitmap_lba + sb.inode_bitmap_sects;
sb.inode_table_sects = inode_table_sects;

sb.data_start_lba = sb.inode_table_lba + sb.inode_table_sects;
sb.root_inode_no = 0;
sb.dir_entry_size = sizeof(struct dir_entry);

printk("%s info:\n", part->name);
printk(" magic:0x%x\n part_lba_base:0x%x\n all_sectors:0x%x\n inode_cnt:0x%x\n block_bitmap_lba:0x%x\n block_bitmap_sectors:0x%x\n inode_bitmap_lba:0x%x\n inode_bitmap_sectors:0x%x\n inode_table_lba:0x%x\n inode_table_sectors:0x%x\n data_start_lba:0x%x\n", sb.magic, sb.part_lba_base, sb.sec_cnt, sb.inode_cnt, sb.block_bitmap_lba, sb.block_bitmap_sects, sb.inode_bitmap_lba, sb.inode_bitmap_sects, sb.inode_table_lba, sb.inode_table_sects, sb.data_start_lba);

struct disk* hd = part->my_disk;
/*******************************
* 1 将超级块写入本分区的1扇区 *
******************************/
ide_write(hd, part->start_lba + 1, &sb, 1);
printk(" super_block_lba:0x%x\n", part->start_lba + 1);

/* 找出数据量最大的元信息,用其尺寸做存储缓冲区*/
uint32_t buf_size = (sb.block_bitmap_sects >= sb.inode_bitmap_sects ? sb.block_bitmap_sects : sb.inode_bitmap_sects);
buf_size = (buf_size >= sb.inode_table_sects ? buf_size : sb.inode_table_sects) * SECTOR_SIZE;
uint8_t* buf = (uint8_t*)sys_malloc(buf_size); // 申请的内存由内存管理系统清0后返回

/**************************************
* 2 将块位图初始化并写入sb.block_bitmap_lba *
*************************************/
/* 初始化块位图block_bitmap */
buf[0] |= 0x01; // 第0个块预留给根目录,位图中先占位
uint32_t block_bitmap_last_byte = block_bitmap_bit_len / 8;
uint8_t block_bitmap_last_bit = block_bitmap_bit_len % 8;
uint32_t last_size = SECTOR_SIZE - (block_bitmap_last_byte % SECTOR_SIZE); // last_size是位图所在最后一个扇区中,不足一扇区的其余部分

/* 1 先将位图最后一字节到其所在的扇区的结束全置为1,即超出实际块数的部分直接置为已占用*/
memset(&buf[block_bitmap_last_byte], 0xff, last_size);

/* 2 再将上一步中覆盖的最后一字节内的有效位重新置0 */
uint8_t bit_idx = 0;
while (bit_idx <= block_bitmap_last_bit) {
buf[block_bitmap_last_byte] &= ~(1 << bit_idx++);
}
ide_write(hd, sb.block_bitmap_lba, buf, sb.block_bitmap_sects);

/***************************************
* 3 将inode位图初始化并写入sb.inode_bitmap_lba *
***************************************/
/* 先清空缓冲区*/
memset(buf, 0, buf_size);
buf[0] |= 0x1; // 第0个inode分给了根目录
/* 由于inode_table中共4096个inode,位图inode_bitmap正好占用1扇区,
* 即inode_bitmap_sects等于1, 所以位图中的位全都代表inode_table中的inode,
* 无须再像block_bitmap那样单独处理最后一扇区的剩余部分,
* inode_bitmap所在的扇区中没有多余的无效位 */
ide_write(hd, sb.inode_bitmap_lba, buf, sb.inode_bitmap_sects);

/***************************************
* 4 将inode数组初始化并写入sb.inode_table_lba *
***************************************/
/* 准备写inode_table中的第0项,即根目录所在的inode */
memset(buf, 0, buf_size); // 先清空缓冲区buf
struct inode* i = (struct inode*)buf;
i->i_size = sb.dir_entry_size * 2; // .和..
i->i_no = 0; // 根目录占inode数组中第0个inode
i->i_sectors[0] = sb.data_start_lba; // 由于上面的memset,i_sectors数组的其它元素都初始化为0
ide_write(hd, sb.inode_table_lba, buf, sb.inode_table_sects);

/***************************************
* 5 将根目录初始化并写入sb.data_start_lba
***************************************/
/* 写入根目录的两个目录项.和.. */
memset(buf, 0, buf_size);
struct dir_entry* p_de = (struct dir_entry*)buf;

/* 初始化当前目录"." */
memcpy(p_de->filename, ".", 1);
p_de->i_no = 0;
p_de->f_type = FT_DIRECTORY;
p_de++;

/* 初始化当前目录父目录".." */
memcpy(p_de->filename, "..", 2);
p_de->i_no = 0; // 根目录的父目录依然是根目录自己
p_de->f_type = FT_DIRECTORY;

/* sb.data_start_lba已经分配给了根目录,里面是根目录的目录项 */
ide_write(hd, sb.data_start_lba, buf, 1);

printk(" root_dir_lba:0x%x\n", sb.data_start_lba);
printk("%s format done\n", part->name);
sys_free(buf);
}

//除了挂载 还需要在内存中把超级块指针 块位图 i结点位图 i结点指针给初始化赋值了 方便使用
static bool mount_partition(struct list_elem* pelem, int arg) {
char* part_name = (char*)arg;
struct partition* part = elem2entry(struct partition, part_tag, pelem);
if (!strcmp(part->name, part_name)) {
cur_part = part;
struct disk* hd = cur_part->my_disk;

/* sb_buf用来存储从硬盘上读入的超级块 */
struct super_block* sb_buf = (struct super_block*)sys_malloc(SECTOR_SIZE);

/* 在内存中创建分区cur_part的超级块 */
cur_part->sb = (struct super_block*)sys_malloc(sizeof(struct super_block));
if (cur_part->sb == NULL) {
PANIC("alloc memory failed!");
}

/* 读入超级块 */
memset(sb_buf, 0, SECTOR_SIZE);
ide_read(hd, cur_part->start_lba + 1, sb_buf, 1);

/* 把sb_buf中超级块的信息复制到分区的超级块sb中。*/
memcpy(cur_part->sb, sb_buf, sizeof(struct super_block));

/********** 将硬盘上的块位图读入到内存 ****************/
cur_part->block_bitmap.bits = (uint8_t*)sys_malloc(sb_buf->block_bitmap_sects * SECTOR_SIZE);
if (cur_part->block_bitmap.bits == NULL) {
PANIC("alloc memory failed!");
}
cur_part->block_bitmap.btmp_bytes_len = sb_buf->block_bitmap_sects * SECTOR_SIZE;
/* 从硬盘上读入块位图到分区的block_bitmap.bits */
ide_read(hd, sb_buf->block_bitmap_lba, cur_part->block_bitmap.bits, sb_buf->block_bitmap_sects);
/*************************************************************/

/********** 将硬盘上的inode位图读入到内存 ************/
cur_part->inode_bitmap.bits = (uint8_t*)sys_malloc(sb_buf->inode_bitmap_sects * SECTOR_SIZE);
if (cur_part->inode_bitmap.bits == NULL) {
PANIC("alloc memory failed!");
}
cur_part->inode_bitmap.btmp_bytes_len = sb_buf->inode_bitmap_sects * SECTOR_SIZE;
/* 从硬盘上读入inode位图到分区的inode_bitmap.bits */
ide_read(hd, sb_buf->inode_bitmap_lba, cur_part->inode_bitmap.bits, sb_buf->inode_bitmap_sects);
/*************************************************************/

list_init(&cur_part->open_inodes);
printk("mount %s done!\n", part->name);

/* 此处返回true是为了迎合主调函数list_traversal的实现,与函数本身功能无关。
只有返回true时list_traversal才会停止遍历,减少了后面元素无意义的遍历.*/
return true;
}
return false; // 使list_traversal继续遍历
}

//文件系统初始化 磁盘上搜索 如果没有则格式化分区 并创建文件系统
/* 在磁盘上搜索文件系统,若没有则格式化分区创建文件系统 */
void filesys_init() {
uint8_t channel_no = 0, dev_no, part_idx = 0;

/* sb_buf用来存储从硬盘上读入的超级块 */
struct super_block* sb_buf = (struct super_block*)sys_malloc(SECTOR_SIZE);

if (sb_buf == NULL) {
PANIC("alloc memory failed!");
}
printk("searching filesystem......\n");
while (channel_no < channel_cnt) {
dev_no = 0;
while(dev_no < 2) {
if (dev_no == 0) { // 跨过裸盘hd60M.img
dev_no++;
continue;
}
struct disk* hd = &channels[channel_no].devices[dev_no];
struct partition* part = hd->prim_parts;
while(part_idx < 12) { // 4个主分区+8个逻辑
if (part_idx == 4) { // 开始处理逻辑分区
part = hd->logic_parts;
}

/* channels数组是全局变量,默认值为0,disk属于其嵌套结构,
* partition又为disk的嵌套结构,因此partition中的成员默认也为0.
* 若partition未初始化,则partition中的成员仍为0.
* 下面处理存在的分区. */
if (part->sec_cnt != 0) { // 如果分区存在
memset(sb_buf, 0, SECTOR_SIZE);

/* 读出分区的超级块,根据魔数是否正确来判断是否存在文件系统 */
ide_read(hd, part->start_lba + 1, sb_buf, 1);

/* 只支持自己的文件系统.若磁盘上已经有文件系统就不再格式化了 */
if (sb_buf->magic == 0x19590318) {
printk("%s has filesystem\n", part->name);
} else { // 其它文件系统不支持,一律按无文件系统处理
printk("formatting %s`s partition %s......\n", hd->name, part->name);
partition_format(part);
}
}
part_idx++;
part++; // 下一分区
}
dev_no++; // 下一磁盘
}
channel_no++; // 下一通道
}
sys_free(sb_buf);
/* 确定默认操作的分区 */
char default_part[8] = "sdb1";
/* 挂载分区 */
list_traversal(&partition_list, mount_partition, (int)default_part);
/* 将当前分区的根目录打开 */
open_root_dir(cur_part);

/* 初始化文件表 */
uint32_t fd_idx = 0;
while (fd_idx < MAX_FILE_OPEN) {
file_table[fd_idx++].fd_inode = NULL;
}
}

/* 将最上层路径名称解析出来 */
char* path_parse(char* pathname, char* name_store) {
if (pathname[0] == '/') { // 根目录不需要单独解析
/* 路径中出现1个或多个连续的字符'/',将这些'/'跳过,如"///a/b" */
while(*(++pathname) == '/');
}

/* 开始一般的路径解析 */
while (*pathname != '/' && *pathname != 0) {
*name_store++ = *pathname++;
}

if (pathname[0] == 0) { // 若路径字符串为空则返回NULL
return NULL;
}
return pathname;
}

/* 返回路径深度,比如/a/b/c,深度为3 */
int32_t path_depth_cnt(char* pathname) {
ASSERT(pathname != NULL);
char* p = pathname;
char name[MAX_FILE_NAME_LEN]; // 用于path_parse的参数做路径解析
uint32_t depth = 0;

/* 解析路径,从中拆分出各级名称 */
p = path_parse(p, name);
while (name[0]) {
depth++;
memset(name, 0, MAX_FILE_NAME_LEN);
if (p) { // 如果p不等于NULL,继续分析路径
p = path_parse(p, name);
}
}
return depth;
}

/* 搜索文件pathname,若找到则返回其inode号,否则返回-1 */
static int search_file(const char* pathname, struct path_search_record* searched_record) {
/* 如果待查找的是根目录,为避免下面无用的查找,直接返回已知根目录信息 */
if (!strcmp(pathname, "/") || !strcmp(pathname, "/.") || !strcmp(pathname, "/..")) {
searched_record->parent_dir = &root_dir;
searched_record->file_type = FT_DIRECTORY;
searched_record->searched_path[0] = 0; // 搜索路径置空
return 0;
}

uint32_t path_len = strlen(pathname);
/* 保证pathname至少是这样的路径/x且小于最大长度 */
ASSERT(pathname[0] == '/' && path_len > 1 && path_len < MAX_PATH_LEN);
char* sub_path = (char*)pathname;
struct dir* parent_dir = &root_dir;
struct dir_entry dir_e;

/* 记录路径解析出来的各级名称,如路径"/a/b/c",
* 数组name每次的值分别是"a","b","c" */
char name[MAX_FILE_NAME_LEN] = {0};

searched_record->parent_dir = parent_dir;
searched_record->file_type = FT_UNKNOWN;
uint32_t parent_inode_no = 0; // 父目录的inode号

sub_path = path_parse(sub_path, name);
while (name[0]) { // 若第一个字符就是结束符,结束循环
/* 记录查找过的路径,但不能超过searched_path的长度512字节 */
ASSERT(strlen(searched_record->searched_path) < 512);

/* 记录已存在的父目录 */
strcat(searched_record->searched_path, "/");
strcat(searched_record->searched_path, name);

/* 在所给的目录中查找文件 */
if (search_dir_entry(cur_part, parent_dir, name, &dir_e)) {
memset(name, 0, MAX_FILE_NAME_LEN);
/* 若sub_path不等于NULL,也就是未结束时继续拆分路径 */
if (sub_path) {
sub_path = path_parse(sub_path, name);
}

if (FT_DIRECTORY == dir_e.f_type) { // 如果被打开的是目录
parent_inode_no = parent_dir->inode->i_no;
dir_close(parent_dir);
parent_dir = dir_open(cur_part, dir_e.i_no); // 更新父目录
searched_record->parent_dir = parent_dir;
continue;
} else if (FT_REGULAR == dir_e.f_type) { // 若是普通文件
searched_record->file_type = FT_REGULAR;
return dir_e.i_no;
}
} else { //若找不到,则返回-1
/* 找不到目录项时,要留着parent_dir不要关闭,
* 若是创建新文件的话需要在parent_dir中创建 */
return -1;
}
}

/* 执行到此,必然是遍历了完整路径并且查找的文件或目录只有同名目录存在 */
dir_close(searched_record->parent_dir);

/* 保存被查找目录的直接父目录 */
searched_record->parent_dir = dir_open(cur_part, parent_inode_no);
searched_record->file_type = FT_DIRECTORY;
return dir_e.i_no;
}

/* 打开或创建文件成功后,返回文件描述符,否则返回-1 */
int32_t sys_open(const char* pathname, uint8_t flags) {
/* 对目录要用dir_open,这里只有open文件 */
if (pathname[strlen(pathname) - 1] == '/') {
printk("can`t open a directory %s\n",pathname);
return -1;
}
ASSERT(flags <= 7);
int32_t fd = -1; // 默认为找不到

struct path_search_record searched_record;
memset(&searched_record, 0, sizeof(struct path_search_record));

/* 记录目录深度.帮助判断中间某个目录不存在的情况 */
uint32_t pathname_depth = path_depth_cnt((char*)pathname);

/* 先检查文件是否存在 */
int inode_no = search_file(pathname, &searched_record);
bool found = inode_no != -1 ? true : false;

if (searched_record.file_type == FT_DIRECTORY) {
printk("can`t open a direcotry with open(), use opendir() to instead\n");
dir_close(searched_record.parent_dir);
return -1;
}

uint32_t path_searched_depth = path_depth_cnt(searched_record.searched_path);

/* 先判断是否把pathname的各层目录都访问到了,即是否在某个中间目录就失败了 */
if (pathname_depth != path_searched_depth) { // 说明并没有访问到全部的路径,某个中间目录是不存在的
printk("cannot access %s: Not a directory, subpath %s is`t exist\n", \
pathname, searched_record.searched_path);
dir_close(searched_record.parent_dir);
return -1;
}

/* 若是在最后一个路径上没找到,并且并不是要创建文件,直接返回-1 */
if (!found && !(flags & O_CREAT)) {
printk("in path %s, file %s is`t exist\n", \
searched_record.searched_path, \
(strrchr(searched_record.searched_path, '/') + 1));
dir_close(searched_record.parent_dir);
return -1;
} else if (found && flags & O_CREAT) { // 若要创建的文件已存在
printk("%s has already exist!\n", pathname);
dir_close(searched_record.parent_dir);
return -1;
}

switch (flags & O_CREAT) {
case O_CREAT:
printk("creating file\n");
fd = file_create(searched_record.parent_dir, (strrchr(pathname, '/') + 1), flags);
dir_close(searched_record.parent_dir);
break;

}

/* 此fd是指任务pcb->fd_table数组中的元素下标,
* 并不是指全局file_table中的下标 */
return fd;
}

kernel/main.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
#include "print.h"
#include "init.h"
#include "debug.h"
#include "string.h"
#include "memory.h"
#include "../thread/thread.h"
#include "interrupt.h"
#include "../device/console.h"
#include "../device/ioqueue.h"
#include "../device/keyboard.h"
#include "../userprog/process.h"
#include "../lib/user/syscall.h"
#include "../userprog/syscall-init.h"
#include "../lib/stdio.h"
#include "../lib/kernel/stdio-kernel.h"
#include "../fs/fs.h"
#include "../fs/file.h"

int main(void) {
put_str("I am kernel\n");
init_all();
intr_enable();
sys_open("/file1",O_CREAT);
while(1);
return 0;
}

运行结果

图为bochs运行界面

写在后面

这一章内容太过繁多,所以我计划从下一节开始另开新章,分三部分完成。