自己动手写文件系统
這一課中,將創(chuàng)建一個(gè)磁盤分區(qū),并在她上面建立文件系統(tǒng)。文章看起來(lái)比較長(zhǎng),但是過(guò)程比較簡(jiǎn)單。
大家都知道,硬盤最小的管理單位是扇區(qū),每個(gè)扇區(qū)512字節(jié)。有兩種方式定位訪問(wèn)磁盤。一種是CHS模式(Cylinder, Head,?Sector),她反映了磁盤的物理結(jié)構(gòu)。扇區(qū)(Sector)是磁盤可以直接訪問(wèn)的最小數(shù)據(jù)單位(即一個(gè)塊)。柱面(Cylinder)也成為磁軌,通常一個(gè)1.44M軟盤每柱面有18扇區(qū),而硬盤每柱面含有64扇區(qū)。磁頭(Head)表示有多少個(gè)盤面(通常每個(gè)盤面使用兩個(gè)磁頭,正反盤面各一個(gè))。關(guān)于CHS相關(guān)的資料網(wǎng)絡(luò)上可以搜索出很多,這里不贅述。
例如我建立了一個(gè)100M字節(jié)的磁盤映象文件,在bocsh可以這樣定義它的結(jié)構(gòu):
ata0-master: type=disk, path="minix.img", mode=flat, cylinders=200, heads=16, spt=63
計(jì)算:16 * 64 * 200 * 512 =?100 M(512是每扇區(qū)字節(jié)數(shù))
很明顯,在CHS模式下,磁盤容量最大可表示為??柱面數(shù)?*?每柱面扇區(qū)數(shù)?*?磁頭數(shù)?* 512.?注意:柱面和磁頭從0開始計(jì)數(shù),而扇區(qū)數(shù)則從1開始。不要問(wèn)我為什么。
另一種硬盤訪問(wèn)模式叫LBA(Linear Block Addressing:線性塊尋址模式),它是物理硬盤的邏輯尋址方式。和線性內(nèi)存尋址一樣,LBA用線性地址來(lái)定位扇區(qū)(這樣,柱面和磁頭就不會(huì)用到了)。這種方式非常簡(jiǎn)單,但是驅(qū)動(dòng)使用的是CHS模式,所以需要進(jìn)行轉(zhuǎn)換(LBA也是從0開始計(jì)數(shù)的)。
LBA =(C-Cs)*PH*PS+(H-Hs)*PS+(S-Ss)
磁盤大小LBA值?= C *?H *?S - 1
方向計(jì)算(由LBA獲取CHS:下面的公式好像有問(wèn)題,讀者最好在網(wǎng)上找到正確的資料):
C = LBA / (heads per cylinder * sectors per track)
temp = LBA % (heads per cylinder * sectors per track)
H = temp / sectors per track
S = temp % sectors per track + 1
CHS跟LBA之間,其實(shí)相當(dāng)于seg:offset與線形地址的關(guān)系。不同點(diǎn)在于:
如果你使用vmware建立一個(gè)虛擬磁盤,可以在啟動(dòng)時(shí)進(jìn)入bios看到該虛擬磁盤的?CHS?和?LBA?信息。
下面三個(gè)demo對(duì)應(yīng)的源程序分別為dpt(分區(qū)表disk partition table),fs和root目錄。
我們可以定義一個(gè)數(shù)據(jù)結(jié)構(gòu):
07/dpt/hd.c
struct HD_PARAM {
??? unsigned int cyl;
??? unsigned int head;
??? unsigned int sect;
} HD0 = {208, 16, 63};????????//?定義一個(gè)104M磁盤
?
由于skelix使用LBA尋址,所以需要把LBA地址轉(zhuǎn)換成CHS地址:
??? unsigned int cyl = lba / (HD0.head * HD0.sect);
??? unsigned int head = (lba % (HD0.head * HD0.sect)) / HD0.sect;
??? unsigned int sect = (lba % (HD0.head * HD0.sect)) % HD0.sect + 1;
現(xiàn)在已經(jīng)得到chs地址了,我們將通過(guò)?0x1F0-0x1F7端口來(lái)訪問(wèn)硬盤。
(這些繁文縟節(jié)一點(diǎn)也不好玩,但是我又不得不講)
07/include/hd.h
#define HD_PORT_DATA??? ???? 0x1f0
#define HD_PORT_ERROR??? ??? 0x1f1
#define HD_PORT_SECT_COUNT?? 0x1f2
#define HD_PORT_SECT_NUM???? 0x1f3
#define HD_PORT_CYL_LOW??? ? 0x1f4
#define HD_PORT_CYL_HIGH???? 0x1f5
#define HD_PORT_DRV_HEAD???? 0x1f6
#define HD_PORT_STATUS??? ?? 0x1f7
#define HD_PORT_COMMAND??? ? 0x1f7
?
另人恐慌,不過(guò)可以很清楚的來(lái)個(gè)分組。我們從0x1f0端口讀取數(shù)據(jù),
如果發(fā)生了錯(cuò)誤就從0x1f1讀取錯(cuò)誤碼,
0x1f2和0x1f3端口設(shè)置扇區(qū)號(hào),
0x1f4和0x1f5端口設(shè)置柱面號(hào),
0x1f6端口設(shè)置磁頭號(hào)。
端口0x1f7用于讀硬盤狀態(tài)或者寫操作命令。
寫0x1f7端口常用的命令字如下:
#define HD_READ??? ??? ??? ? 0x20????????//?讀硬盤
#define HD_WRITE??? ??? ???? 0x30????????//?寫硬盤
通過(guò)上面定義,我們可以粗略的認(rèn)為通過(guò)以下幾個(gè)步驟訪問(wèn)硬盤,
(為了簡(jiǎn)化步驟,暫時(shí)不做一些錯(cuò)誤檢測(cè))
下面這個(gè)函數(shù)hd_rw就是用來(lái)操作硬盤的接口。
07/hd.c
??? while ((inb(HD_PORT_STATUS)&0xc0)!=0x40)
??? //?等待硬盤狀態(tài),直到可以寫或讀為止,狀態(tài)字節(jié)說(shuō)明如下:
| Bit 7 | 控制器忙 |
| Bit 6 | 驅(qū)動(dòng)器準(zhǔn)備就緒 |
| Bit 5 | 寫出錯(cuò) |
| Bit 4 | 尋道結(jié)束 |
| Bit 3 | 數(shù)據(jù)請(qǐng)求復(fù)位 |
| Bit 2 | ECC效驗(yàn)錯(cuò)誤 |
| Bit 1 | 硬盤已收到索引 |
| Bit 0 | 命令執(zhí)行錯(cuò)誤 |
??? outb(sects_to_access, HD_PORT_SECT_COUNT); //?準(zhǔn)備讀或?qū)懚嗌賯€(gè)扇區(qū)
?
??? outb(sect, HD_PORT_SECT_NUM);????????????? //?發(fā)送chs地址
??? outb(cyl, HD_PORT_CYL_LOW);
??? outb(cyl>>8, HD_PORT_CYL_HIGH);
??? outb(0xa0|head, HD_PORT_DRV_HEAD);???????? // a0是第一塊硬盤
| Bits 7-5 | 必須是?101b |
| Bit 4 | HD0(=0第一塊硬盤), HD1(=1第二塊硬盤) |
| Bits 3-0 | 磁頭號(hào) |
?
??? outb(com, HD_PORT_COMMAND);????????????????//?硬盤操作命令
| HD_READ=0x20 | 如果不成功會(huì)反復(fù)讀 |
| HD_WRITE=0x30 | 如果不成功會(huì)反復(fù)寫 |
??? if (com == HD_READ)
??? ??? insl(HD_PORT_DATA, buf, sects_to_access<<7);
??? else if (com == HD_WRITE)
??? ??? outsl(buf, sects_to_access<<7, HD_PORT_DATA);
????//?說(shuō)明:insl和outsl是從io端口讀寫一串?dāng)?shù)據(jù)的宏匯編指令,
????//?這里使用的是pio方式,mdma和udma不作討論
??? while ((inb(HD_PORT_STATUS)&0x80));????????//?等待完成
事實(shí)上,這只是最簡(jiǎn)單的處理流程,連錯(cuò)誤檢測(cè)都沒有。雖然是pio方式,
仍然可以使用中斷,以避免上面的while循環(huán)等待,而減少內(nèi)核浪費(fèi)的時(shí)間。
不過(guò)skelix不準(zhǔn)備做這么復(fù)雜。
?
磁盤分區(qū)表(disk partitiontable,以下簡(jiǎn)稱dpt)
現(xiàn)在我們有能力訪問(wèn)硬盤的扇區(qū)了,需要把這些扇區(qū)管理起來(lái)。硬盤的第一個(gè)扇區(qū)必須包含硬盤分區(qū)表。這個(gè)分區(qū)表從第一個(gè)扇區(qū)的0x1be偏移開始,長(zhǎng)度是64字節(jié)。最多可以包含4個(gè)分區(qū)(本文不考慮邏輯分區(qū),都使用主分區(qū))。這4個(gè)分區(qū)的相對(duì)偏移分別為0x1be, 0x1ce, 0x1de, 0x1fe,第一個(gè)扇區(qū)的最后兩個(gè)字節(jié)必須是0xaa55。
每個(gè)分區(qū)項(xiàng)的格式如下:
| Byte? 0 | 引導(dǎo)標(biāo)識(shí) |
| Bytes 1-3 | CHS?信息 |
| Byte? 4 | 分區(qū)類型 |
| Bytes 5-7 | CHS?信息 |
| Bytes 8-11 | 該分區(qū)的起始扇區(qū)號(hào) |
| Bytes 12-16 | 扇區(qū)數(shù)量 |
?
第0個(gè)字節(jié)是引導(dǎo)標(biāo)識(shí),如果值為0x80標(biāo)識(shí)可引導(dǎo)。對(duì)于一塊硬盤來(lái)說(shuō),只有一個(gè)分區(qū)是可以引導(dǎo)的。第4個(gè)字節(jié)定義分區(qū)類型,例如FAT32, NTFS, ext2等。有一篇文章http://www.osdever.net/documents/partitiontypes.php?the_id=35,里面定義了常見的分區(qū)類型。
從上面的表可以看到dpt項(xiàng)有兩種方式定位扇區(qū),一種是通過(guò)字節(jié)1~3和字節(jié)5~7中的CHS信息,另一種是字節(jié)8~16的LBA信息。隨便用哪一種都是一樣的,在本文中使用LBA方式,所以我不準(zhǔn)備解釋字節(jié)1~3和字節(jié)5~7的具體格式了。
現(xiàn)在我們來(lái)建立分區(qū)表:
07/dpt/hd.c
?
static void
setup_DPT(void) {
??? unsigned char sect[512] = {0};
??? sect[0x1be] = 0x80;?????????????//?第一個(gè)分區(qū)可引導(dǎo)
??? sect[0x1be + 0x04] = FST_FS;????//?自定義的數(shù)據(jù)分區(qū)類型
??? *(unsigned long *)§[0x1be + 0x08] = 1;
??? *(unsigned long *)§[0x1be + 0x0c] = 85*1024*2; /* 85MB */
??? sect[0x1ce + 0x04] = FST_SW;????//?自定義的交換分區(qū)類型,后續(xù)課程使用
??? *(unsigned long *)§[0x1ce + 0x08] = 85*1024*2+1;
??? *(unsigned long *)§[0x1ce + 0x0c] = 16*1024*2; /* 16MB */
??? sect[0x1fe] = 0x55;
??? sect[0x1ff] = 0xaa;
??? hd_rw(0, HD_WRITE, 1, sect);????//?寫入磁盤
????//?寫到扇區(qū)0,扇區(qū)數(shù)為1,sect是寫入緩沖
}
現(xiàn)在,我們?cè)趩?dòng)的過(guò)程中把分區(qū)表信息打印出來(lái):
07/dpt/hd.c
void
verify_DPT(void) {
??? unsigned char sect[512];
??? unsigned i = 0;
??? unsigned int *q = (unsigned int *)(HD0_ADDR);
????//?變量q存放讀出的分區(qū)表(起始扇區(qū)號(hào)和扇區(qū)數(shù)量)數(shù)組
??? hd_rw(0, HD_READ, 1, sect);
??? if ((sect[0x1fe]==0x55) && (sect[0x1ff]==0xaa)) {
??????? unsigned char *p = §[0x1be];
??????? char *s;
??? ??? kprintf(KPL_DUMP, "?? | Bootable | Type????? | Start Sector | Capacity \n");
??????? for (i=0; i<4; ++i) {
??? ??? ??? kprintf(KPL_DUMP, " %d ", i);
??????????? if (p[0x04] == 0x00) {
??????????????? kprintf(KPL_DUMP, "| Empty\n");
??? ??? ??? ??? p += 16;
??? ??? ??? ??? q += 2;
??? ??? ??? ??? continue;
??? ??? ??? }
??? ??? ??? if (p[0x00] == 0x80)
??? ??? ??? ??? s = "| Yes????? ";
??? ??? ??? else
??? ??? ??? ??? s = "| No?????? ";
??????????? kprintf(KPL_DUMP, s);
??? ??? ??? /* system indicator at offset 0x04 */
??? ??? ??? if (p[0x04] == FST_FS) {
??? ??? ??? ??? kprintf(KPL_DUMP, "| Skelix FS ");
??? ??? ??? } else if (p[0x04] == FST_SW) {
??? ??? ??? ??? kprintf(KPL_DUMP, "| Skelix SW ");
??? ??? ??? } else
??? ??? ??? ??? kprintf(KPL_DUMP, "| Unknown?? ", *p);
??????????? /* starting sector number */
??? ??? ??? *q++ = *(unsigned long *)&p[0x08];
??? ??? ??? kprintf(KPL_DUMP, "| 0x%x?? ", *(unsigned long*)&p[0x08]);
??? ??? ??? /* capacity */
??? ??? ??? *q++ = *(unsigned long*)&p[0x0c];
??? ??? ??? kprintf(KPL_DUMP, "| %dM\n", (*(unsigned long*)&p[0x0c]*512)>>20);
??????????? //?保存到內(nèi)存中,32字節(jié)偏移,32字節(jié)長(zhǎng)度
??????????? p += 16;
??? ??? }
??? } else {
??? ??? kprintf(KPL_DUMP, "No bootable DPT found on HD0\n");
??? ??? kprintf(KPL_DUMP, "Creating DPT on HD0 automaticly\n");
??? ??? kprintf(KPL_DUMP, "Creating file system whatever you want it or not!!\n");
??? ??? setup_DPT();
??? ??? verify_DPT();
??? }
}
在我們編譯觀察結(jié)果之前,還需要修改任務(wù)函數(shù)task1_run?和?task2_run,因?yàn)樗鼈儠?huì)滾動(dòng)屏幕把我們想要的結(jié)果覆蓋掉。
07/init.c
void
do_task1(void) {
??? __asm__ ("incb 0xb8000+160*24+2");
}
void
do_task2(void) {
??? __asm__ ("incb 0xb8000+160*24+4");
}
按例,還得改改Makefile,加入?hd.o?到?KERNEL_OBJS,?并在sti()?之前就調(diào)用?verify_DPT()函數(shù):
07/dpt/Makefile
KERNEL_OBJS= load.o init.o isr.o timer.o libcc.o scr.o kb.o task.o kprintf.o hd.o exceptions.o
?
07/dpt/init.c
??? __asm__ ("ltrw??? %%ax\n\t"::"a"(TSS_SEL));
??? __asm__ ("lldt??? %%ax\n\t"::"a"(LDT_SEL));
??? kprintf(KPL_DUMP, "Verifing disk partition table....\n");
??? verify_DPT();?? ?? /* <<<<< Here it is */
??? sti();???????????? //?任務(wù)調(diào)度可以進(jìn)行了
?
編譯運(yùn)行一把,OK!(最好使用一個(gè)未分區(qū)的磁盤映象來(lái)測(cè)試)
文件系統(tǒng)
分區(qū)已經(jīng)建立,下一步就是組織各個(gè)分區(qū)上的文件系統(tǒng)。雖然我們可以做到訪問(wèn)扇區(qū)了,但是對(duì)于訪問(wèn)文件卻是不方便的。需要做一些結(jié)構(gòu)化的工作,為此定義了一個(gè)表示文件的數(shù)據(jù)結(jié)構(gòu):
07/fs/include/fs.h
?
#define FT_NML??? 1???????????? /* normal file */
#define FT_DIR??? 2
struct INODE {????????????????? /*?存放在硬盤里面,在inode區(qū)?*/
??? unsigned int i_mode;??? ??? /* file mode */
??? unsigned int i_size;??? ??? /* size in bytes */
??? unsigned int i_block[8];
};
*nix?用戶可能對(duì)inode比較敏感。現(xiàn)在我來(lái)一一解釋這個(gè)數(shù)據(jù)結(jié)構(gòu)中的域,i_mode定義文件類型。FT_NML?表示這是一個(gè)普通文件,FT_DIR?表示是一個(gè)目錄。i_size?是文件大小,對(duì)于文件夾則是另外意思,后面將會(huì)講到。i_block的前6個(gè)整形表示文件的前6個(gè)扇區(qū)號(hào),第七個(gè)表示二級(jí)指針扇區(qū)(即它指向一個(gè)扇區(qū),這個(gè)扇區(qū)里面存放文件后續(xù)部分使用扇區(qū)號(hào)),由?512 / 4 = 128?扇區(qū),表示文件接下來(lái)使用的128個(gè)扇區(qū)。128 * 512 = 64K。i_block數(shù)組的最后一個(gè)表示三級(jí)指針,最大可以表示?(512 / 4) * (512 / 4) * 512 = 8MB字節(jié)。所以這個(gè)i_block數(shù)組最大可以表示?3k + 64K +?8M文件的大小,雖然比較小,但是對(duì)于我們的85M?分區(qū)來(lái)說(shuō)已經(jīng)足夠了。最重要的是,它比較簡(jiǎn)單。舉例來(lái)說(shuō)把,這個(gè)文件節(jié)點(diǎn)使用了如下扇區(qū)序列:?13, 7, 9, 16, 257, 57, 3, ....., 35, 33, ....., 55, ......., 128, ....., 81.
對(duì)于目錄(也是一種文件)來(lái)說(shuō),它以類似數(shù)組的形式組織:?{{file_name, file_inode}, {file_name, file_inode}, {file_name, file_inode}, },定義如下:
07/fs/include/fs.h
#define MAX_NAME_LEN 11
struct DIR_ENTRY {????????????/?存放在硬盤里面,在data區(qū)?*/
??? char de_name[MAX_NAME_LEN];
??? int de_inode;
};
操作系統(tǒng)中的所有文件都有一個(gè)獨(dú)一無(wú)二的節(jié)點(diǎn)編號(hào),如果有了這個(gè)節(jié)點(diǎn)號(hào),就可以找到對(duì)應(yīng)的文件。最開始的兩個(gè)文件永遠(yuǎn)是"."?和?"..",表示當(dāng)前目錄和上級(jí)目錄,如果我們切換到下級(jí)目錄,可以通過(guò)".."來(lái)回到上一級(jí)。"/"表示最上級(jí)目錄,它沒有父節(jié)點(diǎn)。
舉例來(lái)說(shuō),我們需要定位到?/usr/doc/fvwm/TODO?文件,首先我們找到"/"文件,然后搜索這個(gè)文件項(xiàng)下面的doc文件,因?yàn)?#34;/"是個(gè)目錄,所以先得到"/"目錄的節(jié)點(diǎn)編號(hào),然后搜索指向的節(jié)點(diǎn)表。然后再搜索到fvwm目錄,并且在這個(gè)目錄的節(jié)點(diǎn)表中搜索到"TODO"這個(gè)文件,并通過(guò)"TODO"的節(jié)點(diǎn)編號(hào)來(lái)定位節(jié)點(diǎn)這個(gè)文件的節(jié)點(diǎn)數(shù)據(jù)結(jié)構(gòu)。最后就可以訪問(wèn)i_block數(shù)組了,也就是可以訪問(wèn)這個(gè)文件了。怎么自己看的都昏菜了,s**t!
還有兩個(gè)問(wèn)題,一個(gè)是需要指定從哪里搜索節(jié)點(diǎn)號(hào),我們?cè)诖疟P中組織所有節(jié)點(diǎn)為數(shù)組,并由節(jié)點(diǎn)號(hào)來(lái)索引節(jié)點(diǎn)數(shù)組。另一個(gè)問(wèn)題是,"/"沒有父節(jié)點(diǎn),需要知道"/"存放在什么地方,這個(gè)也好辦,就放在節(jié)點(diǎn)數(shù)組的第一項(xiàng)好了。
文件名聲明成12字節(jié),這樣每個(gè)節(jié)點(diǎn)將占用16字節(jié)(另4字節(jié)是節(jié)點(diǎn)編號(hào)),這樣方便磁盤IO之后的一些操作。當(dāng)磁盤使用一段時(shí)間后,有的節(jié)點(diǎn)使用了,有的節(jié)點(diǎn)沒有使用,那怎么知道呢?一種方法是建立一個(gè)位圖表,每個(gè)位表示inode數(shù)組中的一項(xiàng)。
07/fs/include/fs.h
struct SUPER_BLOCK {
??? unsigned char sb_magic;????/*?分區(qū)類型?FST_FS?或?FST_SW *'
????
??? unsigned int sb_start;???? /* DPT 0x08:?起始扇區(qū)?*/
??? unsigned int sb_blocks;??? /* DPT 0x0c:?扇區(qū)數(shù)量?*/
??? unsigned int sb_dmap_blks;
??? unsigned int sb_imap_blks;
??? unsigned int sb_inode_blks;
};
?
這個(gè)超級(jí)塊的數(shù)據(jù)結(jié)構(gòu)用來(lái)管理各個(gè)分區(qū)。例如,下面是一個(gè)磁盤分區(qū):
?________________________________________________________
|??|?\\\\?| ?|?\\\\?|??|????? ?data????????? |?
?--------------------------------------------------------
每個(gè)分區(qū)的第一個(gè)扇區(qū)(藍(lán)色)是boot secotr,我不打算使用它,一個(gè)扇區(qū)大小。
第二個(gè)扇區(qū)(綠色)是超級(jí)塊(super block,以下簡(jiǎn)稱sb),一個(gè)扇區(qū)大小。
黑色是dmap,336個(gè)扇區(qū)大小。
紅色是imap,一個(gè)扇區(qū)大小。
灰色是inodes,將占有342個(gè)block,即342 * 8?個(gè)扇區(qū)大小。
為了管理這個(gè)85M分區(qū),我們額外花了?1.5M?的空間。
?
在verify_fs()函數(shù)中定義了超級(jí)塊(局部變量)sb,為了方便訪問(wèn)定義了一些宏,獲取相對(duì)整個(gè)硬盤的絕對(duì)地址(LBA):
07/fs/incude/fs.h
#define ABS_BOOT_BLK(sb)??? ??? ((sb).sb_start)
#define ABS_SUPER_BLK(sb)??? ??? ((ABS_BOOT_BLK(sb))+1)
#define ABS_DMAP_BLK(sb)??? ??? ((ABS_SUPER_BLK(sb))+1)
#define ABS_IMAP_BLK(sb)??? ??? ((ABS_DMAP_BLK(sb))+(sb).sb_dmap_blks)
#define ABS_INODE_BLK(sb)??? ??? ((ABS_IMAP_BLK(sb))+(sb).sb_imap_blks)
#define ABS_DATA_BLK(sb)??? ??? ((ABS_INODE_BLK(sb))+INODE_BLKS)
?
說(shuō)明:dmap(data map)存放的是扇區(qū)使用位圖
????? imap(inode map)存放inode使用位圖。
????? inodes存放節(jié)點(diǎn)表。
為了方便,一些位的操作函數(shù)如下:
07/fs/fs.c
void
setb(void *s, unsigned int i) {
??? unsigned char *v = s;
??? v += i>>3;????????????// i的單位由位轉(zhuǎn)換成字節(jié)
??? *v |= 1<<(7-(i%8));
}
void
clrb(void *s, unsigned int i) {
??? unsigned char *v = s;
??? v += i>>3;
??? *v &= ~(1<<(7-(i%8)));
}
int
testb(void *s, unsigned int i) {
??? unsigned char *v = s;
??? v += i>>3;
??? return (*v&(1<<(7-(i%8)))) !=0;
}
例如,設(shè)置緩沖區(qū)sect的1796位,可以使用setb(sect, 1796)
init.c在調(diào)用verify_DPT();創(chuàng)建分區(qū)表后,緊接著調(diào)用verify_fs();創(chuàng)建文件系統(tǒng):
07/fs/fs.c
void
verify_fs(void) {
??? unsigned int *q = (unsigned int *)(HD0_ADDR);
??? unsigned char sect[512] = {0};
??? struct SUPER_BLOCK sb;
??? unsigned int i = 0, j = 0, m = 0, n = 0;
??? /*?讀取超級(jí)塊?*/
??? sb.sb_start = q[0];
??? hd_rw(ABS_SUPER_BLK(sb), HD_READ, 1, &sb);
????//?很難想象這段代碼不越界,正好越界到sect上了?昏菜!
??? /*?判斷超級(jí)塊是否正確,不是就創(chuàng)建文件系統(tǒng)?*/
??? if (sb.sb_magic != FST_FS) {
??????? kprintf(KPL_DUMP, "Partition 1 does not have a valid file system\n");
??? ??? kprintf(KPL_DUMP, "Creating file system\t\t\t\t\t\t\t? ");
??? ??? sb.sb_magic = FST_FS;
??? ??? sb.sb_start = q[0];
??? ??? sb.sb_blocks = q[1];
??? ??? sb.sb_dmap_blks = (sb.sb_blocks+0xfff)>>12;
??? ??? sb.sb_imap_blks = INODE_BIT_BLKS;
??? ??? sb.sb_inode_blks = INODE_BLKS;
??? ??? hd_rw(ABS_SUPER_BLK(sb), HD_WRITE, 1, &sb);
?
????????// dmap位圖中,每個(gè)位表示1個(gè)扇區(qū),也就是說(shuō)dmap中每個(gè)扇區(qū)可以標(biāo)識(shí)512 * 8扇區(qū)。
????????//?另外,我們把inode位圖大小固定,即使用1個(gè)扇區(qū)。
??????? /*?初始化dmap位圖?*/
??? ??? n = ABS_DMAP_BLK(sb);
??? ??? j = sb.sb_dmap_blks+sb.sb_imap_blks+sb.sb_inode_blks+2;
??? ??? memset(sect, 0xff, sizeof sect/sizeof sect[0]);
??? ??? for (i=j/(512*8); i>0; --i) {
??? ??? ??? hd_rw(n++, HD_WRITE, 1, sect);
??? ??? ??? m += 4096;
??? ??? }
??? ??? m += 4096;
??? ??? for (i=j%(512*8); i<512*8; ++i) {
??? ??? ??? clrb(sect, i);
??? ??? ??? --m;
??? ??? }
??? ??? hd_rw(n++, HD_WRITE, 1, sect);
??? ??? memset(sect, 0, sizeof sect/sizeof sect[0]);
??? ??? for (i=sb.sb_imap_blks-(n-ABS_DMAP_BLK(sb)); i>0; --i)
??? ??? ??? hd_rw(n++, HD_WRITE, 1, sect);
??? ??? /*?初始化inode?位圖?*/
??? ??? for (i=sb.sb_imap_blks; i>0; --i)
??? ??? ??? hd_rw(ABS_IMAP_BLK(sb)+i-1, HD_WRITE, 1, sect);
??? ??? /*?初始化inode?數(shù)組?*/
??? ??? for (i=sb.sb_inode_blks; i>0; --i)
??? ??? ??? hd_rw(ABS_INODE_BLK(sb)+i-1, HD_WRITE, 1, sect);
??? ??? kprintf(KPL_DUMP, "[DONE]");
??? }
??? q += 2;
??? kprintf(KPL_DUMP, "0: Type: FST_FS ");
??? kprintf(KPL_DUMP, "start at: %d ", sb.sb_start);
??? kprintf(KPL_DUMP, "blocks: %d ", sb.sb_blocks);
??? kprintf(KPL_DUMP, "dmap: %d ", sb.sb_dmap_blks);
??? kprintf(KPL_DUMP, "imap: %d ", sb.sb_imap_blks);
??? kprintf(KPL_DUMP, "inodes: %d\n", sb.sb_inode_blks);
??? /*?初始化交互分區(qū)?*/
??? sb.sb_start = q[0];
??? hd_rw(ABS_SUPER_BLK(sb), HD_READ, 1, &sb);
??? if (sb.sb_magic != FST_SW) {
????????//?注意,和數(shù)據(jù)分區(qū)不同(每個(gè)塊占有1個(gè)扇區(qū)),
????????//?交互分區(qū)每個(gè)塊占有8個(gè)扇區(qū),即4096字節(jié),和內(nèi)存頁(yè)對(duì)齊
??????? kprintf(KPL_DUMP, "\nPartition 2 does not have a valid file system\n");
??? ??? kprintf(KPL_DUMP, "Creating file system\t\t\t\t\t\t\t? ");
??? ??? sb.sb_magic = FST_SW;
??? ??? sb.sb_start = q[0];
??? ??? sb.sb_blocks = q[1];
??? ??? sb.sb_dmap_blks = (sb.sb_blocks)>>15;??? /* 1 bits == 4K page */
??? ??? hd_rw(ABS_SUPER_BLK(sb), HD_WRITE, 1, &sb);
??? ??? kprintf(KPL_DUMP, "[DONE]");????
??? }
??? /*?初始化數(shù)據(jù)位圖?*/
??? n = ABS_DMAP_BLK(sb);
??? j = sb.sb_dmap_blks+2;
??? memset(sect, 0xff, sizeof sect/sizeof sect[0]);
??? for (i=j/(512*8); i>0; --i) {
??? ??? hd_rw(n++, HD_WRITE, 1, sect);
??? ??? m += 4096;
??? }
??? m += 4096;
??? for (i=j%(512*8); i<512*8; ++i) {
??? ??? clrb(sect, i);
??? ??? --m;
??? }
??? hd_rw(n++, HD_WRITE, 1, sect);
??? kprintf(KPL_DUMP, "1: Type: FST_SW ");
??? kprintf(KPL_DUMP, "start at: %d ", sb.sb_start);
??? kprintf(KPL_DUMP, "blocks: %d ", sb.sb_blocks);
??? kprintf(KPL_DUMP, "dmap: %d, presents %d 4k-page\n",
??? ??? ??? sb.sb_dmap_blks, sb.sb_blocks>>3);
}
最后修改Makefile,然后make clean && make dep && make
07/fs/Makefile
KERNEL_OBJS= load.o init.o isr.o timer.o libcc.o scr.o kb.o task.o kprintf.o hd.o exceptions.o fs.o
編譯,運(yùn)行。
?
root?根目錄
最后一件事情建立根目錄。"/"是所有文件的根目錄,所以我們一開始就必須設(shè)置好它。"/"文件永遠(yuǎn)使用inode 0,這樣skelix內(nèi)核才知道怎樣找到它。然后再讀取"/"文件的內(nèi)容,也就是DIR_ENTRY結(jié)構(gòu)數(shù)組。為了更方便的操作,我們先完成一些基礎(chǔ)函數(shù),用來(lái)操作blocks和inodes。
07/root/fs.c
static struct INODE iroot = {FT_DIR, 2*sizeof(struct DIR_ENTRY), {0,}};
unsigned int
alloc_blk(struct SUPER_BLOCK *sb) {
????//?根據(jù)dmap位圖查找空閑的扇區(qū),返回LBA地址(從1開始)
??? unsigned int i = 0, j = 0, n = 0, m = 0;
??? unsigned char sect[512] = {0};
??? n = ABS_DMAP_BLK(*sb);
??? for (; i<sb->sb_dmap_blks; ++i) {
??? ??? hd_rw(n, HD_READ, 1, sect);
??? ??? for (j=0; j<512*8; ++j) {
??? ??? ??? if (testb(sect, j)) {
??? ??? ??? ??? ++m;
??? ??? ??? } else {??? ??? ??? /* gotcha */
??? ??? ??? ??? setb(sect, j);
??? ??? ??? ??? if (m >= sb->sb_blocks)
??? ??? ??? ??? ??? return 0;
??? ??? ??? ??? else {
??? ??? ??? ??? ??? hd_rw(n, HD_WRITE, 1, sect);
??? ??? ??? ??? ??? return ABS_BOOT_BLK(*sb) + m;
??? ??? ??? ??? }
??? ??? ??? }
??? ??? }
??? ??? ++n;
??? }
??? return 0;
}
void
free_blk(struct SUPER_BLOCK *sb, unsigned int n) {
????//?釋放一個(gè)扇區(qū):設(shè)置dmap位圖中對(duì)應(yīng)的位即可
??? unsigned char sect[512] = {0};
??? unsigned int t = (n-ABS_BOOT_BLK(*sb))/(512*8)+ABS_DMAP_BLK(*sb);
??? hd_rw(t, HD_READ, 1, sect);
??? clrb(sect, (n-ABS_BOOT_BLK(*sb))%(512*8));
??? hd_rw(t, HD_WRITE, 1, sect);
}
static int
alloc_inode(struct SUPER_BLOCK *sb) {
????//?在imap表中中找一個(gè)空閑的項(xiàng)
??? unsigned char sect[512] = {0};
??? int i = 0;
??? hd_rw(ABS_IMAP_BLK(*sb), HD_READ, 1, sect);
??? for (; i<512; ++i) {
??? ??? if (! testb(sect, i)) {
??? ??? ??? setb(sect, i);
??? ??? ??? hd_rw(ABS_IMAP_BLK(*sb), HD_WRITE, 1, sect);
??? ??? ??? break;
??? ??? }
??? }
??? return (i==512)?-1:i;
}
static void
free_inode(struct SUPER_BLOCK *sb, int n) {
????//?釋放inode項(xiàng)
??? unsigned char sect[512] = {0};
??? hd_rw(ABS_IMAP_BLK(*sb), HD_READ, 1, sect);
??? clrb(sect, n);
??? hd_rw(ABS_IMAP_BLK(*sb), HD_WRITE, 1, sect);
}
//?上面4個(gè)函數(shù)就是針對(duì)dmap和imap的操作(申請(qǐng),釋放)
static struct INODE *
iget(struct SUPER_BLOCK *sb, struct INODE *inode, int n) {
??? unsigned char sect[512] = {0};
??? int i = n / INODES_PER_BLK;
??? int j = n % INODES_PER_BLK;
??? hd_rw(ABS_INODE_BLK(*sb)+i, HD_READ, 1, sect);
??? memcpy(inode, sect+j*sizeof(struct INODE), sizeof(struct INODE));
??? return inode;
}
static void
iput(struct SUPER_BLOCK *sb, struct INODE *inode, int n) {
??? unsigned char sect[512] = {0};
??? int i = n/INODES_PER_BLK;
??? int j = n%INODES_PER_BLK;
??? hd_rw(ABS_INODE_BLK(*sb)+i, HD_READ, 1, sect);
??? memcpy(sect+j*sizeof(struct INODE), inode, sizeof(struct INODE));
??? hd_rw(ABS_INODE_BLK(*sb)+i, HD_WRITE, 1, sect);
}
//?上面兩個(gè)函數(shù)分別完成讀/寫磁盤指定下標(biāo)號(hào)對(duì)應(yīng)的inode節(jié)點(diǎn)到內(nèi)存中。
//?需要注意的是,這些函數(shù)對(duì)競(jìng)態(tài)條件做處理,因?yàn)閟kelix僅內(nèi)核讀寫硬盤。
//?本文中暫時(shí)沒有用戶態(tài)的多任務(wù)。
主流程如下:
07/root/fs.c
static void
check_root(void) {
??? struct SUPER_BLOCK sb;
??? unsigned char sect[512] = {0};
??? struct DIR_ENTRY *de = NULL;
??? sb.sb_start = *(unsigned int *)(HD0_ADDR);
??? hd_rw(ABS_SUPER_BLK(sb), HD_READ, 1, sect);
??? memcpy(&sb, sect, sizeof(struct SUPER_BLOCK));
??? hd_rw(ABS_IMAP_BLK(sb), HD_READ, 1, sect);
????//?加載imap扇區(qū),判斷"/"目錄有沒有創(chuàng)建
??? if (! testb(sect, 0)) {????????????// "/"目錄必須使用inode 0,否則halt
??????? kprintf(KPL_DUMP, "/ has not been created, creating....\t\t\t\t\t? ");
??? ??? if (alloc_inode(&sb) != 0) {?? //?分配節(jié)點(diǎn)號(hào):即imap位圖中的一位
??????????? kprintf(KPL_PANIC, "\n/ must be inode 0!!!\n");
??? ??? ??? halt();
??? ??? }
??????? iroot.i_block[0] = alloc_blk(&sb);????//?節(jié)點(diǎn)分配一個(gè)塊(一個(gè)扇區(qū))
??????? iput(&sb, &iroot, 0);???????????????? //?寫入節(jié)點(diǎn)
?
??????? de = (struct DIR_ENTRY *)sect;
??? ??? strcpy(de->de_name, ".");
??? ??? de->de_inode = 0;???????????????????? //?節(jié)點(diǎn)號(hào)為0
??????? ++de;
??? ??? strcpy(de->de_name, "..");
??? ??? de->de_inode = -1;????????????????????//?節(jié)點(diǎn)號(hào)為-1,這樣我們就知道是最上層目錄了
??????? hd_rw(iroot.i_block[0], HD_WRITE, 1, sect);????//?寫入"."?和?".."文件夾
??????? kprintf(KPL_DUMP, "[DONE]");
??? }
??? iget(&sb, &iroot, 0);
??? hd_rw(iroot.i_block[0], HD_READ, 1, sect);
??? de = (struct DIR_ENTRY *)sect;
??? if ((strcmp(de[0].de_name, ".")) || (de[0].de_inode) ||
??? ??? (strcmp(de[1].de_name, "..")) || (de[1].de_inode) != -1) {
??? ??? kprintf(KPL_PANIC, "File system is corrupted!!!\n");
??? ??? halt();
??? }
}
?
//?再來(lái)一個(gè)函數(shù)打印文件的相關(guān)信息
static void
stat(struct INODE *inode) {
??? unsigned int i = 0;
??? char sect[512] = {0};
??? struct DIR_ENTRY *de;
??? kprintf(KPL_DUMP, "======== stat / ========\n");
??? switch (inode->i_mode) {
??? case FT_NML:
??? ??? kprintf(KPL_DUMP, "File, ");
??? ??? break;
??? case FT_DIR:
??? ??? kprintf(KPL_DUMP, "Dir,? ");
??? ??? break;
??? default:
??? ??? kprintf(KPL_PANIC, "UNKNOWN FILE TYPE!!");
??? ??? halt();
??? }
??? kprintf(KPL_DUMP, "Size: %d, ", inode->i_size);
??? kprintf(KPL_DUMP, "Blocks: ");
??? for (; i<8; ++i)????????//?打印inode標(biāo)識(shí)使用的扇區(qū)
??????? kprintf(KPL_DUMP, "%d, ", inode->i_block[i]);
??? hd_rw(inode->i_block[0], HD_READ, 1, sect);
??? switch (inode->i_mode) {
??? case FT_DIR:
??? ??? kprintf(KPL_DUMP, "\nName\tINode\n");
??? ??? de = (struct DIR_ENTRY *)sect;????//?打印子目錄(只一個(gè)扇區(qū))
??????? for (i=0; i<inode->i_size/sizeof(struct DIR_ENTRY); ++i) {
??? ??? ??? kprintf(KPL_DUMP, "%s\t%x\n", de[i].de_name, de[i].de_inode);
??? ??? }
??????? break;
??? default:
??? ??? break;
??? }
}
現(xiàn)在,我們把上面的函數(shù)整理到程序中
void
verify_dir(void) {
??? unsigned char sect[512] = {0};
??? unsigned int *q = (unsigned int *)(HD0_ADDR);
??? struct INODE inode;
??? struct SUPER_BLOCK sb;
??? sb.sb_start = q[0];
??? hd_rw(ABS_SUPER_BLK(sb), HD_READ, 1, sect);
??? check_root();
??? memcpy(&sb, sect, sizeof(struct SUPER_BLOCK));
??? stat(iget(&sb, &inode, 0));
}
07/root/init.c
void?
init(void) {
????……
??? kprintf(KPL_DUMP, "Verifing disk partition table....\n");
??? verify_DPT();
??? kprintf(KPL_DUMP, "Verifing file systes....\n");
??? verify_fs();
??? kprintf(KPL_DUMP, "Checking / directory....\n");
????verify_dir();
????……
}
不需要再編輯Makefile了,直接make && run好了。
總結(jié)
- 上一篇: java实践源码--哈弗曼树
- 下一篇: 自己动手写mvc