《操作系统真象还原》第14章(上)
第14章文件系统大概是本书中最麻烦的一章了,不是说它难,更多的是说它代码(又臭又)长,光看篇幅就知道了——100页呢。不过好在我觉得自己学习xv6时对于文件系统还是有仔细研究的,希望能有所助益。当然,为避免冗长,本章也将分为若干个博客进行记录。
本篇博客对应书中14.1~14.3节。
下面是我认为值得关注的几个点:
“
1.文件系统概念
索引结构inode,即index node,索引结点,用来索引、跟踪一个文件的所有块。一个文件必须对应一个inode,磁盘中有多少文件就有多少inode。
entry是目录中各个文件的描述,它称为目录项,目录项中至少要包括文件名、文件类型及文件对应的inode编号。
有了目录项后,通过文件名找文件实体数据块的的流程是:
(1)在目录中找到文件名所在的目录项。
(2)从目录项中获取inode编号
(3)用inode编号作为inode数组的索引下标,找到inode
(4)从该inode中获取数据块的地址,读取数据块。
”
步骤:
1.创建并挂载文件系统
2.实现文件描述符
1.创建并挂载文件系统
①fs/super_block.h:
1 #ifndef __FS_SUPER_BLOCK_H
2 #define __FS_SUPER_BLOCK_H
3 #include "stdint.h"
4
5 /* 超级块 */
6 struct super_block{
7 uint32_t magic; // 标识文件系统类型
8 uint32_t sec_cnt; // 本分区扇区数
9 uint32_t inode_cnt; // 本分区inode数
10 uint32_t part_lba_base; // 本分区起始lba地址
11
12 uint32_t block_bitmap_lba; // 块位图起始扇区lba地址
13 uint32_t block_bitmap_sects;// 块位图占用的扇区数
14
15 uint32_t inode_bitmap_lba; // i结点位图起始扇区lba地址
16 uint32_t inode_bitmap_sects;// i结点位图占用的扇区数
17
18 uint32_t inode_table_lba; // i结点表起始扇区lba地址
19 uint32_t inode_table_sects; // i节点表占用的扇区数量
20
21 uint32_t data_start_lba; // 数据区开始的第一个扇区号
22 uint32_t root_inode_no; // 根目录所在的i节点号
23 uint32_t dir_entry_size; // 目录项大小
24
25 uint8_t pad[460]; // 加上460字节,凑够512字节1扇区大小
26 } __attribute__ ((packed));
27 #endif
②fs/inode.h:
1 #ifndef __FS_INODE_H
2 #define __FS_INODE_H
3 #include "stdint.h"
4 #include "list.h"
5
6 /* inode结构 */
7 struct inode{
8 uint32_t i_no; // inode编号
9
10 /* 当此inode指向文件时,i_size为文件大小;
11 * 当此inode指向目录时,i_size为目录下所有目录项之和的大小 */
12 uint32_t i_size;
13
14 uint32_t i_open_cnt;//记录此文件被打开的次数
15 bool write_deny; // 写文件不能并行,进程写文件前检查此标识
16
17 /* i_sectors[0-11]是直接块,i_sectors[12]用来存储一级间接块指针 */
18 uint32_t i_sectors[13];
19 struct list_elem inode_tag; // 标识此inode,加入“已打开的inode列表”(内存缓存)中
20 };
21 #endif
③fs/dir.h:
1 #ifndef __FS_DIR_H
2 #define __FS_DIR_H
3 #include "stdint.h"
4 #include "inode.h"
5 #include "ide.h"
6 #include "global.h"
7
8 #define MAX_FILE_NAME_LEN 16
9
10 /* 目录结构 */
11 struct dir{
12 struct inode* inode;
13 uint32_t dir_pos; // 记录在目录内的偏移
14 uint8_t dir_buf[512]; // 目录的数据缓存
15 };
16
17 struct dir_entry{
18 char filename[MAX_FILE_NAME_LEN]; // 普通文件或目录
19 uint32_t i_no; // 普通文件或目录对应的inode
20 enum file_types f_type; // 文件类型
21 };
22
23 #endif
④fs/fs.h:
1 #ifndef __FS_FS_H
2 #define __FS_FS_H
3 #include "stdint.h"
4 #include "ide.h"
5
6 #define MAX_FILES_PER_PART 4096
7 #define BITS_PER_SECTOR 4096
8 #define SECTOR_SIZE 512
9 #define BLOCK_SIZE SECTOR_SIZE
10
11 /* 文件类型 */
12 enum file_types{
13 FT_UNKNOWN, // 不支持的文件类型
14 FT_REGULAR, // 普通文件
15 FT_DIRECTORY // 目录
16 };
17 void filesys_init(void);
18 #endif
⑤fs/fs.c:

1 #include "fs.h"
2 #include "super_block.h"
3 #include "inode.h"
4 #include "dir.h"
5 #include "stdint.h"
6 #include "stdio-kernel.h"
7 #include "list.h"
8 #include "string.h"
9 #include "ide.h"
10 #include "global.h"
11 #include "debug.h"
12 #include "memory.h"
13
14 /* 初始化分区元信息,创建文件系统 */
15 static void partition_format(struct partition* part){
16 /* 为方便,一个块大小是一扇区 */
17 uint32_t boot_sector_sects=1;
18 uint32_t super_block_sects=1;
19 uint32_t inode_bitmap_sects=DIV_ROUND_UP(MAX_FILES_PER_PART,BITS_PER_SECTOR); // inode位图所占用的扇区数
20 uint32_t inode_table_sects=DIV_ROUND_UP(((sizeof(struct inode)*MAX_FILES_PER_PART)),SECTOR_SIZE);
21 uint32_t used_sects=boot_sector_sects+super_block_sects+inode_bitmap_sects+inode_table_sects;
22 uint32_t free_sects=part->sec_cnt-used_sects;
23
24 /****************** 块位图占用的扇区数 ********************/
25 uint32_t block_bitmap_sects;
26 block_bitmap_sects=DIV_ROUND_UP(free_sects,BITS_PER_SECTOR);
27 /* block_bitmap_bit_len是位图中位的长度,也是可用块的数量 */
28 uint32_t block_bitmap_bit_len=free_sects-block_bitmap_sects;
29 block_bitmap_sects=DIV_ROUND_UP(block_bitmap_bit_len,BITS_PER_SECTOR);
30 /*********************************************************/
31
32 /* 超级块初始化 */
33 struct super_block sb;
34 sb.magic=0x19590318;
35 sb.sec_cnt=part->sec_cnt;
36 sb.inode_cnt=MAX_FILES_PER_PART;
37 sb.part_lba_base=part->start_lba;
38
39 sb.block_bitmap_lba=sb.part_lba_base+2; // 第0块为bootblock,第1块为superblock
40 sb.block_bitmap_sects=block_bitmap_sects;
41
42 sb.inode_bitmap_lba=sb.block_bitmap_lba+sb.block_bitmap_sects;
43 sb.inode_bitmap_sects=inode_bitmap_sects;
44
45 sb.inode_table_lba=sb.inode_bitmap_lba+sb.inode_bitmap_sects;
46 sb.inode_table_sects=inode_table_sects;
47
48 sb.data_start_lba=sb.inode_table_lba+sb.inode_table_sects;
49 sb.root_inode_no=0;
50 sb.dir_entry_size=sizeof(struct dir_entry);
51
52 printk("%s info:\n",part->name);
53 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);
54
55 struct disk* hd=part->my_disk;
56 /****************************
57 * 1.将超级块写入本分区的1扇区
58 * *************************/
59 ide_write(hd,part->start_lba+1,&sb,1);
60 printk(" super_block_lba:0x%x\n",part->start_lba+1);
61
62 /* 找出数据量最大的元信息,用其尺寸做存储缓冲区 */
63 uint32_t buf_size=(sb.block_bitmap_sects>=sb.inode_bitmap_sects?sb.block_bitmap_sects:sb.inode_bitmap_sects);
64 buf_size=(buf_size>=sb.inode_table_sects?buf_size:sb.inode_table_sects)*SECTOR_SIZE;
65 uint8_t* buf=(uint8_t*)sys_malloc(buf_size); // 申请的内存由内存管理系统清0后返回
66
67 /****************************
68 * 2.将块位图初始化并写入sb.block_bitmap_lba
69 * *************************/
70 /* 初始化块位图block_bitmap */
71 buf[0]|=0x01; // 第0个块预留给根目录,位图中先占位
72 uint32_t block_bitmap_last_byte=block_bitmap_bit_len/8;
73 uint8_t block_bitmap_last_bit=block_bitmap_bit_len%8;
74 uint32_t last_size=SECTOR_SIZE-(block_bitmap_last_byte%SECTOR_SIZE); // last_size是位图中的填充部分
75
76 /* 2.1先将位图最后一字节到其所在扇区结束全置为1,即超出实际块数部分直接置为已占用 */
77 memset(&buf[block_bitmap_last_byte],0xff,last_size);
78
79 /* 2.2再将上一步中覆盖的最后一字节内有效位重新置0 */
80 uint8_t bit_idx=0;
81 while (bit_idx<=block_bitmap_last_bit){
82 buf[block_bitmap_last_byte]&=~(1<<bit_idx++);
83 }
84 ide_write(hd,sb.block_bitmap_lba,buf,sb.block_bitmap_sects);
85
86 /***************************
87 * 3.将inode位图初始化并写入sb.inode_bitmap_lba
88 * ************************/
89 /* 先清空缓冲区 */
90 memset(buf,0,buf_size);
91 buf[0]|=0x1; // 第0个inode分给了根目录
92 /* 由于inode_table中共4096个inode,位图中inode_bitmap正好占用1扇区,
93 * 即inode_bitmap_sects等于1,所以位图中的位全都代表inode_table中的inode,
94 * 无须再像block_bitmap那样单独处理最后一扇区的剩余部分,
95 * inode_bitmap所在的扇区中没有多余的无效位 */
96 ide_write(hd,sb.inode_bitmap_lba,buf,sb.inode_bitmap_sects);
97
98 /*************************
99 * 4.将inode数组初始化并写入sb.inode_table_lba
100 * **********************/
101 /* 准备写inode_table中的第0项,即根目录所在的inode */
102 memset(buf,0,buf_size); // 先清空缓冲区buf
103 struct inode* i=(struct inode*)buf;
104 i->i_size=sb.dir_entry_size*2; // . & ..
105 i->i_no=0; // 根目录占inode数组中第0个inode
106 i->i_sectors[0]=sb.data_start_lba; // 由于上面的memset,i_sectors数组的其它元素都初始化为0
107 ide_write(hd,sb.inode_table_lba,buf,sb.inode_table_sects);
108
109 /*************************
110 * 5.将根目录初始化并写入sb.data_start_lba
111 * **********************/
112 /* 写入根目录的两个目录项 . & .. */
113 memset(buf,0,buf_size);
114 struct dir_entry* p_de=(struct dir_entry*)buf;
115
116 /* 初始化当前目录"." */
117 memcpy(p_de->filename,".",1);
118 p_de->i_no=0;
119 p_de->f_type=FT_DIRECTORY;
120 ++p_de;
121
122 /* 初始化当前目录父目录 ".." */
123 memcpy(p_de->filename,"..",2);
124 p_de->i_no=0; // 根目录的父目录是自己
125 p_de->f_type=FT_DIRECTORY;
126
127 /* sb.data_start_lba已经分配给了根目录,里面是根目录的目录项 */
128 ide_write(hd,sb.data_start_lba,buf,1);
129
130 printk(" root_dir_lba:0x%x\n",sb.data_start_lba);
131 printk("%s format done\n",part->name);
132 sys_free(buf);
133 }
134
135 /* 在磁盘上搜索文件系统,若没有则格式化分区创建文件系统 */
136 void filesys_init(){
137 uint8_t channel_no=0,dev_no=0,part_idx=0;
138
139 /* sb_buf用来存储从硬盘上读入的超级块 */
140 struct super_block* sb_buf=(struct super_block*)sys_malloc(SECTOR_SIZE);
141
142 if (sb_buf==NULL){
143 PANIC("alloc memory failed!");
144 }
145 printk("searching filesystem......\n");
146 while (channel_no<channel_cnt){
147 dev_no=0;
148 while (dev_no<2){
149 if (dev_no==0){
150 ++dev_no;
151 continue;
152 }
153 struct disk* hd=&channels[channel_no].devices[dev_no];
154 struct partition* part=hd->prim_parts;
155 while (part_idx<12){ // 四个主分区+八个逻辑分区
156 if (part_idx==4){ // 逻辑分区
157 part=hd->logic_parts;
158 }
159
160 /* channels数组是全局变量,默认值为0,disk属于其嵌套结构,
161 * partition又为disk的嵌套结构,因此partition中的成员默认也为0.
162 * 若partition未初始化,则partition中的成员仍为0.
163 * 下面处理存在的分区. */
164 if (part->sec_cnt!=0){ // 如果分区存在
165 memset(sb_buf,0,SECTOR_SIZE);
166
167 /* 读出分区超级块,根据魔数是否正确来判断是否存在fs */
168 ide_read(hd,part->start_lba+1,sb_buf,1);
169
170 /* 只支持自己的文件系统,若磁盘上已经有fs就不再初始化了 */
171 if (sb_buf->magic==0x19590318){
172 printk("%s has filesystem\n",part->name);
173 }else{
174 printk("formatting %s's partition %s.....\n",hd->name,part->name);
175 partition_format(part);
176 }
177 }
178 ++part_idx;
179 ++part; // 下一分区
180 }
181 ++dev_no; //下一磁盘
182 }
183 ++channel_no; // 下一通道
184 }
185 sys_free(sb_buf);
186 }
⑥kernel/init.c:

1 #include "init.h"
2 #include "print.h"
3 #include "interrupt.h"
4 #include "timer.h"
5 #include "memory.h"
6 #include "thread.h"
7 #include "console.h"
8 #include "keyboard.h"
9 #include "tss.h"
10 #include "syscall-init.h"
11 #include "ide.h"
12 #include "fs.h"
13
14 /*负责初始化所有模块*/
15 void init_all(){
16 put_str("init_all\n");
17 idt_init(); // 初始化中断
18 mem_init(); // 初始化内存
19 thread_init(); // 初始化线程
20 timer_init(); // 初始化PIT
21 console_init(); // 初始化终端(控制台)
22 keyboard_init(); // 初始化键盘
23 tss_init(); // 初始化tss
24 syscall_init(); // 初始化系统调用
25 ide_init(); // 初始化ide
26 filesys_init(); // 初始化文件系统
27 }
⑦makefile(就不必放出来了)
看看效果:
因为已经是第二遍运行这个程序了,所以会直接输出"sdb1 has filesystem"等等,否则会输出各sdb的info。
然后再挂载分区:正常情况下安装操作系统,无论是Windows还是Linux,安装的过程都是先选择将os安装到哪一个分区上,然后选择以什么文件系统来格式化该分区。Windows通常是fat32、ntfs;Linux通常是ext2或ext3。该分区格式化出文件系统后才能安装操作系统。
修改fs/fs.c,主要是添加mount_partition()并修改一下filesys_init():

1 #include "fs.h"
2 #include "super_block.h"
3 #include "inode.h"
4 #include "dir.h"
5 #include "stdint.h"
6 #include "stdio-kernel.h"
7 #include "list.h"
8 #include "string.h"
9 #include "ide.h"
10 #include "global.h"
11 #include "debug.h"
12 #include "memory.h"
13
14 struct partition* cur_part; // 默认情况下操作的是哪个分区
15
16 /* 在分区中找到名为part_name的分区,并将其指针赋给cur_part */
17 static bool mount_partition(struct list_elem* pelem,int arg){
18 char* part_name=(char*)arg;
19 struct partition* part=elem2entry(struct partition,part_tag,pelem);
20 if (!strcmp(part->name,part_name)){
21 cur_part=part;
22 struct disk* hd=cur_part->my_disk;
23
24 /* sb_buf用来存储从硬盘上读入的超级块 */
25 struct super_block* sb_buf=(struct super_block*)sys_malloc(SECTOR_SIZE);
26
27 /* 在内存中创建分区cur_part的超级块 */
28 cur_part->sb=(struct super_block*)sys_malloc(sizeof(struct super_block));
29 if (cur_part->sb==NULL){
30 PANIC("alloc memory failed!");
31 }
32
33 /* 读入超级块 */
34 memset(sb_buf,0,SECTOR_SIZE);
35 ide_read(hd,cur_part->start_lba+1,sb_buf,1);
36
37 /* 把sb_buf中超级块的信息复制到分区的超级块sb中 */
38 memcpy(cur_part->sb,sb_buf,sizeof(struct super_block));
39
40 /********** 将硬盘上的块位图读入到内存 **********/
41 cur_part->block_bitmap.bits=(uint8_t*)sys_malloc(sb_buf->block_bitmap_sects*SECTOR_SIZE);
42 if (cur_part->block_bitmap.bits==NULL){
43 PANIC("alloc memory failed!");
44 }
45 cur_part->block_bitmap.btmp_bytes_len=sb_buf->block_bitmap_sects*SECTOR_SIZE;
46 /* 从硬盘上读入块位图到分区的block_bitmap.bits */
47 ide_read(hd,sb_buf->block_bitmap_lba,cur_part->block_bitmap.bits,sb_buf->block_bitmap_sects);
48 /***************************************************/
49
50 /********** 将硬盘上的inode位图读入到内存 **********/
51 cur_part->inode_bitmap.bits=(uint8_t*)sys_malloc(sb_buf->inode_bitmap_sects*SECTOR_SIZE);
52 if (cur_part->inode_bitmap.bits==NULL){
53 PANIC("alloc memory failed!");
54 }
55 cur_part->inode_bitmap.btmp_bytes_len=sb_buf->inode_bitmap_sects*SECTOR_SIZE;
56 /* 从硬盘上读入inode位图到分区的inode_bitmap.bits */
57 ide_read(hd,sb_buf->inode_bitmap_lba,cur_part->inode_bitmap.bits,sb_buf->inode_bitmap_sects);
58 /**************************************************/
59
60 list_init(&cur_part->open_inodes);
61 printk("mount %s done!\n",part->name);
62
63 /* 此处返回true是为了迎合主调函数list_traversal的实现,与函数本身功能无关
64 * 只有返回true时list_traversal才会停止遍历,减少了后面元素无意义的遍历 */
65 return true;
66 }
67 return false; // 使list_traversal继续遍历
68 }
69
70 /* 初始化分区元信息,创建文件系统 */
71 static void partition_format(struct partition* part){
72 /* 为方便,一个块大小是一扇区 */
73 uint32_t boot_sector_sects=1;
74 uint32_t super_block_sects=1;
75 uint32_t inode_bitmap_sects=DIV_ROUND_UP(MAX_FILES_PER_PART,BITS_PER_SECTOR); // inode位图所占用的扇区数
76 uint32_t inode_table_sects=DIV_ROUND_UP(((sizeof(struct inode)*MAX_FILES_PER_PART)),SECTOR_SIZE);
77 uint32_t used_sects=boot_sector_sects+super_block_sects+inode_bitmap_sects+inode_table_sects;
78 uint32_t free_sects=part->sec_cnt-used_sects;
79
80 /****************** 块位图占用的扇区数 ********************/
81 uint32_t block_bitmap_sects;
82 block_bitmap_sects=DIV_ROUND_UP(free_sects,BITS_PER_SECTOR);
83 /* block_bitmap_bit_len是位图中位的长度,也是可用块的数量 */
84 uint32_t block_bitmap_bit_len=free_sects-block_bitmap_sects;
85 block_bitmap_sects=DIV_ROUND_UP(block_bitmap_bit_len,BITS_PER_SECTOR);
86 /*********************************************************/
87
88 /* 超级块初始化 */
89 struct super_block sb;
90 sb.magic=0x19590318;
91 sb.sec_cnt=part->sec_cnt;
92 sb.inode_cnt=MAX_FILES_PER_PART;
93 sb.part_lba_base=part->start_lba;
94
95 sb.block_bitmap_lba=sb.part_lba_base+2; // 第0块为bootblock,第1块为superblock
96 sb.block_bitmap_sects=block_bitmap_sects;
97
98 sb.inode_bitmap_lba=sb.block_bitmap_lba+sb.block_bitmap_sects;
99 sb.inode_bitmap_sects=inode_bitmap_sects;
100
101 sb.inode_table_lba=sb.inode_bitmap_lba+sb.inode_bitmap_sects;
102 sb.inode_table_sects=inode_table_sects;
103
104 sb.data_start_lba=sb.inode_table_lba+sb.inode_table_sects;
105 sb.root_inode_no=0;
106 sb.dir_entry_size=sizeof(struct dir_entry);
107
108 printk("%s info:\n",part->name);
109 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);
110
111 struct disk* hd=part->my_disk;
112 /****************************
113 * 1.将超级块写入本分区的1扇区
114 * *************************/
115 ide_write(hd,part->start_lba+1,&sb,1);
116 printk(" super_block_lba:0x%x\n",part->start_lba+1);
117
118 /* 找出数据量最大的元信息,用其尺寸做存储缓冲区 */
119 uint32_t buf_size=(sb.block_bitmap_sects>=sb.inode_bitmap_sects?sb.block_bitmap_sects:sb.inode_bitmap_sects);
120 buf_size=(buf_size>=sb.inode_table_sects?buf_size:sb.inode_table_sects)*SECTOR_SIZE;
121 uint8_t* buf=(uint8_t*)sys_malloc(buf_size); // 申请的内存由内存管理系统清0后返回
122
123 /****************************
124 * 2.将块位图初始化并写入sb.block_bitmap_lba
125 * *************************/
126 /* 初始化块位图block_bitmap */
127 buf[0]|=0x01; // 第0个块预留给根目录,位图中先占位
128 uint32_t block_bitmap_last_byte=block_bitmap_bit_len/8;
129 uint8_t block_bitmap_last_bit=block_bitmap_bit_len%8;
130 uint32_t last_size=SECTOR_SIZE-(block_bitmap_last_byte%SECTOR_SIZE); // last_size是位图中的填充部分
131
132 /* 2.1先将位图最后一字节到其所在扇区结束全置为1,即超出实际块数部分直接置为已占用 */
133 memset(&buf[block_bitmap_last_byte],0xff,last_size);
134
135 /* 2.2再将上一步中覆盖的最后一字节内有效位重新置0 */
136 uint8_t bit_idx=0;
137 while (bit_idx<=block_bitmap_last_bit){
138 buf[block_bitmap_last_byte]&=~(1<<bit_idx++);
139 }
140 ide_write(hd,sb.block_bitmap_lba,buf,sb.block_bitmap_sects);
141
142 /***************************
143 * 3.将inode位图初始化并写入sb.inode_bitmap_lba
144 * ************************/
145 /* 先清空缓冲区 */
146 memset(buf,0,buf_size);
147 buf[0]|=0x1; // 第0个inode分给了根目录
148 /* 由于inode_table中共4096个inode,位图中inode_bitmap正好占用1扇区,
149 * 即inode_bitmap_sects等于1,所以位图中的位全都代表inode_table中的inode,
150 * 无须再像block_bitmap那样单独处理最后一扇区的剩余部分,
151 * inode_bitmap所在的扇区中没有多余的无效位 */
152 ide_write(hd,sb.inode_bitmap_lba,buf,sb.inode_bitmap_sects);
153
154 /*************************
155 * 4.将inode数组初始化并写入sb.inode_table_lba
156 * **********************/
157 /* 准备写inode_table中的第0项,即根目录所在的inode */
158 memset(buf,0,buf_size); // 先清空缓冲区buf
159 struct inode* i=(struct inode*)buf;
160 i->i_size=sb.dir_entry_size*2; // . & ..
161 i->i_no=0; // 根目录占inode数组中第0个inode
162 i->i_sectors[0]=sb.data_start_lba; // 由于上面的memset,i_sectors数组的其它元素都初始化为0
163 ide_write(hd,sb.inode_table_lba,buf,sb.inode_table_sects);
164
165 /*************************
166 * 5.将根目录初始化并写入sb.data_start_lba
167 * **********************/
168 /* 写入根目录的两个目录项 . & .. */
169 memset(buf,0,buf_size);
170 struct dir_entry* p_de=(struct dir_entry*)buf;
171
172 /* 初始化当前目录"." */
173 memcpy(p_de->filename,".",1);
174 p_de->i_no=0;
175 p_de->f_type=FT_DIRECTORY;
176 ++p_de;
177
178 /* 初始化当前目录父目录 ".." */
179 memcpy(p_de->filename,"..",2);
180 p_de->i_no=0; // 根目录的父目录是自己
181 p_de->f_type=FT_DIRECTORY;
182
183 /* sb.data_start_lba已经分配给了根目录,里面是根目录的目录项 */
184 ide_write(hd,sb.data_start_lba,buf,1);
185
186 printk(" root_dir_lba:0x%x\n",sb.data_start_lba);
187 printk("%s format done\n",part->name);
188 sys_free(buf);
189 }
190
191 /* 在磁盘上搜索文件系统,若没有则格式化分区创建文件系统 */
192 void filesys_init(){
193 uint8_t channel_no=0,dev_no=0,part_idx=0;
194
195 /* sb_buf用来存储从硬盘上读入的超级块 */
196 struct super_block* sb_buf=(struct super_block*)sys_malloc(SECTOR_SIZE);
197
198 if (sb_buf==NULL){
199 PANIC("alloc memory failed!");
200 }
201 printk("searching filesystem......\n");
202 while (channel_no<channel_cnt){
203 dev_no=0;
204 while (dev_no<2){
205 if (dev_no==0){
206 ++dev_no;
207 continue;
208 }
209 struct disk* hd=&channels[channel_no].devices[dev_no];
210 struct partition* part=hd->prim_parts;
211 while (part_idx<12){ // 四个主分区+八个逻辑分区
212 if (part_idx==4){ // 逻辑分区
213 part=hd->logic_parts;
214 }
215
216 /* channels数组是全局变量,默认值为0,disk属于其嵌套结构,
217 * partition又为disk的嵌套结构,因此partition中的成员默认也为0.
218 * 若partition未初始化,则partition中的成员仍为0.
219 * 下面处理存在的分区. */
220 if (part->sec_cnt!=0){ // 如果分区存在
221 memset(sb_buf,0,SECTOR_SIZE);
222
223 /* 读出分区超级块,根据魔数是否正确来判断是否存在fs */
224 ide_read(hd,part->start_lba+1,sb_buf,1);
225
226 /* 只支持自己的文件系统,若磁盘上已经有fs就不再初始化了 */
227 if (sb_buf->magic==0x19590318){
228 printk("%s has filesystem\n",part->name);
229 }else{
230 printk("formatting %s's partition %s.....\n",hd->name,part->name);
231 partition_format(part);
232 }
233 }
234 ++part_idx;
235 ++part; // 下一分区
236 }
237 ++dev_no; //下一磁盘
238 }
239 ++channel_no; // 下一通道
240 }
241 sys_free(sb_buf);
242
243 /* 确定默认操作的分区 */
244 char default_part[8]="sdb1";
245 /* 挂载分区 */
246 list_traversal(&partition_list,mount_partition,(int)default_part);
247 }
就是多显示一句“mount sdb1 done!”,挂载得好低调。
2.实现文件描述符
①先要创建PCB中的文件描述符数组,thread/thread.h,添加一句宏定义和并在pcb结构体task_struct中加入文件描述符数组:
#define MAX_FILES_OPEN_PER_PROC 8
uint32_t fd_table[MAX_FILES_OPEN_PER_PROC]; // 文件描述符数组,每个任务最多打开的文件数为8
②thread/thread.c中初始化,对于init_thread()函数:

1 void init_thread(struct task_struct* pthread,char* name,int prio){
2 memset(pthread,0,sizeof(*pthread));
3 pthread->pid=allocate_pid();
4 strcpy(pthread->name,name);
5
6 if (pthread==main_thread){
7 /* 由于把main函数也封装成一个线程,并且它一直是运行的,故将其直接设为TASK_RUNNING */
8 pthread->status=TASK_RUNNING;
9 }else{
10 pthread->status=TASK_READY;
11 }
12
13 /* self_kstack是线程自己在内核态下使用的栈顶地址 */
14 pthread->self_kstack=(uint32_t*)((uint32_t)pthread+PG_SIZE);
15 pthread->priority=prio;
16 pthread->ticks=prio;
17 pthread->elapsed_ticks=0;
18 pthread->pgdir=NULL;
19
20 /* 预留标准输入输出 */
21 pthread->fd_table[0]=0;
22 pthread->fd_table[1]=1;
23 pthread->fd_table[2]=2;
24 /* 其余全置为-1 */
25 uint8_t fd_idx=3;
26 while (fd_idx<MAX_FILES_OPEN_PER_PROC){
27 pthread->fd_table[fd_idx++]=-1;
28 }
29
30 pthread->stack_magic=0x19870916; // 自定义魔数
31 }
ok,本篇博客到此结束,下一篇我们来看看文件操作相关函数的实现。
参考博客:
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· C++代码改造为UTF-8编码问题的总结
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库