《操作系统真象还原》第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 }
View Code
复制代码

⑥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 }
View Code
复制代码

⑦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 }
View Code
复制代码

就是多显示一句“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 }
View Code
复制代码

ok,本篇博客到此结束,下一篇我们来看看文件操作相关函数的实现。


参考博客:

posted @   Hell0er  阅读(99)  评论(0编辑  收藏  举报
编辑推荐:
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· C++代码改造为UTF-8编码问题的总结
阅读排行:
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
点击右上角即可分享
微信分享提示