Ch 33 硬碟 (2) FAT

在前一章,小木偶介紹了實體硬碟的相關知識,提到了分割區、分割表、擴充分割表、邏輯硬碟、實體硬碟等主題。在這一章堙A小木偶將要介紹實體硬碟分割成邏輯硬碟後,DOS/Windows 9.x/Me 這些作業系統是如何去使用、分配、管理邏輯硬碟,有關這個主題本來有許多可談,例如光光是檔案系統,就有 DOS 的 FAT16、Windows 95 的 VFAT、Windows 98/98SE/Me 的 FAT32,Windows NT/2k/XP 的 NTFS、OS/2 的 HPFS 以及 Linux 的 Ext2/3 等等。但是小木偶除了 FAT16、FAT32 和 VFAT 比較熟悉之外,對於其他的檔案系統是既陌生又無資料參考,只能望硬碟興嘆。而 FAT16 檔案系統在第 18 章已經提過,因此本章主要對 VFAT 以及 FAT32 做一番探討,並整理 FAT16 與 FAT32 之差異。


FAT16 與 FAT32 的結構

FAT16 和 FAT32 分割區的結構類似。每個 FAT16 分割區依序可分為四部份:保留區域 ( reserved region )、FAT 區域 ( FAT region )、根目錄區域 ( root directory region )以及資料區域 ( file and directory data region )四部份;對 FAT32 分割區而言,沒有根目錄區域,其餘均相同,FAT32 分割區把根目錄視為一個目錄,包含在資料區域內。底下就先探討啟動區域。


保留區域

FAT16 分割的保留區域僅僅含一個磁區,就是啟動磁區 ( boot sector );FAT32 分割的保留磁區中除了第一個磁區是啟動磁區外,還包含了檔案系統資訊磁區和啟動磁區的備份。

啟動磁區以及啟動磁區內的 BPB ( BIOS parameter block )

FAT16 和 FAT32 的啟動磁區 ( 啟動磁區在實體硬碟的位置,請參閱前一章,對邏輯磁碟而言,啟動磁區是編號 0 的邏輯磁區 ) 堻ㄕ BPB 資料,BPB 堶惘s有這個分割堛滬垠n資訊,而 FAT16 分割的 BPB 內容與 FAT32 分割大同小異,比較如下表:( 亦可參閱第 18 章 )

名稱 位址範圍 大小
位元組
所代表意義
FAT16 FAT32 FAT16 FAT32
BS_jmpBoot 00-02 3 跳躍指令 同左
BS_OEMName 03-0A 8 廠商軟體名
BPB_BytsPerSec 0B-0C 2 每一磁區的位元組數 同左
BPB_SecPerClu 0D 1 每一磁叢所佔磁區數
BPB_RsvdSecCnt 0E-0F 2 保留磁區數
BPB_NumFATs 101 FAT 份數
BPB_RootEntCnt 11-12 2 根目錄所含 FDB 數 FAT32 不使用此欄
BPB_TotSec16 13-14 2 邏輯磁區總數,若為零表示此硬碟大於 32MB,而在位址 20-23 記錄真正的邏輯磁區總數
BPB_Media 151 磁碟種類 同左
BPB_FATSz16 16-17 2 每份 FAT 所佔磁區數 FAT32 不使用此欄位故此欄為零,而在位址 24-27 表示每份 FAT 磁區數
BPB_SecPerTrk 18-19 2 每一磁軌之磁區數 同左
BPB_NumHeads 1A-1B2 磁頭數
BPB_HiddSec 1C-1F4 隱藏磁區數
BPB_TotSec32 20-23 4 邏輯磁區總數
BPB_FATSz32 FAT16 沒有這些欄位24-27 4 FAT16 沒有這些欄位 每份 FAT 所佔磁區數
BPB_ExtFlags 28-29 2 延伸旗標
BPB_FSVer 2A-2B 2 檔案系統版本
BPB_RootClus 2C-2F 4 根目錄的第一個磁叢編號
BPB_FSInfo 30-31 2 檔案系統資訊磁區
BPB_BkBootSec 32-33 2 備份啟動磁區磁區
BPB_Reserved 34-3F 12 保留
BS_DrvNum24 401 磁碟機編號 同左
BS_Reserved1 2541 1 保留
BS_BootSig 2642 1 特徵碼
BS_VolID 27-2A43-46 4 磁碟序號
BS_VolLab 2B-3547-51 11 磁碟標籤 ( 磁碟卷名 )
BS_FilSysType36-3D52-59 8 字串『FAT16   』 字串『FAT32   』
  3E-1FD 5A-1FD   啟動程式碼 同左
1FE-1FF 2 0AA55H,啟動磁區的結束識別碼

上表中,位址範圍是以十六進位制表示,各欄位大小是以十進位表示,而其中的欄位以藍色為底色的是 BPB。從上表大約可看出,FAT16 分割與 FAT32 分割的 BPB 大致一樣,但是因為 FAT32 需要管理較大的邏輯硬碟容量,所以增加了一些欄位。FAT16 的 BPB 是在啟動磁區的 0BH∼23H,共 25 ( 即十六進位的 19H ) 個位元組,在這範圍以外也提供了一些資訊,但並不真正屬於 BPB。

同樣的,FAT32 的 BPB 是在啟動磁區的 0BH∼3FH,共 53 ( 即 35H ) 個位元組,在此範圍之外,也有一些資訊,但也不是屬於 BPB。底下是啟動磁區的說明:

BS_jmpBoot: 0H∼2H,共 3 個位元組的跳躍指令,使程式跳到啟動程式處執行。跳躍指令有三種:短程跳躍 ( 跳躍範圍在 128 個位元組以內,機械碼是 0EBH )、近程跳躍 ( 跳躍範圍在一個區段內,即 64K 內,機械碼是 0E9H )、遠程跳躍 ( 區段之間的跳躍,機械碼是 0EAH )。一般而言,啟動磁區的跳躍指令不超過 128 個位元組,故均為 0EBH,其後接跳躍目的地離跳躍指令的位址差,此差值僅一個位元組,不過此欄位共有三個位元組,故最後再接上一個不做任何事的 CPU 指令,NOP,其機械碼是 90H。
BS_OEMName: 在位址 03H∼0AH,共 8 個位元組,這是格式化此分割區的作業系統所填入的廠商名稱。例如如果用 Win 95 格式化分割區,此欄填入『MSWIN4.0』字串;如果用 Win 95 OSR2 或用 Win 98 或 Win 98SE 格式化,此欄填入『MSWIN4.1』;如果用 Win 2000 格式化,此欄填入『MSDOS 5.0』。
BPB_BytsPerSec: 位址 0BH∼0CH 埵s放每磁區所含位元組數,一般為 512 個位元組,但也可以是 1024, 2048 或 4096 數值,不過微軟不建議使用除了 512 以外的數,小木偶也沒見過,所以我們可以大膽假設 BPB_BytsPerSec 就等於 512。
BPB_SecPerClu: 位址 0DH 這個位元組表示每一磁叢所佔磁區數,但是要注意不能使每磁叢所含位元組數大於 65536,亦即 BPB_SecPerClu 乘以 BPB_BytsPerSec 不能大於 65536。因此,當 BPB_BytsPerSec 等於 512 時,BPB_SecPerClu 可以是 1、2、4、8、16、32、64、128 等數值。
BPB_RsvdSecCnt: 位址 0EH∼0FH 這個字組表示保留區域的磁區數,對 FAT12 或 FAT16 分割而言,保留區域的磁區數必為一,因為其保留區域只有僅僅含一個啟動磁區而已。但對 FAT32 分割而言,除了啟動磁區外,還有檔案系統資訊磁區和備份啟動磁區,故通常不是 1,而是 20H。
BPB_NumFATs: 位址 10H 這個位元組表示 FAT 份數。不管是 FAT12、FAT16、FAT32 都有兩份檔案配置表 ( FAT )。
BPB_RootEntCnt: 位址 11H∼12H 字組表示根目錄所含 FDB 項數,即根目錄堨i以存放的檔案名稱及子目錄名稱之總和,FAT12/16 不能超過 BPB_RootEntCnt;但是對 FAt32 而言,此欄位不用,填上 0。
BPB_TotSec16: 13H∼14H 這個字組表示邏輯磁區總數,但最多只能表示 0FFFFH,所以若一個磁碟的邏輯磁區大於 0FFFFH ( 或是說磁碟容量大於 32MB,32MB 是 10000H*512D 來的 ),則此欄位為 0,邏輯磁區總數記錄在 20H 處開始的雙字組。因為 FAT32 分割必定大於 32MB,故此欄位不使用,均填入 0。
BPB_Media: 位址 15H 這個位元組表示磁碟種類。此欄位和 FAT 的第一個位元組一樣,其意義如下表:
0F0H:1.44MB 三吋半軟碟0F8H:硬碟
0F9H:1.2M 5.25 吋軟碟0FDH:360K 5.25 吋軟碟
0FEH:160K 5.25 吋軟碟0FFH:320K 5.25 吋軟碟
BPB_FATSz16: 位址 16H∼17H 這個字組表示每份 FAT 所佔磁區數;但對 FAT32 而言,此欄位不用,填上 0,而每份 FAT 所佔磁區數記載於位址 24H∼27H 處的雙字組。
BPB_SecPerTrk: 位址 18H∼19H 這個字組代表每一磁軌之磁區數。
BPB_NumHeads: 位址 1AH∼1BH 這個字組代表磁頭數。
BPB_HiddSec: 位址 1CH∼1FH 這個雙字組 ( 共 4 個位元組 ) 表示隱藏磁區數,與分割表或擴充分割表中的分割區相對位置的數值一樣。
BPB_TotSec32: 位址 20H∼23H 這個雙字組代表邏輯磁區總數。如果 16H∼17H 欄為 0,則此欄不可為零。

在啟動磁區偏移位址 24H∼3DH 的範圍,FAT 12/16 的欄位和 FAT32 差異很大,因此分開說明,底下先看看 FAT 12/16 的欄位,這些並不是真正的 BPB 內容,早期的作業系統或格式化程式並不支援底下的欄位,所以使用時得小心這些限制。

事實上 FAT32 也有這些欄位,意義也相同。只是 FAT32 的 BPB 較長,而這些欄位 ( 包含磁碟機編號、保留、特徵碼等六個欄位 ) 不屬於 BPB,所以 FAT32 是放在 FAT32 的 BPB 之後,也就是 40H 之後。

BS_DrvNum: 這個位元組代表磁碟機編號,FAT12/16 是在位址 24H,而 FAT32 是在位址 40H 。若為硬碟,BS_DrvNum 為 80H;若為軟碟,BS_DrvNum 為 0。
BS_Reserved1: 此位元組保留,需填入 0。對 FAT12/16 而言,在位址25H;對 FAT32 而言在位址 41H。
BS_BootSig: 此位元組表示特徵碼,和系統有關,如果是用微軟的作業系統,此欄位是 29H,此外這個特徵碼也表示下面三個欄位有意義。對 FAT12/16 而言 BS_BootSig 在位址 26H;對 FAT32 而言在 42H。
BS_VolID: 磁碟序號,共佔用 4 個位元組,不管是 FAT16 或 FAT32 所表示意義一樣,都是在 MS-DOS 模式用 dir 指令時顯示的『Volume Serial Number』如下圖。但 FAT12/16 存於位址 27H∼2AH;FAT32 則存於 43H∼46H。

圖一
BS_VolLab: 磁碟標籤 ( 磁碟卷名 ),共佔用 11 個位元組,不管是 FAT16 或 FAT32 所表示意義一樣,都是在 MS-DOS 模式用 dir 指令時顯示的『Volume』,如上圖一,或是顯示在我的電腦堛犖牬郋鬫W稱。但 FAT12/16 存於位址 2BH∼35H;FAT32 則存於 47H∼51H。
BS_FilSysType: 字串『FAT     』、『FAT12   』、『FAT16   』或『FAT32   』其中之一。但是一般不以此欄位判斷是那一個檔案系統,因為早期的作業系統格式化時,BPB 並沒有這一欄,稍後再說明如何判斷檔案系統。FAT12/16 存於 36H∼3DH;FAT32 則存於 52H∼59H。

底下位址 24H∼3FH 是 FAT32 的擴充 BPB,其欄位意義與 FAT16 的意義不同,所以在同時支援 FAT32 及 FAT16 的作業系統,如 Win 95 OSR2/Win98/Me/2K/XP 等,得先判斷邏輯硬碟屬於那一種檔案系統,是 FAT16 抑或 FAT32。

BPB_FATSz32: 位址 24H∼27H 內的雙字組表示每份 FAT 所佔磁區數,因為 FAT32 容量大,磁叢也多,所以用 4 個位元組表示每份 FAT 所佔磁區數。
BPB_ExtFlags: 位址 28H∼29H 內的字組表示延伸旗標。
BPB_FSVer: 位址 2AH∼2BH 內的字組表示檔案系統版本,2BH 表示主要版本,2AH 表示次要版本。
BPB_RootClus: 位址 2CH∼2FH 內的雙字組表示根目錄的第一個磁叢編號。
BPB_FSInfo: 位址 30H∼31H 堛漲r組存放著檔案系統資訊磁區距離啟動磁區有多少磁區,通常是 1 個磁區,也就是啟動磁區之後緊接著案系統資訊磁區。換言之,案系統資訊磁區是編號 1 的邏輯磁區。
BPB_BkBootSec: 位址 32H∼33H 內的字組存放著備份啟動磁區距離啟動磁區有多少磁區,通常是 6,微軟建議最好不要是 6 以外的數。換言之,啟動磁區是由邏輯磁區 6 開始。
BPB_Reserved: 位址 34H∼3FH 共 12 個位元組保留不使用,作為後續擴充之用,均填入 0。

檔案系統資訊磁區與備份啟動磁區

FAT32 的保留區域除了啟動磁區外,還有檔案系統資訊磁區及備份啟動磁區,底下來看看這兩個磁區。檔案系統資訊是為了能快速計算剩餘磁碟空間用的。在 FAT12/16 等磁碟容量不大的時代,要計算剩餘磁碟容量一般是讀取 FAT,並計算還有那些 FAT 可用 ( 註一 ),但到了 FAT32 時代,FAT 變得很大,所以得另闢捷徑才行。

名稱 位址範圍 大小
位元組
所代表意義
FSI_LeadSig 0H∼3H 4 引導記號,為一雙字組數值,等於 16 進位的 41615252H,表示此磁區為檔案系統資訊。
FSI_Reserved1 4H∼1E3H 1E0H 保留不用,均為 0
FSI_StrucSig 1E4H∼1E7H 4 另一個雙字組的數值,61417272H,表示下一雙字組為可用的磁叢數。
FSI_Free_Count 1E8H∼1EBH 4 表示此邏輯磁碟上可用的磁叢數,亦即在 MS-DOS 模式中用 DIR 指令時顯示的『bytes free』,如上圖一中但藍色框框框起來的,可用位元組數應等於 BPB_BytsPerSec*BPB_SecPerClu*FSI_StrucSig。如果 FSI_Free_Count 為 0FFFFFFFFH,表示系統尚未計算出可用磁叢數。
FSI_Nxt_Free 1ECH∼1EFH 4 第一個未使用的 FAT 編號。如果 FSI_Nxt_Free 為 0FFFFFFFFH,表示系統尚未計算出第一個未使用的 FAT 編號。
FSI_Reserved2 1F0H∼1FBH 12 保留,均填入 0。
FSI_TrailSig 1FCH∼1FFH 4 檔案系統資訊磁區結束的記號,為一雙字組,0AA550000H。

在檔案系統資訊磁區之後,有數個不知做為何用的磁區,小木偶手邊也沒有資料,所以也不知其所代表的意義。但在第 6 個邏輯磁區,就是啟動備份磁區,它是啟動磁區、檔案系統資訊磁區以及後面 4 個不知何用的磁區的拷貝,內容跟這六個磁區一樣,所以也得佔用 6 個磁區。接下來的保留區域,還有數個磁區,但觀其內容均為 0,也沒有資料可查。

如何判斷 FAT12、FAT16,還是 FAT32?

如果您寫一個程式要低階的讀取檔案內容 ( 亦即不靠 DOS 中斷服務或 Windows API ),那麼您得先判斷此邏輯分割屬於那個檔案系統?是 FAT12、FAT16 還是 FAT32?

要判斷檔案系統,一般的方法,也是微軟建議的方法是檢查磁叢總數,第 18 章提到當磁叢數小於 4085 時為 FAT12;大於或等於 4085 且小於 65525 時為 FAT16;大於或等於 65525 為 FAT32。而磁叢總數則等於資料區域的磁區總數除以每個磁叢所含磁區數,而資料區域的磁區數是邏輯分割總磁區數減去保留區域、FAT 區域、根目錄區域的磁區數。可寫成下面數學算式:

 TotSec = 邏輯磁區總數
  FATSz = 每份 FAT 的磁區數
DataSec = 資料區域的磁區總數
        = TotSec - BPB_ResvdSecCnt - ( BPB_NumFATs * FATSz )
          - RootDirSectors ...... (1)
磁叢總數 = CountofClusters = DataSec / BPB_SecPerClus ...... (2)

邏輯磁區總數 ( TotSec ) 可由 BPB 堛 BPB_TotSec16 或 BPB_TotSec32 取得。依據 BPB 的資料,若 BPB_TotSec16 為零,則邏輯磁區總數存於 BPB_TotSec32 堙A否則存於 BPB_TotSec16 內。

FAT 區域的磁區數是每份 FAT 所佔磁區數與 FAT 份數之乘積,但是前面曾經提到,FAT12/FAT16 與 FAT32 存放每份 FAT 所佔磁區數的位置不同,因此得先看看 BPB_FATSz16 是否為零,若為零則每份 FAT 所佔磁區數存放 BPB_FATSz32 處,若不為零則每份 FAT 所佔磁區數存放於 BPB_FATSz16 處。

到此,所需資料皆已具備,僅剩下根目錄區域的磁區數 ( RootDirSectors ) 了,在整個 BPB 堙A和根目錄有關的是 BPB_RootEntCnt,此字組記錄著根目錄堛檔案描述區塊 ( FDB ) 總數。而每個 FDB 佔有 32 個位元組,因此根目錄所佔位元組大小為 BPB_RootEntCnt*32,再除以每磁區所佔位元組數 ( BPB_BytsPerSec ) 即得根目錄區域的磁區數:

RootDirSectors = 
( BPB_RootEntCnt * 32 + BPB_BytsPerSec - 1 )/BPB_BytsPerSec ...... (3)

對 FAT32 而言,BPB_RootEntCnt 為零,故式 (3) 所得結果為零,符合 FAT32 沒有根目錄區域;但對 FAT16 或 FAT12 而言,因為 BPB_RootEntCnt 不為零,所以結果也不會是零。至於加上 BPB_BytsPerSec - 1 的目的是為使被除數不為零,但加或不加無所謂。依據上述結論,我們可以寫成下列組合語言程式:

        .386
;***********************************************************
data    segment
drv_bpb         bpb     <?>
data    ends
;***********************************************************
code    segment public  'code'  use16
        assume  cs:code,ds:data
;-----------------------------------------------------------
fat_type        proc    near
;由 bpb_drv 判斷此分割為 FAT12、FAT16 還是 FAT32
;輸出:CL=12H 表示 FAT12
;      CL=16H 表示 FAT16
;      CL=32H 表示 FAT32
fat_type        proc    near
        sub     edx,edx
        sub     eax,eax
        mov     ecx,edx
        mov     ax,drv_bpb.BPB_FATSz16
        or      ax,ax
        jnz     fat_ok
        mov     eax,drv_bpb.BPB_FATSz32
fat_ok: mov     cl,drv_bpb.BPB_NumFATs
        mul     ecx
        mov     total_FAT_sector,eax    ;EAX=所有 FAT 所佔磁區數
        mov     ax,drv_bpb.BPB_RootEntCnt
        mov     cx,drv_bpb.BPB_BytsPerSec
        shl     eax,5
        add     eax,ecx
        sub     edx,edx
        dec     eax
        div     ecx             ;EAX=根目錄所佔磁區數
        mov     dx,drv_bpb.BPB_TotSec16
        or      dx,dx
        jnz     total_sector_in_edx
        mov     edx,drv_bpb.BPB_TotSec32
        mov     total_sector,edx
total_sector_in_edx:            ;EDX=邏輯磁區總數
        sub     edx,eax
        sub     edx,total_FAT_sector
        mov     ax,drv_bpb.BPB_RsvdSecCnt
        cwd                     ;EAX=保留磁區數
        sub     edx,eax         ;EDX=資料區域的磁區數
        sub     eax,eax
        xchg    eax,edx
        mov     cl,drv_bpb.BPB_SecPerClu
        mov     ch,0
        div     ecx             ;EAX=磁叢總數
        mov     cl,32h
        cmp     eax,65525
        jae     got_it
        mov     cl,16h
        cmp     eax,4085
        jae     got_it
        mov     cl,12h
got_it: ret
fat_type        endp
;-----------------------------------------------------------

資料區域的第零個邏輯磁區編號

資料區域是在保留區域、FAT 區域及根目錄區域之後,因此資料區域的第零個邏輯磁區編號 ( FirstDataSector ) 也就是這些區域磁區數之和:

FirstDataSector = BPB_RsvdSecCnt + BPB_NumFATs*FATSz + RootDirSectors ... (4)

FAT 區域

在保留區域之後,就是稱為檔案配置表 ( File Allocation Table ) 的區域。這塊區域記載著每個檔案佔據了那些磁叢 ( cluster ),磁叢是由數個連續的邏輯磁區組成,其大小是隨著磁碟容量以及 DOS 版本不同而變。每當作業系統寫入資料到磁碟上時,是以磁叢為單位,為檔案配置佔用的磁叢,換句話說,每個檔案都佔有整數個磁叢。

那麼 FAT 怎樣記錄檔案佔用了那些磁叢呢?在第 18 章中曾對 FAT12 有過詳細的介紹,FAT16 或 FAT32 記錄檔案佔有那些磁叢的原理與 FAT12 相同。對 FAT12、FAT16、FAT32 這幾種檔案系統而言,每個磁叢都以一個編號表示,這些編號是由記錄在 FAT 區域堛漲鼽m決定,而各種檔案系統的磁叢編號所佔位元長度不同,您可以由它們的名稱很容易就得知,FAT12 的磁叢編號是 12 個位元 ( 即 1.5 個位元組 ),FAT16 的磁叢編號是 16 個位元 ( 即 2 個位元組 ),FAT32 的磁叢編號是 32 個位元 ( 即 4 個位元組 )。

第 18 章已看過 FAT12 的 FAT 區域,底下來就來看看另一個不同的例子,一個 FAT32 的 FAT 區域,此邏輯硬碟是小木偶 Windows 98 SE 的啟動硬碟。小木偶將藉著這個例子來說明 FAT32 中,FAT 內欄位與邏輯磁區的關係以及 FAT 各欄位的意義。

第 N 個 FAT 欄位與其對應的邏輯磁區

首先先在 MS-DOS 模式底下輸入 DEBUG,( 照往例,黃色的是小木偶輸入的指令,[Enter] 表示按下 Enter 鍵,其餘都是電腦的回應 ) 接著載入 C: 邏輯硬碟的啟動磁區:

C:\WINDOWS>DEBUG [Enter]
-L 100 2 0 1 [Enter]

DEBUG 的 L 指令可載入磁區,『100』表示載入後的磁區資料放在位址 100H 處,『2』表示『C:』磁碟,『0』表示載入第零磁區 ( 邏輯磁區皆由 0 開始 ),『1』表示載入磁區數。載入後要尋找 FAT 區域,照前面所說,FAT 區域在保留區域之後,因此知道保留區域多大就知道 FAT 區域由那個磁區開始。因此由螢幕印出 BPB:


-D 100 13F [Enter]
136C:0100  EB 58 90 4D 53 57 49 4E-34 2E 31 00 02 08 20 00   .X.MSWIN4.1... .
136C:0110  02 00 00 00 00 F8 00 00-3F 00 FF 00 80 60 1F 00   ........?....`..
136C:0120  7E 04 7D 00 32 1F 00 00-00 00 00 00 02 00 00 00   ~.}.2...........
136C:0130  01 00 06 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
-L 300 2 20 1 [Enter]

保留磁區大小是在 BPB_RsvdSecCnt 處,亦即 010E 起的一個字組 ( 因為存放啟動磁區的資料起始於位址 0100H 處,故 BPB_RsvdSecCnt 在 010E 處,即上面天藍色處 ),然後載入 FAT 區域的第一個磁區,如上『L 300 2 20 1』,這堛 20 就是保留磁區大小。然後輸入『D 300』觀察 FAT:

對 FAT32 來說,每個 FAT 欄位佔了 32 位元,亦即一個雙字組,每個欄位以一個編號表示。位址 0300∼0303H 的雙字組是 FAT 的第 0 個欄位,即編號 0;0304∼0307H 是第 1 個欄位,即編號 1;0308∼030BH 是第 2 個欄位,即編號 2;……依此類推。第零個欄位的最低位元組是 0F8H,與 BPB_Media 相同,其餘位元均設為一。第一個欄位的位元均設為一,這兩個 FAT 欄位均為系統使用。

從第 2 個 FAT 欄位才開始做為儲存資料,所以資料區域的磁叢編號也是 2 開始,那麼編號為二的磁叢是由那一個邏輯磁區開始呢?或者可問第 2 個 FAT 代表那些邏輯磁區呢?

先看看底下的圖,可能比較好瞭解:

FAT 與邏輯磁區的關係
由上圖可以知道,原來編號二的磁叢起始磁區就是資料區域的第零個邏輯磁區,而每個磁叢所含磁區數存於 BPB_SecPerClu 內。因此可以藉由下面算式求出編號 N 的 FAT 欄位之起始磁區:

FirstSectorofCluster = ( N - 2 ) * BPB_SecPerClu + FirstDataSector .... (5)

在這個例子堙A根目錄區域所佔磁區數為零,保留區域所佔磁區數為 20H,兩份 FAT 區域共佔有 1F23H*2,即 3E64H,因此這三個區域共佔 3E84H,故資料磁區的第零個邏輯磁區編號為 3E84H,而連此磁區以及其後的磁區,共 8 個磁區是編號 2 磁叢所含有的磁區;再接著的 8 個磁區則是編號 3 磁叢的磁區……。

FAT 欄位內容之意義

前面已解釋過 FAT 第 0、1 欄位內之數值意義,它們是供系統使用。而其他 FAT 欄位內的數值是代表什麼意義呢?請看下表:

FAT12 FAT16FAT32 意  義
0000000 0000000此 FAT 欄位對應的磁叢並未使用
3∼FF63∼FFF63∼FFFFFF6 表示此檔案的下一個磁叢編號
FF7FFF7FFFFFF7 此 FAT 欄位所對應的磁叢已損壞,不可使用
大於或等於 FF8大於或等於 FFF8 大於或等於 FFFFFF8此 FAT 欄位所對應的磁叢為檔案的最後一個磁叢

我想還是延續上面的例子說明好了。剛提過,小木偶的 Windows 98 SE 的啟動硬碟中,第一個資料磁區是 3E84,載入到記憶體觀察看看:

-L 500 2 3E84 1 [Enter]
-D 500 [Enter]
136C:0500  53 55 48 44 4C 4F 47 20-44 41 54 03 00 00 00 00   SUHDLOG DAT.....
136C:0510  00 00 43 33 00 00 4C 6C-6F 30 03 00 4E 14 00 00   ..C3..Llo0..N...
136C:0520  42 4F 4F 54 4C 4F 47 20-54 58 54 22 00 00 00 00   BOOTLOG TXT"....
136C:0530  00 00 43 33 00 00 93 6D-6F 30 05 00 6E B5 00 00   ..C3...mo0..n...
136C:0540  46 52 55 4E 4C 4F 47 20-54 58 54 20 00 95 7A 6C   FRUNLOG TXT ..zl
136C:0550  6F 30 43 33 00 00 92 6C-6F 30 11 00 ED 03 00 00   o0C3...lo0......
136C:0560  57 49 4E 39 38 53 45 20-20 20 20 28 00 00 00 00   WIN98SE    (....
136C:0570  00 00 6F 30 00 00 58 6B-6F 30 00 00 00 00 00 00   ..o0..Xko0......

看起來似乎是一些檔案名稱及某些資料,的確,這就是根目錄磁區。由 BPB_RootClus 得知 ( 見上面 BPB 白色處 ),根目錄是在編號 2 的磁叢處,代入式 (5),便可得根目錄磁區佔據編號 3E48 的邏輯磁區起,共 8 個磁區。根目錄是不是只佔據了這 8 個磁區呢?可以由 FAT 區域的第 2 個欄位得知,該欄位是 0FFFFFFF ( 見上面黃線框起來處 ),就知道根目錄結束於此。

再舉另一例說明。見根目錄 SUHDLOG.TXT 這個檔案的磁叢指位器指的是 3 ( 上面紅色字 ),表示此檔由編號 3 的磁叢開始存放,亦可代入式 (5) 求得邏輯磁區,而 FAT 區域的第 3 個欄位內的數值是 4,表示此檔不只佔據一個磁叢,還佔據編號 4 的磁叢,再看 FAT 區域的第 4 個欄位是結束記號,表示此檔就佔有這兩個連續的磁叢。因此您可以把一個檔案所含的 FAT 欄位視為一連串的鏈,此鏈的開端是在 FDB,而中間是靠 FAT 堛瘧璁鴗漁e連接,尾端是以 FFFFFF8 結尾 ( 對 FAT32 而言 )。

取得編號 N 的 FAT 欄位內容

由上面的說明,應當明白,取得 FAT 欄為堛漱漁e是很重要的,因為 FAT 欄位堛漱漁e是下一個檔案磁叢所在,或是結束記號,能取得 FAT 欄位內容,才能存取整個檔案。底下來看看怎麼樣取得編號為 N 的 FAT 欄位內容。由下面的 FAT 區域部份內容

可知:我們得先計算第 N 個編號的 FAT 欄位距離 FAT 區域起始有多少個位元組,這個距離顯然和 FAT 型態有關。也就是說,如果是 FAT32,這距離為 4*N ( 因為 FAT32,每個 FAT 欄位佔用 4 個位元組,亦即 32 位元 );如果是 FAT16,則為 2*N;如果是 FAT12,則為 1.5*N。所以可以寫成如下的組合語言程式:

        mov     eax,N   ;第 N 個 FAT 欄位
.if fat_type==12h
        mov     ecx,eax
        shr     ecx,1   ;ECX = EAX/2
        add     eax,ecx ;EAX = EAX + EAX/2,即 1.5*N
.elseif fat_type==16h
        shl     eax,1
.elseif fat_type==32h
        shl     eax,2
.endif                  ;執行完後,EAX 為第 N 個編號的 FAT 欄
                        ;位距離 FAT 區域起始有多少個位元組

而每 512 個位元組組成一磁區,因此執行完上面程式的 EAX 再除以 512,就可求得 FAT 區域的第幾個磁區,而餘數所指的位址之數值就是第 N 個 FAT 欄位的內容。此數值的長度與 FAT 型態有關,若為 FAT32 則必須取得 32 位元長,若為 FAT16 則必須取得 16 位元長,若為 FAT12 則必須取得 12 位元長。對 FAT32/16 而言,只要取得實際長度即可,但是 FAT12 的欄位有 1.5 位元組長,比較麻煩,一般是依照下面公式計算:

        movzx   eax,word ptr fs:[bx]    ;BX = EAX 除以 512 後的餘數
.if fat_type==12h
        test    N,1
        jz      even_n
        shr     eax,4
even_n: and     ax,0fffh
.elseif fat_type==16h
.elseif fat_type==32h
        mov     eax,dword ptr fs:[bx]
        and     eax,0fffffffh
.endif

執行完後,EAX 就是第 N 個磁叢編號之內容。


根目錄區域

對 FAT12/16 檔案系統而言,FAT 區域之後是根目錄區域,根目錄是最上層的目錄,所有的子目錄或檔案都是以這個目錄為基準點存放的。根目錄堸O載著許多檔案的資料,例如檔名、建立日期時間、起始磁叢等等,每個檔案都以 32 個位元組記錄這些資料,稱為檔案描述區塊 ( FDB )

根目錄是在磁碟格式化的時候就已建立的目錄,其所佔有的磁區數無法改變,所以存放在根目錄堛瑰仵蚸峇l目錄總數是有限制的,這個總數存放於 BPB 堛 BPB_RootEntCnt 欄位堙C因為這樣,所以如果在一個很大的磁碟機堙A如果不建立子目錄的話,當根目錄的 FDB 耗盡,即使資料區域還有空間,也無法存入資料。

不過經由建立子目錄可以迴避這個限制,此外也可以使檔案分門別類存放,可說好處多多。子目錄與根目錄相同都是由許多 FDB 組成,不過有兩點不同:

  1. 子目錄的前兩個 FDB 檔名必定是『.』和『..』,分別代表這個子目錄以及上一層目錄。
  2. 子目錄是屬於資料區域,其長度可以改變。

當新建立一個子目錄時,系統會分配一個磁叢記錄這個子目錄的所有 FDB 資料,而且會在這個子目錄的上層目錄中,以一個 FDB 記錄這個子目錄的資料,包含起始磁叢等等,而起始磁叢在 FAT 欄位會先記錄成 結束記號。當這個子目錄的檔案漸多,以致使得一個磁叢不夠用時,系統可以再分配另一磁叢給這個子目錄,同時修改起始磁叢的 FAT 欄位,使其指向被新分配到的磁叢。系統以上述方法使子目錄堛瑰仵袧ぁu受磁碟容量的限制。

對 FAT32 檔案系統而言,並沒有所謂的根目錄區域,而是把根目錄看成是資料區域的一部份,根目錄就像是一個比較特別的子目錄一樣。FAT32 檔案系統建立根目錄時僅分配一個磁叢給根目錄,而這個磁叢會存入 BPB 的 BPB_RootClus 欄位,當根目錄 FDB 不夠時,系統會比照子目錄的方式再分配一個磁叢給根目錄,因此根目錄檔案不會有限制。


8.3 檔名格式與 VFAT

在 DOS 6.x 及其之前的 DOS 版本,可能為了磁碟容量或其他理由,檔案名稱有許多規則,或許您也可以說是限制。這些規則是:

  1. 檔名通常可以分為主檔名以及副檔名,中間以『.』分隔。副檔名可有可無,但副檔名常代表這個檔案的用途、性質等等,例如可執行檔是以 .EXE 表示。
  2. 主檔名最多只能有八個 ASCII 字元組成,副檔名最多只能有 3 個 ASCII 字元組成,並且兩者皆不能用特殊符號例如『\』、『|』等 ( 這些被當做 DOS 的特殊字元,如重新導向、管線等 ),也不能用 ASCII 字元小於 32 以下的數值,例如 Carrier Return、Escape 等字元。

由於主檔名、副檔名最多為 8、3 個 ASCII 字元,故稱為『8.3 檔名格式』或者稱為短檔名。當然,這些限制式很不合理的,所幸到了 Windows 95 上市時,推出了 VFAT。雖然有點兒晚,但是總比沒有來得好。

VFAT 除了可使用長檔名 ( LFN,Long File Name,最多可達 255 位元組 ) 外,還有幾項重大的性質,一是檔名使用萬國碼 ( UniCode,請參閱附錄十 ) 編碼而不再用 ASCII 碼 ( 這使得檔名可以用中文、阿拉伯文等世界上大多的文字作為檔名 );二是相容於原有的 FAT12/16/32 檔案系統,也就是說在 Windows 9x/Me 系統下,原來用 DOS 格式化的邏輯磁碟也可以用 VFAT。要達到這些目的,微軟是怎麼做到的呢?

原來微軟修改了 FDB,變成兩種形式,一種即短檔名形式,與原來的相同;另一種是長檔名形式,專門用來儲存長檔名的。假如某個檔案的檔名符合『8.3 檔名格式』,那麼這個檔案就只有一個短檔名形式的 FDB,而沒有長檔名形式的 FDB;如果某個檔案的檔名不符合『8.3 檔名格式』,那麼這個檔案就有一個短檔名形式的 FDB 與一個或數個長檔名形式的 FDB 兩種 FDB,長檔名形式的 FDB 只儲存長檔名,而檔案的建立日期、長度、起始磁叢等仍儲存於短檔名形式的 FDB 堙A而且長檔名形式的 FDB 之後,緊接著檔檔名形式的 FDB,底下先介紹長檔名形式的 FDB。每個長檔名形式的 FDB 也擁有 32 個位元組:

位址 長度
(位元組)
意  義
0 1 長檔名形式的 FDB 順序,一個具有長檔名的檔案很可能擁有數個長檔名形式的 FDB,這些 FDB 由短檔名形式的 FDB 開始向低位址排列,而這個位元組就表示其順序,由 1 開始,最多到 20,但是如果是最後一個 FDB,則須加上 40H。
1∼0AH 0AH 長檔名,此部份可以存 5 個萬國碼字元
0BH 1 長檔名形式 FDB 屬性,必為 0FH,檢查此位元組,若為 0FH,表示此 FDB 為長檔名形式的 FDB
0CH 1 保留,必為 00H
0DH 1 查核碼
0EH∼19H 0CH 長檔名,此部份可以存 6 個萬國碼字元
1AH∼1BH 2 保留,必為 00H
1CH∼1FH 4 長檔名,此部份可以存 2 個萬國碼字元

由上表得知,每個長檔名形式的 FDB,雖然佔有 32 個位元組,但是順序碼、查核碼、屬性、保留等位元組,實際上能夠儲存檔名只有 26 個位元組,但是每個萬國碼需佔用 2 個位元組,因此實際上每個長檔名形式的 FDB 只能存有 13 個字。而這 13 個字是以萬國碼編碼而成,並且散落於 FDB 的三個部份。

底下來看看一個例子,這是小木偶硬碟中的『I learn assembly.txt 』檔案,小木偶用 DEBUG.EXE 載入它所在的目錄磁區,然後傾印的結果:

128B:0000  42 62 00 6C 00 79 00 2E-00 74 00 0F 00 D4 78 00   Bb.l.y...t....x.
128B:0010  74 00 00 00 FF FF FF FF-FF FF 00 00 FF FF FF FF   t...............
128B:0020  01 49 00 20 00 6C 00 65-00 61 00 0F 00 D4 72 00   .I. .l.e.a....r.
128B:0030  6E 00 20 00 61 00 73 00-73 00 00 00 65 00 6D 00   n. .a.s.s...e.m.
128B:0040  49 4C 45 41 52 4E 7E 31-54 58 54 20 00 5C 76 30   ILEARN~1TXT .\v0
128B:0050  97 34 97 34 0F 00 7B 30-97 34 B6 49 03 00 00 00   .4.4..{0.4.I....

最底下藍色的部份就是短檔名形式的 FDB,包含了檔案屬性、建立日期等等,而長檔名則記載於低位址的黃色與橙色的兩個長檔名形式的 FDB 堙A黃色部份則為長檔名的第一個 FDB,故第零個位元組為 1,橙色為第二個,也是最後一個 FDB,故第零個位元組為 42H。而完整的長檔名則是由短檔名形式的 FDB 低一個位址的 FDB 開始,把每個長檔名形式的 FDB 堛漱T部份檔名組合,若一個 FDB 不夠用則往低位址的 FDB 延伸而組成,直到遇到零為止。


註一:對 FAT16 分割而言,最多有 65536 個磁叢,所以有 65536 個磁叢編號,每個磁叢編號佔 16 位元,亦即 2 個位元組,因此 FAT 最多佔有 256 個磁區。

一個 FAT 所佔位元組數:65536*2=131072
每個磁區有 512 位元組,故一個 FAT 所佔磁區數:131072/512=256

還不至影響太大,但是對 FAT32 分割而言,不只每個磁叢編號佔用 32 位元,同時磁叢編號數也增加了,所以如果用上述讀取 FAT,計算未使用的磁叢數,將會曠日費時,所以用 FSI_StrucSig 來計算剩餘容量。


註二:在第 18、32 和這一章提到了許多磁區編號與存取磁區的服務程式,我想可能有許多人會搞混,在此稍做解釋。

首先得要會分辨實體磁碟與邏輯磁碟。實體硬碟就是您肉眼所見的硬碟,例如現在的 IDE 界面最多可以接上四顆硬碟,此處便是指實體硬碟。但是一顆硬碟可以藉由 FDISK.EXE 等工具,把它分割成好幾個分割區,每個分割區可視為一顆邏輯硬碟,因此邏輯硬碟是並非您看見的硬碟,不過我們在作業系統中見到的 C:、D:…等磁碟編號就是邏輯磁碟。存取實體硬碟時,用 INT 13H/INT 13H Extension 服務程式,其定址方式有 CHS 或 LBA 邏輯定址。而存取邏輯磁區則是用 INT 25H/INT 26H 或 AX=7305/INT 21H。其他請參考下表:

硬碟 實體硬碟 邏輯硬碟
磁區編號
名稱
CHS LBA  
磁區編號 由最外圈為零磁柱,向內圈直到最大磁柱;每磁柱分成若干磁頭,磁頭由零到最大磁頭;每磁頭再分成若干磁區,磁區由一開始到最大磁區 由實體硬碟的 MBR 開始編號,此磁區為零一直到實體硬碟的磁區總數減一 由邏輯磁碟的最前面磁區 ( 即啟動磁區 ) 開始標號,此磁區為零磁區一直到這個邏輯磁碟的磁區總數減一
服務程式 INT 13H INT 13H Extension INT 25H/26H 或
AX=7305H/INT 21H
磁碟編號 軟碟由 0 開始,0:A:,1:B:…
硬碟由 80H 開始,80H:第一顆實體硬碟,81H:第二顆實體硬碟…依此類推
0 表示 A: 磁碟機,1 表示 B: 磁碟機,2 表示 C: 磁碟機…依此類推
磁碟機放於何處 DL 暫存器 AL 暫存器

回到首頁到第三十二章到第三十四章