有不少朋友在論壇或是發郵件給我,問關于FAT12的問題。對一些開發操作系統的朋友來說,軟盤或軟盤鏡像是不可或缺的系統載體,我們的操作系統要在存儲介質上操作就幾乎不可能離開文件系統,如果想了解一下FAT12、FAT16、FAT32之類的Windows 用過的文件系統,可以去:http://www.yesky.com/20030313/1656880.shtml 如果想全面地了解硬盤結構,文件系統原理,下面的一系列文章是很不錯的材料: 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標準格式化的FAT12文件系統的軟盤為介紹對象。這里強調那么多是因為:1.44M的軟盤格式化可以不是1.44M,可以大于也可以小于;格式化的文件系統也可以不是FAT12。
為什么會出現正常的1.44M軟盤格式化后可大可小的情況呢?從軟盤及軟盤驅動器原理出發,軟盤的尋址方式(可以認為是讀取數據的方式)是:CHS,C = Cylinder(柱面),H = Header(磁頭),S = Sector(扇區)。標準地格式化后,磁盤將被格式化為 每面80磁道(80個同心圓,柱面),每個磁道有18個扇區,每個扇區是 512字節,那么高密3.5英寸軟盤的容量為:2×80×18×512 = 1474560 Byte = 1440 KB = 1.44 MB。然而,軟盤可以不格式為80磁道,每個磁道也可以不是18扇區,這是題外話,如果您有興趣,可以用古老的HDCopy試試。
文件存儲到磁盤上時至少要占用1個扇區,即使這個文件只有1個字節,如果文件有513字節,那就得占用2個扇區,下一個文件就不能用這只使用了一個字節的扇區。即軟盤以扇區為單位存儲文件。現在用下面的假設來說明本文的目的: 假設只有18個扇區的磁盤,以 0 - 17 編址,如果一個文件保存在 1 - 6扇區,另一文件保存在 7 - 16扇區,如果我們對第一個文件增加了內容,又需要一個扇區來保存它,但由于文件連續存儲, 7號扇區是第二個文件的,我們當然不能用它,只有最后留有一個扇區可用,我們會不會把第二個文件先挪到8-17扇區以騰出一個扇區來給第一個文件使用呢?當只有少數兩個文件的時候可以,但有很多文件的時候會變得麻煩起來。如果我們用一個表來表示有一個文件占用了 1-6扇區 和 17扇區,那事情就簡單了——我們不必為文件不連續而煩惱。這個表就叫它:文件分配表(File Allocation Table)。那怎樣才能知道這個文件存儲的文件名和文件存放的起始扇區?再建一個表,用于存放文件名、起始扇區、文件創建時間、文件實際大小等等資料,這個表叫:文件目錄表(File Directory Table)。將這兩個表放在磁盤指定的位置,以便操作系統使用,磁盤的其它扇區全都用來存放文件的實際內容,這就構成了有文件系統的磁盤。
磁盤上,0面0磁道第1扇區用于存放引導程序,如果這512字節最后兩個字節分別是0x55,0xAA(一個字是0xAA55),稱為可引導標志,BIOS會將這512字節讀取出來執行,操作系統便是利用這里來實現引導的。標識軟盤是不是FAT12并不是沒有根據的,在這512字節中,還有一個設備頭用于標識這個軟盤(設備),例子如下:
;=========================================================================== ; 程序執行的第一條指令必須是跳轉(如果你想使用FAT12這類文件系統的磁盤) ; 必須占用3字節 ;=========================================================================== jmp SHORT main ; 2 bits,跳轉到主程序執行 nop ; 1 bit ;=========================================================================== ; FAT12 文件系統頭,從NYAOS 借過來的,可以參考相關的文檔以獲得更多細節 ; 這個塊會讓 Winimage 認出編譯后的二進制文件為有效的引導文件 ; 如果不使用這個塊,Winimage將不會將其作為引導程序處理 ; 但我們可以借助其它方法和工具處理,比如DEBUG ;=========================================================================== bsOEM db "ExOS0.02" ; OEM String,任意你喜歡的8字節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文件系統 ;=========================================================================== ; Main start here ;=========================================================================== main: #至于如何引導計算機,可以參考我Blog里的more.asp?name=xemean&id=2
0面0道第2扇區到第10扇區的9個扇區是FAT表的存放位置,為了預防,0面0道的第11扇區到1面0道第1扇區的9個扇區是第2個FAT表的存放位置,這第2個FAT是備用的,當第一個FAT出了問題里,可以用第2個FAT。1面0道的第2扇區起到1面0道的第15扇區(共14個扇區)用于存放 FDT。FDT沒有備份,所以沒有第二個FDT。這里要注意的是,磁盤為了讀寫的速度,0面0道的18個扇區接下來的是 1面0道的扇區,而不是0面1道,因為0面0道跟1面0道同在一個柱面上(同心圓),只是用的磁頭不同。
FAT12 中,每個文件分配表項只占12位(bit),即1.5字節(byte),每個表項代表一個扇區,在這里,磁盤只有扇區的概念,磁盤里所有扇區都被類似于上一段提到的磁盤讀寫方式線性地編址(LBA),不再有CHS。這里還要提一提簇的概念:DOS會把2個扇區作為一簇,那么文件就要以簇為單位讀寫。簇的大小通常根據磁盤的大小設定,以盡可能少浪費磁盤空間為本。
FAT12每個表項的值指出文件存放的下一個扇區號,同時也是表項入口。比如如果文件的存放的第一個扇區是002,那系統首先找FAT的002,在002處得到一個值003,表示文件下一個扇區是003號,再接著003表項找,得到006...,表項的值含義如下: 000 - 此簇未用;FF8 - FFF 該簇為文件的最后一簇;FF0 - FF7,此簇為壞,不可用;其它值表示文件下一簇的簇號。 下面的圖來說明FAT的基本原理: 表項編號 值(16位) 備注 000 | FF0 | <- 000 項 001項為表頭,1字節 0xF0表示存儲介質 001 | FFF | <- 2、3字節為 0xFFFF ,固定值,FAT標志 002 | 003 | <- 文件下一簇為003 003 | 005 | <- 下一簇:005 004 | FF7 | <- 壞簇,不可用 005 | 011 | <- 下一簇:011 ........................................ 011 | FF8 | <- 該文件結束 012 | 000 | <- 可用簇 ....................................... 根據上表,我們可以知道,一個文件占用了 002,003,005,011 這4個簇。 簇號 + 31 = 邏輯扇區號 //// 31 = 保留扇區數 + 隱藏扇區數 + FAT數×每個FAT所占扇區數 + FDT所占扇區數 - 2 = 1 + 0 + 2*18 + 14 -2 LBA = 邏輯扇區號 - 1 扇區 = (LBA MOD 每道扇區數) + 1 磁道 = (LBA / 每道扇區數) / 磁頭數 磁頭 = (LBA / 每道扇區數) MOD 磁頭數
根據上面的公式,得到以下計算值: 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
就此,我們已經可心根據簇號得到物理CHS了,那怎樣才能得到一個文件的關系首簇號呢?前面我們提到了FDT。下面說說FDT的結構: 每個FDT項占32字節,分配如下: ======================= 0 - 7 : 8字節,文件名 8 - 10: 3字節,文件擴展名 11 :1字節,文件的屬性 12 - 15:4字節,保留 16 - 21:6字節,保留 22 - 23:2字節,文件最后修改時間(時分秒,5:6:5) 24 - 25:2字節,文件最后修改日期(年月日,7:4:5,年取0-119對應 1980 - 2099) 26 - 27:2字節,文件首簇號,我們可以根據這個值在FAT中找到文件的存儲位置 28 - 31:4字節,文件的長度,以字節為單位 =========================== 0 - 7 文件名含義:0 - 目錄項為空,可用;E5 - 此文件已經被刪除 7 - 10 :文件名和擴展名為8.3格式,如果不夠,必需用空格填充,即文件名如果只有6個字節,那剩下的2個字節必須以空格填充。文件名和擴展名都是大寫。 11屬性字節含義:00 - 普通文件;01 - 只讀;02 - 隱藏;04 - 系統文件;10(1x) - 該文件是目錄。
就此,本文已經基本介紹完了軟盤的結構,下面介紹如何讀一個文件:
- 給出一個文件名,比如“KERNEL.SYS”
- 將文件名擴展為“KERNEL SYS”,即去掉點并為文件名和擴展名補充空格
- 讀FDT到內存中(用BIOS INT 13)
- 在FDT中查找到符合的文件名
- 可選,判斷在FDT中找到的是否是目錄
- 在符合的FDT中取出文件首簇號
- 讀入FAT,可以選擇讀入兩個FAT表,以檢查是否有效
- 將簇號轉換為CHS,將扇區讀入內存
- 根據簇號在FAT中查找下一簇,并判斷是否是文件最后一簇
- 如果是文件最后一簇,則文件讀取完畢;如果不是,則轉第8步
如果在引導程序中讀指定的內核文件,則可以省略1、2步,直接給出內核文件名即可。
如果給出的文件是帶目錄的,那這里還有必要介紹一下:實際上,目錄也是一個文件,只不過這個文件是一個FDT,FDT指出該目錄下其它文件或目錄。因此,給出如下路徑:/EXOS/KERNEL.BIN ,則先是在根目錄中,將“EXOS ”這個“文件”讀出來,然后在讀出的FDT中找“KERNEL BIN”。
好了,本文至此告一段落,但愿能給大家一定的幫助。近期正在寫一個ERP,時間倉促,本文寫得也有不盡如人意的地方,更沒有代碼實例。還請大家見諒。
X. 2005-08-26 |