1. 程式人生 > >fat32檔案系統的實現與buddy演算法

fat32檔案系統的實現與buddy演算法

報告一    FAT32檔案系統的實現

檔案系統(File System)是計算機系統必不可少的組成部分,可以說除了部分結構簡單的微控制器系統之外,檔案系統是支撐每一個計算機系統執行的最重要的支撐,無論是作業系統、應用程式、文件還是音視訊都是基於檔案系統的。所以由此可見檔案系統在計算機上的重要地位。

起初所有的FAT檔案系統都是為PC機器設計的,這說明了一個重要的問題:FAT檔案系統在磁碟上的資料是以“小端”結構儲存的。我們使用4個8-bit位元組----起始位元組為byte[0],結束位元組為byte[3],類儲存一個32-bit的FAT項。然後分別給這32位編號為00—31,從下表可以看出這32位如何排序。

Byte[3]  3 3 2 2 2 2 2 2

         1 0 9 8 7 6 5 4

Byte[2]  2 2 2 2 1 1 1 1

         3 2 1 0 9 8 7 6

Byte[1]  1 1 1 1 0 0 0 0

         5 4 3 2 1 0 9 8

Byte[0]  0 0 0 0 0 0 0 0

         7 6 5 4 3 2 1 0

這對於那些使用“大端”儲存結構的機器就顯得尤為重要,因為在磁碟存取資料之前,必須完成big-endian和little-endian之間的轉換。

每個FAT32檔案系統由DBR及其保留扇區,FAT1,FAT2和DATA四個部分組成,其結構如下圖:

這些結構是在分割槽被格式化時創建出來的,含義解釋如下:

DBR及其保留扇區:DBR的含義是DOS引導記錄,也稱為作業系統引導記錄,在DBR之後往往會有一些保留扇區。

FAT1:FAT的含義是檔案分配表,FAT32一般有兩份FAT,FAT1是第一份,也是主FAT。

FAT2:FAT2是FAT32的第二份檔案分配表,也是FAT1的備份。

DATA:DATA也就是資料區,是FAT32檔案系統的主要區域,其中包含目錄區域。

 

FAT32檔案系統的DBR有5部分組成,分別為跳轉指令,OEM代號,BPB,拓展BPB,載入程式和結束標誌。

跳轉指令將呈現執行流程跳轉到載入程式處;

OEM ID由廠商指定,這裡是MSDOS5.0;

BPB記錄檔案系統相關的重要資訊,由BPB和拓展BPB組成。

 

接下來一個重要的資料結構就是FAT表,它是一一對應與資料區簇號的列表。

檔案系統分配磁碟空間按簇來分配的。因此,檔案佔用磁碟空間時,基本單位不是位元組,而是簇。即使某個檔案只有一個位元組,作業系統也會給他分配一個最小單元----即一個簇。

磁碟格式化後,使用者檔案是以簇為單位存放在資料區中,一個檔案至少佔用一個簇。當一個檔案佔用多個簇時,這些簇的簇號不一定是連續,但這些簇號間有由儲存該檔案時確定了的順序,即每個檔案都有其特定的“簇號鏈”。

在磁碟上的每一個可用的簇在FAT中就只有一個登記項,通過在對應簇號的登記項內填入“表項值”來表明資料區中的該簇是否佔用、空閒或是已損壞的。損壞的簇是在格式化的過程中,通過FORMAT命令發現。在一個簇中,只要有一個扇區有問題,該簇就不能夠使用。

現將FAT表的組成格式及功能總結如下:
·表明磁碟型別。FAT的第0簇和第1簇為保留簇,其中第0位元組(首位元組)表示磁碟型別,其值與BPB中磁介質說明符對應的磁碟型別相同。
·表明一個檔案所佔用各簇的簇鏈分配情況。FAT從002簇開始分配給檔案。表項值“001H--FEFH”、“0001H--FFEFH”或“00000001H--FFFFFFEFH”中的任一值表明檔案的下一簇號。檔案的起始簇號由檔案目錄表(FDT)中每個目錄登記項的第26、27位元組決定,作為FAT的入口,起始簇號在FAT中的表項值即檔案的第2簇號,第二簇號的表項值即第3簇號,依此類推,直到表項值為FF8H--FFFH、FFF8H--FFFFH或FFFFFFF8H--FFFFFFFFH,表示該簇為檔案的最後一簇。
·標明壞簇和可用簇。若軟盤格式化時發現壞扇區,即在相應簇的表項中寫入FF7H(或FFF7H),表明該扇區所在簇不能使用,DOS就不會將它分配給使用者檔案。
磁碟上未用,但可用的“空簇”的表項值為000H(或0000H)。當需要存放新檔案時,檔案管理系統將它們按一定順序分配給新檔案。
雖然FAT表記錄了檔案所用的磁碟空間資訊,但是DOS引導區、兩個FAT表、檔案目錄區等並不由FAT表中的簇表示。

 

FAT表結構及作用

1、FAT32檔案一般有兩份FAT,他們由格式化程式在對分割槽進行格式化時建立,FAT1是主,FAT2是備份。

2、FAT1跟在DBR之後,其具體地址由DBR的BPB引數中指定,FAT2跟在FAT1的後面。

3、FAT表由FAT表項構成,我們把FAT表項簡稱FAT項,每個FAT項佔用4位元組。

4、每個FAT項都有一個固定的編號,這個編號從0開始。

5、FAT表項的前兩個FAT項為檔案系統保留使用,0號FAT為介質型別,1號FAT為檔案系統錯誤標誌。

6、分割槽的資料區中每個簇都會對映到FAT表中的唯一一個FAT項,因為0號FAT和1號FAT被系統佔用,使用者的資料從2號FAT開始記錄。

7、如果某個檔案佔用很多個簇,則第一個FAT項記錄下一個FAT項的編號(既簇號),如果這個檔案結束了,則用“0F FF FF FF”表示。

8、分割槽格式化後,使用者檔案以簇為單位存放在資料區中,一個檔案至少佔用一個簇。

9、FAT的主要作用是標明分割槽儲存的介質以及簇的使用情況。

 

定位FAT絕對位置的方法如下:

1、首先從MBR的分割槽表中得知分割槽的起始扇區,偏移到此扇區。

2、從DBR的BPB中得知DBR的保留扇區數,FAT表的個數,FAT表的大小。

3、因此FAT1=分割槽起始扇區+DBR保留扇區,FAT2=分割槽起始扇區+DBR保留扇區+FAT1。

 

資料區的位置在FAT2的後面,具體定位方式如下;

1、通過MBR中的分割槽表資訊得知分割槽的起始位置。

2、通過分割槽中DBR得知DBR的保留扇區數以及FAT表的大小,FAT表的個數。

3、通過上面的資訊就可以找到資料區的起始位置,根目錄=資料區的起始扇區+(簇大小*2)。

 

資料區的類容主要由三部分組成:根目錄,子目錄和檔案內容。在資料區中是以“簇”為單位進行儲存的,2號簇被分配給根目錄使用。

根目錄的定位方式為:根目錄=分割槽起始扇區+DBR保留扇區+(FAT表*2)+(簇大小*2)

FAT32檔案系統中,分割槽根目錄下的檔案和目錄都放在根目錄區中,子目錄中的檔案和目錄都放在子目錄區中,並且沒每32個位元組為一個目錄項,每個目錄項紀錄著一個目錄或檔案(也可能是多個目錄項記錄一個檔案或目錄),如上圖所示就是一個目錄項。

在FAT32檔案系統中,目錄項可以分為四類:卷標目錄項、“.”和“..”目錄項、短檔名目錄項、長檔名目錄項。

卷標目錄項:卷標就是分割槽的名字,可以在格式化分割槽時建立,也可以隨意修改,長度為11位元組。

“.”和“..”目錄項:“.”表示當前目錄,“..”表示上一層目錄。這兩個目錄項多存在子目錄中。

短檔名目錄項:所謂短檔名既檔名的“8.3”格式,此格式支援主檔名不能超過8位元組,副檔名不能超過3位元組。短檔名目錄始終存放在一個目錄項中。

短檔名的各引數解釋如下:

關於時間的表達方式:10111(23)   111000(56)   00001(1*2)時間值:23時56分02秒。

關於日期的表達方式:0011101(29+1980)   1010(10)   10011(19)時間值:2009年10月19日。

長檔名目錄項:由於短檔名“8.3”的格式遠遠不能滿足現實中的需求,所以就出現了長檔名。

FAT32長檔案目錄項32個位元組的表示定義如下:

 

小結

上述的內容已經簡單的介紹了FAT32檔案系統,下面根據定位某個檔案來詳細的瞭解FAT32檔案系統是如何儲存資料的。

1、根據磁碟0號扇區MBR的分割槽表得知分割槽的起始位置,既DBR;

2、根據DBR中BPB記錄的資訊,得知DBR保留扇區數,FAT的大小,FAT的個數;

3、根據上述資訊可以算出資料的起始位置,資料區=分割槽起始扇區+DBR保留扇區+(FAT表*2);

4、計算根目錄所在的絕對位置,根目錄=資料區的起始扇區+(簇大小*2);

5、根據根目錄中的目錄項資訊得知,根目錄下的檔案以及子目錄等所對應的簇;

6、根據檔案的簇號就可以找到檔案內容的絕對扇區;

7、如果一個檔案佔用多個簇,則需要根據FAT表項得知下一個資料簇的簇號。

7、如果根目錄下的目錄項是子目錄的話,則根據子目錄中的檔案目錄項得知檔案內容的簇號;

8、如果子目錄中還有子目錄的話,則根據這種方法一直找下去即可。

 

 

報告二    linux中記憶體管理Buddy如何實現

在系統執行過程中,經常需要分配一組連續的頁,而頻繁的申請和釋放記憶體頁會導致記憶體中散佈著許多不連續的頁,這樣,當某一時刻要申請一塊較大的連續記憶體時,雖然系統記憶體餘量足夠,即很多頁是空閒的,但找不到一大塊連續的記憶體供使用。

Linux核心中使用夥伴系統(buddy system)演算法來管理記憶體頁。它把所有的空閒頁放到11個連結串列中,每個連結串列分別管理大小為1,2,4,8,16,32,64,128,256,512,1024個頁的記憶體塊。

buddy就是為了解決外部碎片(外部碎片指的是還沒有被分配出去(不屬於任何程序),但由於太小了無法分配給申請記憶體空間的新程序的記憶體空閒區域。)問題,它將記憶體按照按2的冪級(order)大小排成連結串列佇列,存放在free_area陣列。

對1024個頁框的最大請求對應著4MB大小的連續RAM塊。每個塊的第一個頁框的實體地址是該塊大小的整數倍。例如,大小為16個頁框的塊,其起始地址是16*2^12的倍數。核心試圖把大小為b的一對空閒夥伴塊合併為一個大小為2b的單獨塊。滿足以下條件的兩個塊稱為夥伴:

1,兩個塊具有相同的大小,記作b

2,他們的實體地址是連續的

3,第一塊的第一個頁框的實體地址是2*b*2^12的倍數

該演算法是迭代的,如果它成功合併所釋放的塊,它會試圖合併2b的塊,以再次試圖形成更大的塊。

 

夥伴演算法主要步驟:

1.將空閒頁面分為m個組,第1組儲存2^0個單位的記憶體塊,第2組儲存2^1個單位的記憶體塊,第3組儲存2^2個單位的記憶體塊,第4組儲存2^3個單位的記憶體塊,以此類推,直到m組。

2.每個組是一個連結串列,用於連線同等大小的記憶體塊。

3.夥伴塊的大小是相等的,並且第1塊和第2塊是夥伴,第三塊和第四塊是夥伴,以此類推。

 

夥伴演算法分配記憶體:

1.如果該陣列有剩餘記憶體塊,則分配出去。

2.若沒有剩餘記憶體塊就沿陣列向上查詢,然後再將該記憶體塊分割出來s並將剩餘的記憶體塊放入相應大小的陣列中。

Linux使用Buddy演算法來有效的分配與回收頁面塊。頁面分配程式碼每次分配包含一個或者多個物理頁面的記憶體塊。頁面以2的次冪的記憶體塊來分配。這意味著它可以分配1個、2個和4個頁面的塊。只要系統中有足夠的空閒頁面來滿足這個要求(nr_free_pages > min_free_page),記憶體分配程式碼將在free_area中尋找一個與請求大小相同的空閒塊。free_area中的每個元素儲存著一個反映這樣大小的已分配與空閒頁面 的點陣圖。例如,free_area陣列中第二個元素指向一個反映大小為四個頁面的記憶體塊分配情況的記憶體映象。

分配演算法首先搜尋滿足請求大小的頁面。它從free_area資料結構的list域著手沿鏈來搜尋空閒頁面。如果沒有這樣請求大小的空閒頁面,則它搜尋兩倍於請求大小的記憶體塊。這個過程一直將持續到free_area 被搜尋完或找到滿足要求的記憶體塊為止。如果找到的頁面塊大於請求的塊則對其進行分割以使其大小與請求塊匹配。由於塊大小都是2的次冪所以分割過程十分簡單。空閒塊被連進相應的佇列而這個頁面塊被分配給呼叫者。

例如分配5大小的記憶體塊

定位到大小為8的連結串列中,若該連結串列中之中沒有空餘元素,則定位到16的連結串列中,16中有剩餘元素,則取出該元素,並分割出大小為8的記憶體塊供使用者使用,然後將剩餘的8連線到大小為8的陣列中。

 再比如如圖所示,要分配4(2^2)頁(16k)的記憶體空間,演算法會先從free_area[2]中檢視nr_free是否為空,如果有空閒塊,就直接從中摘下並分配出去,如果沒有空閒塊,就順著陣列向上查詢,從它的上一級free_area[3](每塊32K)中分配,如果free_area[3]中有空閒塊,則將其從連結串列中摘下,分成等大小的兩部分,前四個頁面作為一個塊插入free_area[2],後4個頁面分配出去,如果free_area[3]也沒有空閒,則從更上一級申請空間,如果free_area[4]中有,就將這16(2*2*2*2)個頁面等分成兩份,前一半掛在free_area[3]的連結串列頭部,後一半的8個頁等分成兩等分,前一半掛free_area[2]的連結串列中,後一半分配出去。依次遞推,直到free_area[max_order],如果頂級都沒有空間,那麼就報告分配失敗。

 

夥伴演算法記憶體合併:

當用戶用完記憶體後會歸還,然後根據該記憶體塊實際大小(向上取整為2的冪)歸入連結串列中,在歸入之前,

1.我們還要檢測他的夥伴記憶體塊是否空閒,

2.如果空閒就合併在一起,合併後轉到1,繼續執行。

3.若果不是空閒的就直接歸入連結串列中。

將大的頁面塊打碎進行分配將增加系統中零碎空閒頁面塊的數目。頁面回收程式碼在適當時機下要將這些頁面結合起來形成單一大頁面塊。事實上頁面塊大小決定了頁面重新組合的難易程度。

當頁面塊被釋放時,程式碼將檢查是否有相同大小的相鄰或者buddy記憶體塊存在。如果有,則將它們結合起來形成一個大小為原來兩倍的新空閒塊。每次結合完之後,程式碼還要檢查是否可以繼續合併成更大的頁面。最佳情況是系統的空閒頁面塊將和允許分配的最大記憶體一樣大。

注意:在Buddy System進行合併時,只會將互為夥伴的兩個儲存塊合併成大塊,也就是說如果有兩個儲存塊大小相同,地址也相鄰,但是不是由同一個大塊分裂出來的,也不會被合併起來。

一般來說,夥伴演算法實現中會用點陣圖記錄記憶體塊是否被使用,用於夥伴記憶體的合併。

再比如如圖所示,釋放是申請的逆過程,也可以看作是夥伴的合併過程。當釋放一個記憶體塊時,先在其對於的free_area連結串列中查詢是否有夥伴存在,如果沒有夥伴塊,直接將釋放的塊插入連結串列頭。如果有或板塊的存在,則將其從連結串列摘下,合併成一個大塊,然後繼續查詢合併後的塊在更大一級連結串列中是否有夥伴的存在,直至不能合併或者已經合併至最大塊2^10為止。核心試圖將大小為b的一對空閒塊(一個是現有空閒連結串列上的,一個是待回收的),合併為一個大小為2b的單獨塊,如果它成功合併所釋放的塊,它會試圖合併2b大小的塊。

 

夥伴演算法的特點:

顯而易見,夥伴演算法會浪費大量的記憶體,(如果需要大小為9的記憶體塊必須分配大小為16的記憶體塊)。而優點也是明顯的,可以解決實體記憶體的碎片化,分配和合並演算法都很簡單易行。但是,當分配和回收較快的時候,例如分配大小為9的記憶體塊,此時分配16,然後又回收,即合併夥伴記憶體塊,這樣會造成不必要的cpu浪費,應該設定連結串列中記憶體塊的低潮個數,即當連結串列中記憶體塊個數小於某個值的時候,並不合併夥伴記憶體塊,只要當高於低潮個數的時候才合併。

 

Buddy演算法的優缺點:

優點:

  1,較好的解決外部碎片問題

   2,當需要分配若干個記憶體頁面時,用於DMA的記憶體頁面必須連續,夥伴演算法很好的滿足了這個要求

  3,只要請求的塊不超過512個頁面(2K),核心就儘量分配連續的頁面。

  4,針對大記憶體分配設計。

缺點:

   1,合併的要求有點嚴格,只能是滿足夥伴關係的塊才能合併,比如第1塊和第2塊就不能合併。

  2, 碎片問題:一個連續的記憶體中僅僅一個頁面被佔用,導致整塊記憶體區都不具備合併的條件

  3,浪費問題:夥伴演算法只能分配2的冪次方記憶體區,當需要8K(2頁)時,這個好解決,當需要9K時,那就需要分配16K(4頁)的記憶體空間,但是實際只用到9K空間,多餘的7K空間就被浪費掉。