开始之前先修正一个BUG,害叔找了半天,就是硬盘驱动进程的栈空间太小了,搞得栈溢出波及到其它的数据,在proc.h把栈空间设为2K就好。
下面再来补充硬盘驱动程序,我们先取得对应分区的信息,则先增加相应的宏:
include/const.h
- #define HD_GET_PRT_INFO 5 /* 得到指定分区的信息 */
再在硬盘驱动进程的执行体中添加:
kernel/hd.c
- case HD_GET_PRT_INFO:
- Get_Prt_Info(&m);
- break;
接着添加Get_Prt_Info函数,hd.c追加:
- /*------------------------------------------------------------------Get_Prt_Info
- 获得指定分区(设备号指出)的信息
- */
- static void Get_Prt_Info(Message *m)
- {
- int device = m->i1; /* 得到设备号 */
- /* 得到要返回的缓冲区,强制转换为分区信息结构 */
- Prt_Info *ret_prt_info = (Prt_Info*)m->r1;
- /* 主分区 */
- if(device >= 0 && device <= 5)
- {
- ret_prt_info->base = hd_prt_info.primary[device].base;
- ret_prt_info->sec_num = hd_prt_info.primary[device].sec_num;
- }
- /* 扩展分区 */
- else if(device >=16 && device <=80)
- {
- ret_prt_info->base = hd_prt_info.logical[device - 16].base;
- ret_prt_info->sec_num = hd_prt_info.logical[device - 16].sec_num;
- }
- }
OK,现在来进行文件系统的设计,文件系统有两方面的意思:一是在硬盘上的一组数据结构,二是一组能对与之对应的数据结构管理文件的机制,我们先来做物理意义的工作,就是数据结构的定义。
在前面写boot和loader的时候已经接触过这方面的内容,就是FAT12文件系统。在这里我们的文件系统也很类似。
在指定的分区中,0扇区为引导扇区,我们单纯的在里面放置引导代码,先不管它。
下一步就是超级块,放在1扇区,记录此文件系统的各种信息,比如最多可以存放多少个文件等等,下面给出结构,以下结构均在include/fs.h中定义:
- /* 超级块 */
- typedef struct s_super_block
- {
- u32 identity; /* 表示本文件系统的信息 */
- u32 inode_num; /* i结点的个数 */
- u32 sector_total; /* 此文件系统所在分区的扇区总数 */
- u32 imap_sec_total; /* i结点映射表所占用的扇区总数 */
- u32 smap_sec_total; /* 数据区的扇区的使用映射表的扇区总数 */
- u32 first_data_sec; /* 数据区的第一个扇区的索引 */
- u32 inodes_sec_total; /* i结点数组所占用的扇区数 */
- u32 root_dir_inode; /* 根目录所占用的文件所属的i节点索引 */
- u32 inode_size; /* 一个i结点的字节数 */
- u32 dir_entry_size; /* 一个目录项的字节数 */
- }Super_Block;
- #define SUPER_BLOCK_SIZE 10 * 4 /* 超级块占用的字节数 */
再下面是i结点的映射表,放在1扇区。记录i结点的使用情况,占据一个扇区,每一位记录一个i结点的使用情况,1代表已使用,0表示未使用。这样就可以记录512*8=4096个i结点,就是说此文件系统最多可以记录4096个文件,但索引为0的i结点不使用,表示错误的i结点。根目录也是一个文件,占用一个i结点。并且3个TTY也算是一个文件,这是借鉴LINUX的结果。所以最多只有4096-5=4091个文件。i结点的作用稍后再说。
接着是文件数据区的扇区部分的映射表,起始扇区在2扇区,作用与i结点的映射表一样,用来管理扇区的使用情况,占据扇区个数为:整个硬盘的扇区数/512*8,我们知道实际上有些扇区是不能来存放数据的,这里懒得算了,就当浪费一些扇区吧,但要在这个表的第0位是代表实际的数据区的第一个扇区的使用情况的,这里需要注意。还有第0个数据扇区我们是不用的。
再下面是i结点数据,,i结点的作用是指明一个文件的类型,占用的扇区大小等信息,下面是定义:
- /* i结点 */
- typedef struct s_i_node
- {
- u32 i_mode; /* 访问模式 */
- u32 i_size; /* 所表示的文件实际占用的字节数 */
- u32 i_start_sec; /* 所表示的文件的首扇区的索引 */
- u32 i_sect_total; /* 所表示的文件实际占用的扇区数,是固定的 */
- u8 unused[16]; /* 无意义,用于对齐 */
- }I_Node;
- #define I_NODE_SIZE 8 * 4 /* 一个i结点所占用的字节数 */
最后就是根目录区了,是一个Dir_Entry数组,根目录区也算一个文件,所以它的起始扇区位于数据区的第1个扇区,第0个扇区不使用,这里的结构如下:
- #define MAX_FILENAME_SIZE 12 /* 一个文件的最长的文件名 */
- typedef struct s_dir_entry
- {
- u32 inode_index; /* 此文件所属的i结点的索引 */
- char file_name[MAX_FILENAME_SIZE]; /* 文件名 */
- }Dir_Entry;
- #define DIR_ENTRY_SIZE sizeof(Dir_Entry) /* 一个根目录文件项的字节大小 */
OK,整个系统就设计好了,下面的工作就是在实际的硬盘上填充这些信息了。
kernel/fs.c
- /*-----------------------------------------------------------------------Init_FS
- 初始化文件系统,取得硬盘的分区信息
- */
- static void Init_FS()
- {
- Message m;
- m.msg_type = HD_INFO;
- Send_Receive_Shell(BOTH,PROC_HD_PID,&m);
- }
- /*-----------------------------------------------------------------------Make_FS
- 把文件系统的信息写到磁盘中
- */
- static void Make_FS()
- {
- /* 取得文件系统的宿主分区的信息 */
- Message m;
- Prt_Info prt_info;
- m.msg_type = HD_GET_PRT_INFO;
- m.i1 = FS_DEV;
- m.r1 = &prt_info;
- Send_Receive_Shell(BOTH,PROC_HD_PID,&m);
- Printf("FILE SYSTEM PARTITION HAS 0x%x SECTORS/n",prt_info.sec_num);
- /* 开始填充填充超级块 */
- Super_Block sb;
- sb.identity = IDENTITY; /* 填充文件系统的标识 */
- sb.inode_num = 4096; /* i结点的个数 */
- sb.sector_total = prt_info.sec_num; /* 此文件系统所在分区的扇区总数 */
- sb.imap_sec_total = 1; /* i结点映射表所占用的扇区总数 */
- sb.smap_sec_total = prt_info.sec_num / 4096 + 1; /* 数据区的映射表所占的扇区数 */
- sb.inodes_sec_total = I_NODE_SIZE * sb.inode_num / SECTOR_SIZE; /* i结点数组所占用的扇区数 */
- /* 数据区的第一个扇区的索引 */
- sb.first_data_sec = 1 + 1 + sb.imap_sec_total + sb.smap_sec_total + sb.inodes_sec_total;
- sb.root_dir_inode = 1; /* 根目录所占用的文件所属的i节点索引 */
- sb.inode_size = I_NODE_SIZE; /* 一个i结点的字节数 */
- sb.dir_entry_size = DIR_ENTRY_SIZE; /* 一个目录项的字节数 */
- Memory_Set((u8*)FS_BUF,0,FS_BUF_SIZE); /* 缓冲区清零 */
- Memory_Copy((void*)FS_BUF,&sb,sizeof(Super_Block)); /* 复制超级块到缓冲区中 */
- Write_Sector(FS_DEV,1); /* 写入硬盘 */
- /* 打印文件系统各部分在分区的偏移字节 */
- Printf("DEV:0x%x00 SUPER BLOCK:0x%x00/nINODE MAP:0x%x00 SECTOR MAP:0x%x00/nINODE ARRAY:0x%x00,FIRST DATA SECTOR:0x%x00/n",
- prt_info.base * 2,(prt_info.base + 1) * 2,(prt_info.base + 2) * 2,
- (prt_info.base + 3) * 2,(prt_info.base + 3 + sb.smap_sec_total) * 2,
- (prt_info.base + sb.first_data_sec) * 2);
- /* 填充i结点映射表 */
- Memory_Set((u8*)FS_BUF,0,SECTOR_SIZE); /* 缓冲区清零 */
- /*
- 前5个文件被占用,分别是:
- 保留的i结点
- 根目录区代表的文件
- TTY0-3
- */
- *(u8*)FS_BUF = 0x1f;
- Write_Sector(FS_DEV,2); /* 写入硬盘 */
- /* 填充扇区使用映射表 */
- /* 先把硬盘中相关的扇区清零 */
- Memory_Set((u8*)FS_BUF,0,FS_BUF_SIZE); /* 缓冲区清零 */
- int i;
- int j;
- for(i = 0;i < sb.smap_sec_total;i++)
- {
- Write_Sector(FS_DEV,3 + i);
- }
- /* 根目录区占用ROOT_DIR_SEC_NUM个扇区 + 保留扇区 */
- int occupy_data_sec_num = ROOT_DIR_SEC_NUM + 1;
- /* 在缓冲中先写入正确的值 */
- for(i = 0;i < occupy_data_sec_num / 8;i++)
- {
- *(u8*)(FS_BUF + i) = 0xff;
- }
- for(j = 0;j < occupy_data_sec_num % 8;j++)
- {
- *(u8*)(FS_BUF + i) |= (1 << j);
- }
- Write_Sector(FS_DEV,3); /* 写入硬盘 */
- /* 填充i结点数组 */
- Memory_Set((u8*)FS_BUF,0,SECTOR_SIZE); /* 缓冲区清零 */
- /* 先把硬盘中相关的扇区清零 */
- for(i = 0;i < sb.inodes_sec_total;i++)
- {
- Write_Sector(FS_DEV,3 + sb.smap_sec_total + i);
- }
- /* 填充根目录区的i结点 */
- /* 执行缓冲区的下一个i结点的位置,因为0结点不用 */
- I_Node *i_node = (I_Node*)(FS_BUF + I_NODE_SIZE);
- /* 填充根目录的i结点 */
- i_node->i_mode = INODE_MODE_DIR; /* 文件类型为根目录 */
- i_node->i_size = DIR_ENTRY_SIZE * 4; /* 实际字节数为4个Dir_Entry的大小 */
- i_node->i_start_sec = sb.first_data_sec; /* 起始扇区为数据区的第一个字节 */
- i_node->i_sect_total = ROOT_DIR_SEC_NUM; /* 实际占用扇区大小为ROOT_DIR_SEC_NUM */
- /* 填充TTY的i结点 */
- for(i = 0;i < TTY_NUM;i++)
- {
- i_node++;
- i_node->i_mode = INODE_MODE_SPECIAL; /* 文件类型为特殊的 */
- i_node->i_size = 0; /* 不占磁盘,当然为0 */
- i_node->i_start_sec = i; /* 这里表示TTY的编号 */
- i_node->i_sect_total = 0; /* 不占磁盘,当然为0 */
- }
- Write_Sector(FS_DEV,3 + sb.smap_sec_total); /* 写入硬盘 */
- /* 填充根目录区的Dir_Entry数组 */
- Memory_Set((u8*)FS_BUF,0,SECTOR_SIZE); /* 缓冲区清零 */
- /* 先把硬盘中相关的扇区清零,加1是因为数据区的第一个扇区不用 */
- for(i = 0;i < ROOT_DIR_SEC_NUM;i++)
- {
- Write_Sector(FS_DEV,sb.first_data_sec + i + 1);
- }
- /* 填充根目录文件的Dir_Entry */
- Dir_Entry *dir_entry = (Dir_Entry*)FS_BUF;
- dir_entry->inode_index = 1; /* 填充i结点的索引 */
- Str_Cpy(dir_entry->file_name,"."); /* 名字复制 */
- /* 填充各TTY文件 */
- char *tty_name[] = {"tty0","tty1","tty2"};
- for(i = 0;i < TTY_NUM;i++)
- {
- dir_entry++;
- dir_entry->inode_index = i + 2;
- Str_Cpy(dir_entry->file_name,tty_name[i]);
- }
- Write_Sector(FS_DEV,sb.first_data_sec + 1); /* 写入硬盘 */
- }
- /*------------------------------------------------------------------Write_Sector
- 填充一个扇区的数据到指定的device的分区的offset_sec个扇区中
- 数据来自FS_BUF缓冲区
- */
- static void Write_Sector(int device,int offset_sec)
- {
- Message m;
- m.msg_type = HD_WRITE;
- m.i1 = device;
- m.i2 = offset_sec;
- m.i3 = SECTOR_SIZE;
- m.r1 = (void*)FS_BUF;
- Send_Receive_Shell(BOTH,PROC_HD_PID,&m);
- }
注释得还可以了。。不赘述。。
用到的宏如下:
include/fs.h
- #define FS_BUF 0x600000 /* 填充FS信息使用的缓冲区 */
- #define FS_BUF_SIZE 0x100000 /* 此缓冲区的大小 */
- #define FS_DEV 32 /* FS安装的逻辑分区的设备号 */
- #define ROOT_DIR_SEC_NUM 128 /* 根目录占用的扇区数 */
- #define IDENTITY 0xabcd /* 此文件系统的标识 */
- /* 以下为i结点的模式 */
- #define INODE_MODE_DIR 0x1 /* 表示目录 */
- #define INODE_MODE_FILE 0x2 /* 表示普通文件 */
- #define INODE_MODE_SPECIAL 0x4 /* 表示特殊文件 */
还添加了一个Memory_Set函数,如下:
lib/lib_kernel_in_c.c
- /*--------------------------------------------------------------------Memory_Set
- 把addr指定的内存开始的size个字节置为set_value
- */
- void Memory_Set(u8 *addr,u8 set_value,u32 size)
- {
- while(size-- > 0)
- {
- *addr++ = set_value;
- }
- }
下面是检验结果的时候了,运行结果如下:
从中我们得到了文件系统的各部分在硬盘的偏移字节数,下面依次来查看。
首先是超级块:
i结点映射表:
数据区扇区映射表:
i结点数组:
最后是根目录区:
仔细对照下,嗯,没错。