有不少朋友在論壇或是發(fā)郵件給我,問關(guān)于FAT12的問題。對(duì)一些開發(fā)操作系統(tǒng)的朋友來說,軟盤或軟盤鏡像是不可或缺的系統(tǒng)載體,我們的操作系統(tǒng)要在存儲(chǔ)介質(zhì)上操作就幾乎不可能離開文件系統(tǒng),如果想了解一下FAT12、FAT16、FAT32之類的Windows 用過的文件系統(tǒng),可以去:http://www.yesky.com/20030313/1656880.shtml 如果想全面地了解硬盤結(jié)構(gòu),文件系統(tǒng)原理,下面的一系列文章是很不錯(cuò)的材料: http://www.sjhf.net/Article/sjhfdoc/200404/1.html http://www.sjhf.net/Article/sjhfdoc/200404/2.html http://www.sjhf.net/Article/sjhfdoc/200404/3.html http://www.sjhf.net/Article/sjhfdoc/200404/4.html http://www.sjhf.net/Article/Index.html
本文主要介紹以3.5英寸的1.44M標(biāo)準(zhǔn)格式化的FAT12文件系統(tǒng)的軟盤為介紹對(duì)象。這里強(qiáng)調(diào)那么多是因?yàn)椋?.44M的軟盤格式化可以不是1.44M,可以大于也可以小于;格式化的文件系統(tǒng)也可以不是FAT12。
為什么會(huì)出現(xiàn)正常的1.44M軟盤格式化后可大可小的情況呢?從軟盤及軟盤驅(qū)動(dòng)器原理出發(fā),軟盤的尋址方式(可以認(rèn)為是讀取數(shù)據(jù)的方式)是:CHS,C = Cylinder(柱面),H = Header(磁頭),S = Sector(扇區(qū))。標(biāo)準(zhǔn)地格式化后,磁盤將被格式化為 每面80磁道(80個(gè)同心圓,柱面),每個(gè)磁道有18個(gè)扇區(qū),每個(gè)扇區(qū)是 512字節(jié),那么高密3.5英寸軟盤的容量為:2×80×18×512 = 1474560 Byte = 1440 KB = 1.44 MB。然而,軟盤可以不格式為80磁道,每個(gè)磁道也可以不是18扇區(qū),這是題外話,如果您有興趣,可以用古老的HDCopy試試。
文件存儲(chǔ)到磁盤上時(shí)至少要占用1個(gè)扇區(qū),即使這個(gè)文件只有1個(gè)字節(jié),如果文件有513字節(jié),那就得占用2個(gè)扇區(qū),下一個(gè)文件就不能用這只使用了一個(gè)字節(jié)的扇區(qū)。即軟盤以扇區(qū)為單位存儲(chǔ)文件。現(xiàn)在用下面的假設(shè)來說明本文的目的: 假設(shè)只有18個(gè)扇區(qū)的磁盤,以 0 - 17 編址,如果一個(gè)文件保存在 1 - 6扇區(qū),另一文件保存在 7 - 16扇區(qū),如果我們對(duì)第一個(gè)文件增加了內(nèi)容,又需要一個(gè)扇區(qū)來保存它,但由于文件連續(xù)存儲(chǔ), 7號(hào)扇區(qū)是第二個(gè)文件的,我們當(dāng)然不能用它,只有最后留有一個(gè)扇區(qū)可用,我們會(huì)不會(huì)把第二個(gè)文件先挪到8-17扇區(qū)以騰出一個(gè)扇區(qū)來給第一個(gè)文件使用呢?當(dāng)只有少數(shù)兩個(gè)文件的時(shí)候可以,但有很多文件的時(shí)候會(huì)變得麻煩起來。如果我們用一個(gè)表來表示有一個(gè)文件占用了 1-6扇區(qū) 和 17扇區(qū),那事情就簡(jiǎn)單了——我們不必為文件不連續(xù)而煩惱。這個(gè)表就叫它:文件分配表(File Allocation Table)。那怎樣才能知道這個(gè)文件存儲(chǔ)的文件名和文件存放的起始扇區(qū)?再建一個(gè)表,用于存放文件名、起始扇區(qū)、文件創(chuàng)建時(shí)間、文件實(shí)際大小等等資料,這個(gè)表叫:文件目錄表(File Directory Table)。將這兩個(gè)表放在磁盤指定的位置,以便操作系統(tǒng)使用,磁盤的其它扇區(qū)全都用來存放文件的實(shí)際內(nèi)容,這就構(gòu)成了有文件系統(tǒng)的磁盤。
磁盤上,0面0磁道第1扇區(qū)用于存放引導(dǎo)程序,如果這512字節(jié)最后兩個(gè)字節(jié)分別是0x55,0xAA(一個(gè)字是0xAA55),稱為可引導(dǎo)標(biāo)志,BIOS會(huì)將這512字節(jié)讀取出來執(zhí)行,操作系統(tǒng)便是利用這里來實(shí)現(xiàn)引導(dǎo)的。標(biāo)識(shí)軟盤是不是FAT12并不是沒有根據(jù)的,在這512字節(jié)中,還有一個(gè)設(shè)備頭用于標(biāo)識(shí)這個(gè)軟盤(設(shè)備),例子如下:
;=========================================================================== ; 程序執(zhí)行的第一條指令必須是跳轉(zhuǎn)(如果你想使用FAT12這類文件系統(tǒng)的磁盤) ; 必須占用3字節(jié) ;=========================================================================== jmp SHORT main ; 2 bits,跳轉(zhuǎn)到主程序執(zhí)行 nop ; 1 bit ;=========================================================================== ; FAT12 文件系統(tǒng)頭,從NYAOS 借過來的,可以參考相關(guān)的文檔以獲得更多細(xì)節(jié) ; 這個(gè)塊會(huì)讓 Winimage 認(rèn)出編譯后的二進(jìn)制文件為有效的引導(dǎo)文件 ; 如果不使用這個(gè)塊,Winimage將不會(huì)將其作為引導(dǎo)程序處理 ; 但我們可以借助其它方法和工具處理,比如DEBUG ;=========================================================================== bsOEM db "ExOS0.02" ; OEM String,任意你喜歡的8字節(jié)ASCII碼 bsSectSize dw 512 ; Bytes per sector bsClustSize db 1 ; Sectors per cluster bsRessect dw 1 ; # of reserved sectors bsFatCnt db 2 ; # of fat copies bsRootSize dw 224 ; size of root directory bsTotalSect dw 2880 ; total # of sectors if < 32 meg bsMedia db 0xF0 ; Media Descriptor bsFatSize dw 9 ; Size of each FAT bsTrackSect dw 18 ; Sectors per track bsHeadCnt dw 2 ; number of read-write heads bsHidenSect dd 0 ; number of hidden sectors bsHugeSect dd 0 ; if bsTotalSect is 0 this value is ; the number of sectors bsBootDrv db 0 ; holds drive that the bs came from bsReserv db 0 ; not used for anything bsBootSign db 29h ; boot signature 29h bsVolID dd 0 ; Disk volume ID also used for temp ; sector # / # sectors to load bsVoLabel db "NO NAME " ; Volume Label bsFSType db "FAT12 " ; File System type <- FAT 12文件系統(tǒng) ;=========================================================================== ; Main start here ;=========================================================================== main: #至于如何引導(dǎo)計(jì)算機(jī),可以參考我Blog里的more.asp?name=xemean&id=2
0面0道第2扇區(qū)到第10扇區(qū)的9個(gè)扇區(qū)是FAT表的存放位置,為了預(yù)防,0面0道的第11扇區(qū)到1面0道第1扇區(qū)的9個(gè)扇區(qū)是第2個(gè)FAT表的存放位置,這第2個(gè)FAT是備用的,當(dāng)?shù)谝粋€(gè)FAT出了問題里,可以用第2個(gè)FAT。1面0道的第2扇區(qū)起到1面0道的第15扇區(qū)(共14個(gè)扇區(qū))用于存放 FDT。FDT沒有備份,所以沒有第二個(gè)FDT。這里要注意的是,磁盤為了讀寫的速度,0面0道的18個(gè)扇區(qū)接下來的是 1面0道的扇區(qū),而不是0面1道,因?yàn)?面0道跟1面0道同在一個(gè)柱面上(同心圓),只是用的磁頭不同。
FAT12 中,每個(gè)文件分配表項(xiàng)只占12位(bit),即1.5字節(jié)(byte),每個(gè)表項(xiàng)代表一個(gè)扇區(qū),在這里,磁盤只有扇區(qū)的概念,磁盤里所有扇區(qū)都被類似于上一段提到的磁盤讀寫方式線性地編址(LBA),不再有CHS。這里還要提一提簇的概念:DOS會(huì)把2個(gè)扇區(qū)作為一簇,那么文件就要以簇為單位讀寫。簇的大小通常根據(jù)磁盤的大小設(shè)定,以盡可能少浪費(fèi)磁盤空間為本。
FAT12每個(gè)表項(xiàng)的值指出文件存放的下一個(gè)扇區(qū)號(hào),同時(shí)也是表項(xiàng)入口。比如如果文件的存放的第一個(gè)扇區(qū)是002,那系統(tǒng)首先找FAT的002,在002處得到一個(gè)值003,表示文件下一個(gè)扇區(qū)是003號(hào),再接著003表項(xiàng)找,得到006...,表項(xiàng)的值含義如下: 000 - 此簇未用;FF8 - FFF 該簇為文件的最后一簇;FF0 - FF7,此簇為壞,不可用;其它值表示文件下一簇的簇號(hào)。 下面的圖來說明FAT的基本原理: 表項(xiàng)編號(hào) 值(16位) 備注 000 | FF0 | <- 000 項(xiàng) 001項(xiàng)為表頭,1字節(jié) 0xF0表示存儲(chǔ)介質(zhì) 001 | FFF | <- 2、3字節(jié)為 0xFFFF ,固定值,F(xiàn)AT標(biāo)志 002 | 003 | <- 文件下一簇為003 003 | 005 | <- 下一簇:005 004 | FF7 | <- 壞簇,不可用 005 | 011 | <- 下一簇:011 ........................................ 011 | FF8 | <- 該文件結(jié)束 012 | 000 | <- 可用簇 ....................................... 根據(jù)上表,我們可以知道,一個(gè)文件占用了 002,003,005,011 這4個(gè)簇。 簇號(hào) + 31 = 邏輯扇區(qū)號(hào) //// 31 = 保留扇區(qū)數(shù) + 隱藏扇區(qū)數(shù) + FAT數(shù)×每個(gè)FAT所占扇區(qū)數(shù) + FDT所占扇區(qū)數(shù) - 2 = 1 + 0 + 2*18 + 14 -2 LBA = 邏輯扇區(qū)號(hào) - 1 扇區(qū) = (LBA MOD 每道扇區(qū)數(shù)) + 1 磁道 = (LBA / 每道扇區(qū)數(shù)) / 磁頭數(shù) 磁頭 = (LBA / 每道扇區(qū)數(shù)) MOD 磁頭數(shù)
根據(jù)上面的公式,得到以下計(jì)算值: 002: S = ( 32 MOD 18 ) + 1 = 15 002: C = ( 32 / 18 ) / 2 = 0 002: H = ( 32 / 18 ) MOD 2 = 1 ----------------- 011: S = ( ( 11+31-1) MOD 18) + 1 = 6 011: C = ( ( 11+31-1) / 18) / 2 = 1 011: H = ( ( 11+31-1) / 18) MOD 2 = 0
就此,我們已經(jīng)可心根據(jù)簇號(hào)得到物理CHS了,那怎樣才能得到一個(gè)文件的關(guān)系首簇號(hào)呢?前面我們提到了FDT。下面說說FDT的結(jié)構(gòu): 每個(gè)FDT項(xiàng)占32字節(jié),分配如下: ======================= 0 - 7 : 8字節(jié),文件名 8 - 10: 3字節(jié),文件擴(kuò)展名 11 :1字節(jié),文件的屬性 12 - 15:4字節(jié),保留 16 - 21:6字節(jié),保留 22 - 23:2字節(jié),文件最后修改時(shí)間(時(shí)分秒,5:6:5) 24 - 25:2字節(jié),文件最后修改日期(年月日,7:4:5,年取0-119對(duì)應(yīng) 1980 - 2099) 26 - 27:2字節(jié),文件首簇號(hào),我們可以根據(jù)這個(gè)值在FAT中找到文件的存儲(chǔ)位置 28 - 31:4字節(jié),文件的長(zhǎng)度,以字節(jié)為單位 =========================== 0 - 7 文件名含義:0 - 目錄項(xiàng)為空,可用;E5 - 此文件已經(jīng)被刪除 7 - 10 :文件名和擴(kuò)展名為8.3格式,如果不夠,必需用空格填充,即文件名如果只有6個(gè)字節(jié),那剩下的2個(gè)字節(jié)必須以空格填充。文件名和擴(kuò)展名都是大寫。 11屬性字節(jié)含義:00 - 普通文件;01 - 只讀;02 - 隱藏;04 - 系統(tǒng)文件;10(1x) - 該文件是目錄。
就此,本文已經(jīng)基本介紹完了軟盤的結(jié)構(gòu),下面介紹如何讀一個(gè)文件:
- 給出一個(gè)文件名,比如“KERNEL.SYS”
- 將文件名擴(kuò)展為“KERNEL SYS”,即去掉點(diǎn)并為文件名和擴(kuò)展名補(bǔ)充空格
- 讀FDT到內(nèi)存中(用BIOS INT 13)
- 在FDT中查找到符合的文件名
- 可選,判斷在FDT中找到的是否是目錄
- 在符合的FDT中取出文件首簇號(hào)
- 讀入FAT,可以選擇讀入兩個(gè)FAT表,以檢查是否有效
- 將簇號(hào)轉(zhuǎn)換為CHS,將扇區(qū)讀入內(nèi)存
- 根據(jù)簇號(hào)在FAT中查找下一簇,并判斷是否是文件最后一簇
- 如果是文件最后一簇,則文件讀取完畢;如果不是,則轉(zhuǎn)第8步
如果在引導(dǎo)程序中讀指定的內(nèi)核文件,則可以省略1、2步,直接給出內(nèi)核文件名即可。
如果給出的文件是帶目錄的,那這里還有必要介紹一下:實(shí)際上,目錄也是一個(gè)文件,只不過這個(gè)文件是一個(gè)FDT,F(xiàn)DT指出該目錄下其它文件或目錄。因此,給出如下路徑:/EXOS/KERNEL.BIN ,則先是在根目錄中,將“EXOS ”這個(gè)“文件”讀出來,然后在讀出的FDT中找“KERNEL BIN”。
好了,本文至此告一段落,但愿能給大家一定的幫助。近期正在寫一個(gè)ERP,時(shí)間倉(cāng)促,本文寫得也有不盡如人意的地方,更沒有代碼實(shí)例。還請(qǐng)大家見諒。
X. 2005-08-26 |