戲說文件系統之ext2【上】

    前面說過,虛擬文件系統VFS是對各種文件系統的一個抽象層,抽取其共性,以便對外提供統一管理接口,便於內核對不同種類的文件系統進行管理。那麼首先我們得看一下對於一個具體的文件系統,我們該關注重點在哪裏。

     對於存儲設備(以硬盤爲例)上的數據,可分爲兩部分:
     用戶數據:存儲用戶實際數據的部分;
     管理數據:用於管理這些數據的部分,這部分我們通常叫它元數據(metadata)
     我們今天要討論的就是這些元數據。這裏有個概念首先需要明確一下:塊設備。所謂塊設備就是以塊爲基本讀寫單位的設備,支持緩衝和隨機訪問。每個文件系統提供的mk2fs.xx工具都支持在構建文件系統時由用戶指定塊大小,當然用戶不指定時會有一個缺省值。在博文“硬盤的存儲原理和內部架構”裏我們提到過,硬盤的每個扇區512字節,而多個相鄰的若干扇區就構成了一個簇,從文件系統的角度看這個簇對應的就是我們這裏所說塊。用戶從上層下發的數據首先被緩存在塊設備的緩存裏,當寫滿一個塊時數據纔會被髮給硬盤驅動程序將數據最終寫到存儲介質上。如果想將設備緩存中數據立即寫到存儲介質上可以通過sync命令來完成。上一篇博文裏我們也說,塊越大存儲性能越好,但浪費比較嚴重;塊越小空間利用率較高,但性能相對較低。如果你不是專業的“骨灰級”玩兒家,在存儲設備上構建文件系統時,塊大小就用默認值。通過命令“tune2fs -l /dev/sda1”可以查看該存儲設備上文件系統所使用的塊大小:

點擊(此處)摺疊或打開

  1. [root@localhost ~]# tune2fs -l /dev/sda1
  2. tune2fs 1.39 (29-May-2006)
  3. Filesystem volume name: /boot
  4. Last mounted on:
  5. Filesystem UUID: 6ade5e49-ddab-4bf1-9a45-a0a742995775
  6. Filesystem magic number: 0xEF53
  7. Filesystem revision #: 1 (dynamic)
  8. Filesystem features: has_journal ext_attr resize_inode dir_index filetype needs_recovery sparse_super
  9. Default mount options: user_xattr acl
  10. Filesystem state: clean
  11. Errors behavior: Continue
  12. Filesystem OS type: Linux
  13. Inode count: 38152
  14. Block count: 152584
  15. Reserved block count: 7629
  16. Free blocks: 130852
  17. Free inodes: 38111
  18. First block: 1
  19. Block size: 1024
  20. Fragment size: 1024
  21. Reserved GDT blocks: 256
  22. Blocks per group: 8192
  23. Fragments per group: 8192
  24. Inodes per group: 2008
  25. Inode blocks per group: 251
  26. Filesystem created: Thu Dec 13 00:42:52 2012
  27. Last mount time: Tue Nov 20 10:35:28 2012
  28. Last write time: Tue Nov 20 10:35:28 2012
  29. Mount count: 12
  30. Maximum mount count: -1
  31. Last checked: Thu Dec 13 00:42:52 2012
  32. Check interval: 0 ()
  33. Reserved blocks uid: 0 (user root)
  34. Reserved blocks gid: 0 (group root)
  35. First inode: 11
  36. Inode size: 128
  37. Journal inode: 8
  38. Default directory hash: tea
  39. Directory Hash Seed: 72070587-1b60-42de-bd8b-a7b7eb7cbe63
  40. Journal backup: inode blocks
    該命令已經暴露了文件系統的很多信息,接下我們將詳細分析它們。
     下圖是我的虛擬機的情況,三塊IDE的硬盤。容量分別是:

      hda: 37580963840/(1024*1024*1024)=35GB

      hdb: 8589934592/(1024*1024*1024)=8GB

      hdd: 8589934592/(1024*1024*1024)=8GB

23069658_1357743586lfPN.jpg

     如果這是三塊實際的物理硬盤的話,廠家所標稱的容量就分別是37.5GB8.5GB8.5GB可能有些童鞋覺得虛擬機有點“假”,那麼我就來看看實際硬盤到底是個啥樣子。

     主角1:西部數據 500G SATA接口 CentOS 5.5

     實際容量:500107862016B = 465.7GB

23069658_1357743764MMbQ.jpg

     主角2:希捷 160G  SCSI接口 CentOS 5.5

     實際容量:160041885696B=149GB

23069658_1357743767dXMM.jpg

    大家可以看到,VMware公司的水平還是相當不錯的,虛擬硬盤和物理硬盤“根本”看不出差別,畢竟屬於雲平臺基礎架構支撐者的風雲人物嘛。

   以硬盤/dev/hdd1爲例,它是我新增的一塊新盤,格式化成ext2後,根目錄下只有一個lost+found目錄,讓我們來看一下它的佈局情況,以此來開始我們的文件系統之旅。

23069658_1357744015AGlb.jpg

     對於使用了ext2文件系統的分區來說,superblock的大小爲1024字節,其實ext3superblock也是1024字節。下面的小程序可以證明這一點:

點擊(此處)摺疊或打開

  1. #include <stdio.h>
  2. #include <linux/ext2_fs.h>
  3. #include <linux/ext3_fs.h>

  4. int main(int argc,char** argv){
  5.     printf("sizeof of ext2 superblock=%d\n",sizeof(struct ext2_super_block));
  6.     printf("sizeof of ext3 superblock=%d\n",sizeof(struct ext3_super_block));
  7.         return 0;
  8. }
     輸出結果如下:

23069658_13577440187Zvj.jpg

     硬盤的第一個字節是從0開始編號,所以第一個字節是byte0,以此類推。/dev/hdd1分區頭部的1024個字節(byte0~byte1023)都用0填充,因爲/dev/hdd1不是主引導盤。superblock是從byte1024開始,佔1024B存儲空間。我們用dd命令把superblock的信息提取出來:

點擊(此處)摺疊或打開

  1. dd if=/dev/hdd1 of=./hdd1sb bs=1024 skip=1 count=1
     上述命令將從/dev/hdd1分區的byte1024處開始,提取1024個字節的數據存儲到當前目錄下的hdd1sb文件裏,該文件裏就存儲了我們superblock的所有信息,如下:

23069658_1357744187wPMt.jpg

     上面的程序稍加改造,我們就可以以更直觀的方式看到superblock的輸出了:

點擊(此處)摺疊或打開

  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/stat.h>
  4. #include <fcntl.h>
  5. #include <unistd.h>
  6. #include <string.h>
  7. #include <linux/ext2_fs.h>
  8. #include <linux/ext3_fs.h>

  9. int main(int argc,char** argv){
  10.     printf("sizeof of ext2 superblock=%d\n",sizeof(struct ext2_super_block));
  11.     printf("sizeof of ext3 superblock=%d\n",sizeof(struct ext3_super_block));
  12.     char buf[1024] = {0};
  13.     int fd = -1;
  14.     struct ext2_super_block hdd1sb;
  15.     memset(&hdd1sb,0,1024);

  16.     if(-1 == (fd=open("./hdd1sb",O_RDONLY,0777))){
  17.         printf("open file error!\n");
  18.         return 1;
  19.     }

  20.     if(-1 == read(fd,buf,1024)){
  21.         printf("read error!\n");
  22.         close(fd);
  23.         return 1;
  24.     }

  25.     memcpy((char*)&hdd1sb,buf,1024);
  26.     printf("inode count : %ld\n",hdd1sb.s_inodes_count);
  27.     printf("block count : %ld\n",hdd1sb.s_blocks_count);
  28.     printf("Reserved blocks count : %ld\n",hdd1sb.s_r_blocks_count);
  29.     printf("Free blocks count : %ld\n",hdd1sb.s_free_blocks_count);
  30.     printf("Free inodes count : %ld\n",hdd1sb.s_free_inodes_count);
  31.     printf("First Data Block : %ld\n",hdd1sb.s_first_data_block);
  32.     printf("Block size : %ld\n",1<<(hdd1sb.s_log_block_size+10));
  33.     printf("Fragment size : %ld\n",1<<(hdd1sb.s_log_frag_size+10));
  34.     printf("Blocks per group : %ld\n",hdd1sb.s_blocks_per_group);
  35.     printf("Fragments per group : %ld\n",hdd1sb.s_frags_per_group);
  36.     printf("Inodes per group : %ld\n",hdd1sb.s_inodes_per_group);
  37.     printf("Magic signature : 0x%x\n",hdd1sb.s_magic);
  38.     printf("size of inode structure : %d\n",hdd1sb.s_inode_size);
  39.     close(fd);
  40.     return 0;
  41. }
     打印結果如下:

23069658_13577441908z3B.jpg

     對於ext2/ext3文件系統來說數字簽名Magic signature都是0xef53,如果不是那麼它一定不是ext2/ext3文件系統。這裏我們可以看到,我們的/dev/hdd1確實是ext2文件系統類型。hdd1中一共包含1048576inode節點(inode編號從1開始),每個inode節點大小爲128字節,所有inode消耗的存儲空間是1048576×128=128MB;總共包含2097065block,每個block大小爲4096字節,每32768block組成一個group,所以一共有2097065/32768=63.99,即64group(group編號從0開始,即Group0Group63) 所以整個/dev/hdd1被劃分成了64group,詳情如下:

23069658_1357744369C5DG.jpg

     用命令tune2fs可以驗證我們之前的分析:

23069658_1357744401Q9mR.jpg

     再通過命令dumpe2fs /dev/hdd1的輸出,可以得到我們關注如下部分:

23069658_1357744406Mvto.jpg

     接下來以Group0爲例,主superblockGroup0block0裏,根據前面的分析,我們可以畫出主superblockblock0中的位置如下:

23069658_1357744525tOTv.jpg

     因爲superblock是如此之重要,一旦它出錯你的整個系統就玩兒完了,所以文件系統中會存在磁盤的多個不同位置會存在主superblock的備份副本,一旦系統出問題後還可以通過備份的superblock對文件系統進行修復。第一版ext2文件系統的實現裏,每個Group裏都存在一份superblock的副本,然而這樣做的負面效果也是相當明顯,那就是嚴重降低了磁盤的空間利用率。所以在後續ext2的實現代碼中,選擇用於備份superblockGroup組號的原則是3N5N7N其中N=0,1,2,3…。根據這個公式我們來計算一下/dev/hdd1中備份有supeblockGroup號:

23069658_1357744586IMD2.jpg

     也就是說Group13579252749裏都保存有superblock的拷貝,如下:

23069658_1357744411KGbg.jpg

     用block號分別除以32768就得到了備份superblockGroup號,和我們在上面看到的結果一致。我們來看一下/dev/hdd1中Group和block的關係:

23069658_1357745026Y4l0.jpg

     從上圖中我們可以清晰地看出在使用了ext2文件系統的分區上,包含有主superblockGroup、備份superblockGroup以及沒有備份superblockGroup的佈局情況。存儲了superblockGroup中有一個組描述符(Group descriptors)緊跟在superblock所在的block後面,佔一個block大小;同時還有個Reserved GDT跟在組描述符的後面。

     Reserved GDT的存在主要是支持ext2文件系統的resize功能,它有自己的inodedata block,這樣一來如果文件系統動態增大,Reserved GDT就正好可以騰出一部分空間讓Group descriptor向下擴展。

     未完,待續...

<script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];</script>
閱讀(4188) | 評論(8) | 轉發(16) |
給主人留下些什麼吧!~~
19_avatar_small.jpg

shangbaogen2013-01-15 09:38:35

相當通俗易懂,學習啦,希望樓主多寫幾篇這樣的博文啊!1.gif

83_avatar_small.jpg

ayuan21672013-01-11 15:14:09

學習了

58_avatar_small.jpg

wjlkoorey2582013-01-10 19:02:32

lmnos: Bean_lee 和 wjlkoorey258 再接再勵,多寫點這樣的好博文.....
我這個“好”字受之有愧啊,只能算是初級入門類的科普讀物而已。真正要鑽研深入還得向Bean_lee學習啊。
58_avatar_small.jpg

wjlkoorey2582013-01-10 19:00:28

Bean_lee: 有時間我也寫幾篇你寫過的東西,也搶下你的生意,呵呵。.....
哈哈,不存在。學術交流,學術交流5.gif
58_avatar_small.jpg

wjlkoorey2582013-01-10 18:59:52

Bean_lee: 搶我生意啊,兄弟,呵呵。
我12年寫了ext2 幾篇文章,供你參考。.....
呵呵,我後知後覺了。不過我現在還達不到Bean_lee兄的境界,只能先做些表面功夫了4.gif。所以還請兄臺爲我的文章把把脈,免得兄弟我弄出笑話了,哈哈
評論熱議
發佈了87 篇原創文章 · 獲贊 14 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章