天天看點

我所認識的EXT2(一)

前言:

本文是筆者自己在學習檔案系統中的一些體會,寫出來和大家分享一下。本文首先是介紹了下檔案系統的一些理論概念,然後分析了ext2檔案系統的原理和部分源碼。

檔案系統是什麼:

         人們在認識一件陌生事物時一開始總是從事物的定義、作用和結構入手的。那麼首先檔案系統的定義是什麼呢?從網上抓下來的:“檔案系統是作業系統用于明确磁盤或分區上的檔案的方法和資料結構;即在磁盤上組織檔案的方法。也指用于存儲檔案的磁盤或分區,或檔案系統種類。”簡單地說檔案系統就是在磁盤上組織檔案的方法。

可是為什麼需要用檔案系統呢?也許朋友們會這麼認為:磁盤是自己的,想怎麼放檔案就怎麼放,還要弄個檔案系統來管多麻煩啊!那讓我們來打個比方好了,比如你花了100萬買了一套100平米的房子,如果沒有一個預先規劃,今天在廚房放個洗衣機,明天在陽台放個雙人床,後天你就會發現新買的沖水馬桶隻能放在卧室了。。。當然這種局面是我們不希望看到了,為了避免這種情況的出現,我們在放東西之前就找一個能幫我們放東西的“管家”,一來這個“管家”知道東西怎麼放可以在100萬的房子裡放盡量多的“東西”,二來“管家”可以幫我們快速地找“東西”,不會發生在卧室找到沖水馬桶的尴尬。甚至這個有些“管家”還能在我們不小心“丢掉東西”以後還能幫我們找回來。而我們在放東西或取東西的時候隻需要委托“管家”,讓她幫我們實作放和找。将這個例子推演到檔案系統中就可以得知檔案系統(“管家”)的主要作用:一、優化磁盤空間使用率;二、提高磁盤查找資料的效率;三、能夠提供N多“增值”服務,如:磁盤恢複、壓縮、通路許可、磁盤配額等等,當然要視不同的檔案系統而言;四、由于我們是委托“管家”幫我們操作的,隻要接口統一,“管家”是可以更換的,這樣一來系統的耦合性就降低了。當然,我們要請到這個超有能力的“管家”是要付出代價的,首先,“管家”是需要住在你的“家裡的”,是以需要空間上的代價;然後,你要利用“管家”幫你“放東西”,當然需要去了解“管家”,是以需要時間上的代價;最後,還需要承擔“管家”做錯事的風險,因為如果檔案系統一旦出錯,有可能損失的将是整個磁盤的寶貴資料。

目前在各種作業系統中存在着各種各樣的檔案系統,在Windows平台主流的有:FAT,FAT16,FAT32,NTFS等,在Unix平台主流的有:EXT2、EXT3、EXT4等以及在應用CD光牒中的檔案系統有ISO-9660、UDFCD光牒檔案系統标準等。其實這裡列舉的還是檔案系統大家族的冰山一角,在“海水”下藏着的還有N多各種各樣的檔案系統。為什麼檔案系統要有這麼多呢?就搞一個通用的國際标準的難道不好嗎?這個願望是美好的,不過這種對于标準的定義正是各大利益方角力的焦點,正所謂“一流公司做标準,二流公司做技術,三流公司做生産”也就是這個道理。

那麼到底這些“管家”是如何在工作的呢?這麼多的“管家”在管理磁盤時都有自己的一套政策和方法,當然随之也帶來不同的優點和缺點,下面就以EXT2為例,分析下EXT2檔案系統的原理。

EXT2的原理:

EXT2是 GNU/Linux 系統中标準的檔案系統,其特點為存取檔案的性能極好,對于中小型的檔案更顯示出優勢,從EXT2、EXT3、EXT4的名稱定義中不能看出,EXT3和EXT4都是在EXT2的基礎上擴充出來的,EXT2才是本源,下面那就讓我們一起來探究下最本源的東西吧。

Ø  EXT2的結構

EXT2會将磁盤空間分為一個個BlockGroup(塊組),這些塊組的大小都是一緻的,每個磁盤空間中至少有一個塊組。大緻就相當于EXT2這個“管家”會把我們的家先分成一個個大小一緻的“房間”。如圖:

Boot Sector

(啟動塊)

BlockGroup 0 BlockGroup 1 。。。 BlockGroup N

圖1

當然你也許會問,磁盤不是已經有扇區了嗎?為什麼還要把磁盤再分成一個個塊呢?如圖:

我所認識的EXT2(一)

圖2(摘自百度百科http://baike.baidu.com/view/1298929.htm)

設想一下,如果沒有将磁盤分成一個個塊,而是直接将資料放在扇區中,這時我們來考慮這個場景使用者要寫和讀一個大小為N(假設磁盤的扇區大小也為N)的檔案,首先在寫的場景時,由于找不到這麼大的連續磁盤空間,就要先進行移動扇區,空出N個扇區用來存放這個檔案;在讀取這個檔案的時候,磁盤的尋道時間将會很慢,因為每次讀一個扇區的資料需要重複N次。那麼如果EXT2将磁盤分為一個個快來進行管理,這時情況将會怎麼樣呢?在寫的場景時,不需要進行移動操作,而是通過指針的方式,将檔案實體上分散地存儲到空閑的塊中。在讀的場景時,假設快的大小為磁盤扇區的4倍,就隻需花費1/4之一的時間來讀取這個大小為N的檔案。

這裡要出來一些概念上的東西,一、是兩個術語來定義這裡的磁盤扇區和塊:磁盤扇區=實體塊,塊=邏輯塊,下文中提到塊,隻要不是特殊說明的都是指邏輯塊;二、是同一檔案系統中的邏輯塊大小是一緻的(請注意前提是同一檔案系統中);三、不管資料多大,必須占據1個以上的邏輯塊,打個比方,如果一個檔案就隻有1k位元組,那麼這個檔案也必須占據一個邏輯塊(就算這個邏輯塊的大小為4K位元組,剩下的3K位元組将浪費了)。四、實體塊的大小是由硬體廠商決定的,目前主流的磁盤扇區大小為512位元組,注意:自 2009 年 12 月起,硬碟制造商開始引入使用 4096 位元組扇區的磁盤,而不是常見的 512 位元組扇區磁盤。關于大扇區的實際建議請檢視:http://www.ibm.com/developerworks/cn/linux/l-4kb-sector-disks/?ca=drs-tp4608,而邏輯塊的大小是可以由檔案系統決定的,但必須是實體塊的整數倍,那麼在EXT2中邏輯塊的大小的定義:

#define EXT2_MIN_BLOCK_SIZE                             1024

#define EXT2_MAX_BLOCK_SIZE                            4096

那麼邏輯塊能否比實體塊還小呢?理論上當然可以了(事實上在09年很多硬碟使用進階格式化技術後,邏輯塊很有可能是比事實上的實體塊要小),但是出于性能和可用上的考量,幾乎沒有這麼做的。

再來回過頭看邏輯塊的結構圖(圖1),這裡怎麼多出了一個Boot Sector呢?這個不是EXT2弄的,而是硬體廠商的規則,必須在一個磁盤空間的最前面留出1K位元組的空間作為啟動扇區,其實這個啟動扇區就相當于我們現實中的門牌号的作用。(關于啟動扇區的詳細解釋有興趣的話可以看看Wiki:http://en.wikipedia.org/wiki/Boot_sector )

當然隻是簡單地将磁盤空間分為一個個塊組還是不足以很好地進行管理的,于是EXT2會進一步地将每個塊組分成以下格式:

我所認識的EXT2(一)

圖3

Ÿ   SuperBlock(超級塊):這裡記錄整個檔案系統的資訊,包括:Block和Inode的大小、Block和Inode的數量、Block和Inode的已使用和未使用數量、檔案系統版本号、最近一次挂載時間等等,完整的資訊可以檢視核心源代碼:/usr/src/kernels/… /include/linux/ext2_fs.h。EXT2為了安全考量,會将超級塊備份到每個組塊的開頭(也就是說有多少個塊組就有多少個超級塊備份)。具體結構體如下:

struct ext2_super_block {

     __le32     s_inodes_count;            

     __le32     s_blocks_count;             

     __le32     s_r_blocks_count;          

     __le32     s_free_blocks_count;     

     __le32     s_free_inodes_count;     

     __le32     s_first_data_block;         

     __le32     s_log_block_size;           

     __le32     s_log_frag_size;             

     __le32     s_blocks_per_group;      

     __le32     s_frags_per_group;        

     __le32     s_inodes_per_group;     

     __le32     s_mtime;                        

     __le32     s_wtime;                        

     __le16     s_mnt_count;                 

     __le16     s_max_mnt_count;        

     __le16     s_magic;                   

     __le16     s_state;                    

     __le16     s_errors;                   

     __le16     s_minor_rev_level;        

     __le32     s_lastcheck;                  

     __le32     s_checkinterval;            

     __le32     s_creator_os;                

     __le32     s_rev_level;             

     __le16     s_def_resuid;          

     __le16     s_def_resgid;          

     __le32     s_first_ino;           

     __le16   s_inode_size;           

     __le16     s_block_group_nr;      

     __le32     s_feature_compat;      

     __le32     s_feature_incompat;      

     __le32     s_feature_ro_compat;      

     __u8     s_uuid[16];          

     char     s_volume_name[16];      

     char     s_last_mounted[64];      

     __le32     s_algorithm_usage_bitmap;

     __u8     s_prealloc_blocks;     

     __u8     s_prealloc_dir_blocks;     

     __u16     s_padding1;             

     ……

     __u32     s_reserved[190];     

};

此結構的翻譯摘自http://blog.csdn.net/yunsongice/archive/2010/06/22/5685562.aspx

通讀一下以上的結構體,有助于對超級塊的了解。

Ÿ   GDT:塊組描述表是由一個個塊組描述符組成的,有多少個塊組就有多少個,塊組描述符記錄了以下資訊:

struct ext2_group_desc

{

    __le32            bg_block_bitmap;                            

    __le32            bg_inode_bitmap;                           

    __le32            bg_inode_table;                  

    __le16            bg_free_blocks_count;   

    __le16            bg_free_inodes_count;  

    __le16            bg_used_dirs_count;       /* Directories count 本塊組的目錄數量/

    __le16            bg_pad;

    __le32            bg_reserved[3];

};

當然塊組描述表也是非常重要的,如果一旦損壞失去的将去整個塊組的資料,是以EXT2也會将塊組資訊拷貝到每個塊組的開頭(具體位置在超級塊之後)。

Ÿ   Block Bitmap:塊使用情況,關于塊的位圖,表示的就是塊是否被占用,簡單的真假關系:1為占用,0為空閑。

Ÿ   Inode Bitmap:索引節點使用情況,關于索引節點的位圖,表示的就是索引節點是否被占用,簡單的真假關系:1為占用,0為空閑。(關于索引節點的資訊将在下文詳述)

Ÿ   Inode Table:索引節點表

Ÿ   DataBlocks:真正存放資料的空間。

繼續閱讀