自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

聊聊磁盤文件系統(tǒng)(二)

存儲 存儲設(shè)備
如果一個(gè)文件比較大,inode的塊號不足以標(biāo)識所有的數(shù)據(jù)塊,就會使用間接塊。文件系統(tǒng)會在硬盤上分配一個(gè)數(shù)據(jù)塊,不存儲文件數(shù)據(jù),專門用來存儲塊號。

[[406814]]

數(shù)據(jù)的存放

  • 在 ext2 和 ext3 中,其中前 12 項(xiàng)直接保存了塊的位置,也就是說,我們可以通過 i_block[0-11],直接得到保存文件內(nèi)容的塊。

但是,如果一個(gè)文件比較大,inode的塊號不足以標(biāo)識所有的數(shù)據(jù)塊,就會使用間接塊。文件系統(tǒng)會在硬盤上分配一個(gè)數(shù)據(jù)塊,不存儲文件數(shù)據(jù),專門用來存儲塊號。該塊被稱為間接塊。inode的長度是固定的。間接塊占用的空間對于大文件來說是必要的。但是對于小文件不會帶來額外的開銷。當(dāng)我們用到 i_block[12]的時(shí)候,就不能直接放數(shù)據(jù)塊的位置了,要不然 i_block 很快就會用完了。這該怎么辦呢?我們需要想個(gè)辦法。我們可以讓 i_block[12]指向間接塊。也就是說,我們在 i_block[12]里面放間接塊的位置,通過 i_block[12]找到間接塊后,間接塊里面放數(shù)據(jù)塊的位置,通過間接塊可以找到數(shù)據(jù)塊。如果文件再大一些,i_block[13]會指向一個(gè)塊,我們可以用二次間接塊。二次間接塊里面存放了間接塊的位置,間接塊里面存放了數(shù)據(jù)塊的位置,數(shù)據(jù)塊里面存放的是真正的數(shù)據(jù)。如果文件再大一些,i_block[14]會指向三次間接塊。

  • ext4文件系統(tǒng)的Extents一棵樹:

解釋一下 Extents。比方說,一個(gè)文件大小為 128M,如果使用 4k 大小的塊進(jìn)行存儲,需要 32k 個(gè)塊。Extents 可以用于存放連續(xù)的塊,也就是說,我們可以把 128M 放在一個(gè) Extents 里面。這樣的話,對大文件的讀寫性能提高了,文件碎片也減少了。如下圖所示:

索引節(jié)點(diǎn)區(qū),用來存儲索引節(jié)點(diǎn)。Inode存儲了文件系統(tǒng)對象的一些元信息,如所有者、訪問權(quán)限(讀、寫、執(zhí)行)、類型(是文件還是目錄)、內(nèi)容修改時(shí)間、inode修改時(shí)間、上次訪問時(shí)間、對應(yīng)的文件系統(tǒng)存儲塊的地址,等等。知道了1個(gè)文件的inode號碼,就可以在inode元數(shù)據(jù)中查出文件內(nèi)容數(shù)據(jù)的存儲地址。對于EXT4的默認(rèn)情況,一個(gè)inode的大小是256字節(jié),inode是EXT4最重要的元數(shù)據(jù)信息。注意Inode沒有文件名稱,將在下文中講述。

  1. struct ext4_inode { 
  2.  __le16 i_mode;  /* File mode */ 
  3.  __le16 i_uid;  /* Low 16 bits of Owner Uid */ 
  4.  __le32 i_size_lo; /* Size in bytes */ 
  5.  __le32 i_atime; /* Access time */ 
  6.  __le32 i_ctime; /* Inode Change time */ 
  7.  __le32 i_mtime; /* Modification time */ 
  8.  __le32 i_dtime; /* Deletion Time */ 
  9.  __le16 i_gid;  /* Low 16 bits of Group Id */ 
  10.  __le16 i_links_count; /* Links count */ 
  11.  __le32 i_blocks_lo; /* Blocks count */ 
  12.  __le32 i_flags; /* File flags */ 
  13.  union { 
  14.   struct { 
  15.    __le32  l_i_version; 
  16.   } linux1; 
  17.   struct { 
  18.    __u32  h_i_translator; 
  19.   } hurd1; 
  20.   struct { 
  21.    __u32  m_i_reserved1; 
  22.   } masix1; 
  23.  } osd1;    /* OS dependent 1 */ 
  24.  __le32 i_block[EXT4_N_BLOCKS];/* Pointers to blocks */ 
  25.  __le32 i_generation; /* File version (for NFS) */ 
  26.  __le32 i_file_acl_lo; /* File ACL */ 
  27.  __le32 i_size_high; 
  28.  __le32 i_obso_faddr; /* Obsoleted fragment address */ 
  29.  union { 
  30.   struct { 
  31.    __le16 l_i_blocks_high; /* were l_i_reserved1 */ 
  32.    __le16 l_i_file_acl_high; 
  33.    __le16 l_i_uid_high; /* these 2 fields */ 
  34.    __le16 l_i_gid_high; /* were reserved2[0] */ 
  35.    __le16 l_i_checksum_lo;/* crc32c(uuid+inum+inode) LE */ 
  36.    __le16 l_i_reserved; 
  37.   } linux2; 
  38.   struct { 
  39.    __le16 h_i_reserved1; /* Obsoleted fragment number/size which are removed in ext4 */ 
  40.    __u16 h_i_mode_high; 
  41.    __u16 h_i_uid_high; 
  42.    __u16 h_i_gid_high; 
  43.    __u32 h_i_author; 
  44.   } hurd2; 
  45.   struct { 
  46.    __le16 h_i_reserved1; /* Obsoleted fragment number/size which are removed in ext4 */ 
  47.    __le16 m_i_file_acl_high; 
  48.    __u32 m_i_reserved2[2]; 
  49.   } masix2; 
  50.  } osd2;    /* OS dependent 2 */ 
  51.  __le16 i_extra_isize; 
  52.  __le16 i_checksum_hi; /* crc32c(uuid+inum+inode) BE */ 
  53.  __le32  i_ctime_extra;  /* extra Change time      (nsec << 2 | epoch) */ 
  54.  __le32  i_mtime_extra;  /* extra Modification time(nsec << 2 | epoch) */ 
  55.  __le32  i_atime_extra;  /* extra Access time      (nsec << 2 | epoch) */ 
  56.  __le32  i_crtime;       /* File Creation time */ 
  57.  __le32  i_crtime_extra; /* extra FileCreationtime (nsec << 2 | epoch) */ 
  58.  __le32  i_version_hi; /* high 32 bits for 64-bit version */ 
  59.  __le32 i_projid; /* Project ID */ 
  60. }; 

普通文件的存儲格式

數(shù)據(jù)塊區(qū),則用來存儲文件數(shù)據(jù)。i_block,我們來看看EXT4_N_BLOCKS的具體定義:

  1. #define  EXT4_NDIR_BLOCKS    12 
  2. #define  EXT4_IND_BLOCK      EXT4_NDIR_BLOCKS 
  3. #define  EXT4_DIND_BLOCK      (EXT4_IND_BLOCK + 1) 
  4. #define  EXT4_TIND_BLOCK      (EXT4_DIND_BLOCK + 1) 
  5. #define  EXT4_N_BLOCKS      (EXT4_TIND_BLOCK + 1) 

inode 里面的 i_block 中,可以放得下一個(gè) ext4_extent_header 和 4 項(xiàng) ext4_extent。

  1. struct ext4_extent_header {  
  2.     __le16 eh_magic; /* probably will support different formats */  
  3.      
  4.     __le16 eh_entries; /* number of valid entries */  
  5.                             
  6.     __le16 eh_max; /* capacity of store in entries */  
  7.                            
  8.     __le16 eh_depth; /* has tree real underlying blocks? */  
  9.                             
  10.     __le32 eh_generation; /* generation of the tree */ 
  11. }; 
  1. /* 
  2.  * This is the extent on-disk structure. 
  3.  * It's used at the bottom of the tree. 
  4.  */ 
  5. struct ext4_extent { 
  6.   __le32  ee_block;  /* first logical block extent covers */ 
  7.   __le16  ee_len;    /* number of blocks covered by extent */ 
  8.   __le16  ee_start_hi;  /* high 16 bits of physical block */ 
  9.   __le32  ee_start_lo;  /* low 32 bits of physical block */ 
  10. }; 
  11. /* 
  12.  * This is index on-disk structure. 
  13.  * It's used at all the levels except the bottom. 
  14.  */ 
  15. struct ext4_extent_idx { 
  16.   __le32  ei_block;  /* index covers logical blocks from 'block' */ 
  17.   __le32  ei_leaf_lo;  /* pointer to the physical block of the next * 
  18.          * level. leaf or next index could be there */ 
  19.   __le16  ei_leaf_hi;  /* high 16 bits of physical block */ 
  20.   __u16  ei_unused; 
  21. }; 

如果文件不大,inode 里面的 i_block 中,可以放得下一個(gè) ext4_extent_header 和 4 項(xiàng) ext4_extent。所以這個(gè)時(shí)候,eh_depth 為 0,也即 inode 里面的就是葉子節(jié)點(diǎn),樹高度為 0。如果文件比較大,4 個(gè) extent 放不下,就要分裂成為一棵樹,eh_depth>0 的節(jié)點(diǎn)就是索引節(jié)點(diǎn),其中根節(jié)點(diǎn)深度最大,在 inode 中。最底層 eh_depth=0 的是葉子節(jié)點(diǎn)。

目錄與文件名的存儲格式

目錄下文件比較少的情況下:目錄本身也是個(gè)文件,也有 inode。inode 里面也是指向一些塊。和普通文件不同的是,普通文件的塊里面保存的是文件數(shù)據(jù),而目錄文件的塊里面保存的是目錄里面一項(xiàng)一項(xiàng)的文件信息。這些信息我們稱為 ext4_dir_entry。從代碼來看,有兩個(gè)版本,在成員來講幾乎沒有差別,只不過第二個(gè)版本 ext4_dir_entry_2 是將一個(gè) 16 位的 name_len,變成了一個(gè) 8 位的 name_len 和 8 位的 file_type。即該目錄項(xiàng)的數(shù)據(jù)所在inode編號、文件名長度與類型、文件名字三部分組成。

  1. struct ext4_dir_entry { 
  2.   __le32  inode;      /* Inode number */ 
  3.   __le16  rec_len;    /* Directory entry length */ 
  4.   __le16  name_len;    /* Name length */ 
  5.   char  name[EXT4_NAME_LEN];  /* File name */ 
  6. }; 
  7. struct ext4_dir_entry_2 { 
  8.   __le32  inode;      /* Inode number */ 
  9.   __le16  rec_len;    /* Directory entry length */ 
  10.   __u8  name_len;    /* Name length */ 
  11.   __u8  file_type;   /* File type */ 
  12.   char  name[EXT4_NAME_LEN];  /* File name */ 
  13. }; 

file_type指定了目錄項(xiàng)的類型。改變量的可能值,由以下枚舉類型定義:

  1. enum{ 
  2.     EXT4_FT_UNKNOWN, 
  3.     EXT4_FT_REG_FILE, 
  4.     EXT4_FT_DIR, 
  5.     EXT4_FT_CHRDEV, 
  6.     EXT4_FT_BLKDEV, 
  7.     EXT4_FT_FIFO, 
  8.     EXT4_FT_SOCK, 
  9.     EXT4_FT_SYMLINK, 
  10.     EXT4_FT_MAX 

ls列出的目錄內(nèi)容如下:

  1. [root@localhost ~]# ls -la 
  2. 總用量 37536 
  3. dr-xr-x---.  7 root root     4096 5月  26 16:54 . 
  4. dr-xr-xr-x. 19 root root      288 6月  10 14:51 .. 
  5. -rw-------.  1 root root     1260 1月  11 2014 anaconda-ks.cfg 

每一項(xiàng)都會保存這個(gè)目錄的下一級的文件的文件名和對應(yīng)的 inode,通過這個(gè) inode,就能找到真正的文件。第一項(xiàng)是“.”,表示當(dāng)前目錄,第二項(xiàng)是“..”,表示上一級目錄,接下來就是一項(xiàng)一項(xiàng)的文件名和 inode。**目錄下文件比較多的情況下:如果一個(gè)目錄下有幾萬幾十萬個(gè)條目,這個(gè)方法就比較慢了。原因在于線性掃描,而且,1個(gè)block(4096字節(jié)),基本只能放下幾十~200個(gè)條目,一旦需要幾十幾百個(gè)block,那么為了獲取子文件的inode,這個(gè)DISK IO的消耗是不能忍受的。因此開發(fā)了dir_index的功能。dir_index使用dx_entry來對目錄文件的block進(jìn)行管理,一個(gè)dx_entry對象對應(yīng)一個(gè)block。dx_entry.hash記錄的是其對應(yīng)block內(nèi)所有目錄項(xiàng)的最小hash值,dx_entry.block記錄的是目錄文件的邏輯塊號。從/etc/mke2fs.conf中也可以看出,這個(gè)是格式化文件系統(tǒng)的默認(rèn)選項(xiàng):

  1. [defaults] 
  2.     base_features = sparse_super,filetype,resize_inode,dir_index,ext_attr 
  3.     default_mntopts = acl,user_xattr 
  4.     enable_periodic_fsck = 0 
  5.     blocksize = 4096 
  6.     inode_size = 256 
  7.     inode_ratio = 16384 
  8.  
  9. [fs_types] 
  10.     ext3 = { 
  11.         features = has_journal 
  12.     } 
  13.     ext4 = { 
  14.         features = has_journal,extent,huge_file,flex_bg,uninit_bg,dir_nlink,extra_isize 
  15.         auto_64-bit_support = 1 
  16.         inode_size = 256 
  17.     } 

如果在 inode 中設(shè)置 dir_index 標(biāo)志,則目錄文件的塊的組織形式將發(fā)生變化,變成了下面定義的這個(gè)樣子:

  1. struct dx_root 
  2.   struct fake_dirent dot; 
  3.   char dot_name[4]; 
  4.   struct fake_dirent dotdot; 
  5.   char dotdot_name[4]; 
  6.   struct dx_root_info 
  7.   { 
  8.     __le32 reserved_zero; 
  9.     u8 hash_version; 
  10.     u8 info_length; /* 8 */ 
  11.     u8 indirect_levels; 
  12.     u8 unused_flags; 
  13.   } 
  14.   info; 
  15.   struct dx_entry  entries[0]; 
  16. }; 

當(dāng)然,首先出現(xiàn)的還是差不多的,第一項(xiàng)是“.”,表示當(dāng)前目錄;第二項(xiàng)是“..”,表示上一級目錄,這兩個(gè)不變。接下來就開始發(fā)生改變了。是一個(gè) dx_root_info 的結(jié)構(gòu),其中最重要的成員變量是 indirect_levels,表示間接索引的層數(shù)。接下來我們來看索引項(xiàng) dx_entry。這個(gè)也很簡單,其實(shí)就是文件名的哈希值和數(shù)據(jù)塊的一個(gè)映射關(guān)系。

  1. struct dx_entry 
  2.   __le32 hash; 
  3.   __le32 block; 
  4. }; 

那么,找到一個(gè)子文件需要如下步驟。1)根據(jù)待查找子文件名計(jì)算出hash值 2)在當(dāng)前的全部dx_entry中采用二分查找的方式找到對應(yīng)的dx_entry 3)根據(jù)dx_entry.block記錄值讀取目錄文件對應(yīng)的邏輯塊內(nèi)容 4)在讀取到的block內(nèi)容中遍歷查找匹配的子文件目錄項(xiàng) 不難發(fā)現(xiàn),之前的需要讀取N + 1個(gè)block的困境被簡化為只需要讀取一個(gè)block的內(nèi)容即可,問題迎刃而解

為了表示圖中上半部分的那個(gè)簡單的樹形結(jié)構(gòu),在文件系統(tǒng)上的布局就像圖的下半部分一樣。無論是文件夾還是文件,都有一個(gè) inode。inode 里面會指向數(shù)據(jù)塊,對于文件夾的數(shù)據(jù)塊,里面是一個(gè)表,是下一層的文件名和 inode 的對應(yīng)關(guān)系,文件的數(shù)據(jù)塊里面存放的才是真正的數(shù)據(jù)。

ext類文件系統(tǒng)的缺點(diǎn)

最大的缺點(diǎn)是它在創(chuàng)建文件系統(tǒng)的時(shí)候就劃分好一切需要?jiǎng)澐值臇|西,以后用到的時(shí)候可以直接進(jìn)行分配,也就是說它不支持動態(tài)劃分和動態(tài)分配。對于較小的分區(qū)來說速度還好,但是對于一個(gè)超大的磁盤,速度是極慢極慢的。例如將一個(gè)幾十T的磁盤陣列格式化為ext4文件系統(tǒng),可能你會因此而失去一切耐心。除了格式化速度超慢以外,ext4文件系統(tǒng)還是非??扇〉摹.?dāng)然,不同公司開發(fā)的文件系統(tǒng)都各有特色,最主要的還是根據(jù)需求選擇合適的文件系統(tǒng)類型。

本文轉(zhuǎn)載自微信公眾號「運(yùn)維開發(fā)故事」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系運(yùn)維開發(fā)故事公眾號。

 

責(zé)任編輯:姜華 來源: 運(yùn)維開發(fā)故事
相關(guān)推薦

2021-06-21 14:52:45

磁盤機(jī)械磁盤固態(tài)磁盤

2021-06-28 06:24:12

磁盤存儲VFS

2018-01-10 12:42:09

Linux磁盤文件系統(tǒng)

2009-10-12 11:14:51

LinuxLinux磁盤文件系統(tǒng)管理

2021-06-29 07:47:22

文件系統(tǒng)磁盤

2016-12-27 10:48:59

Linux命令磁盤與文件系統(tǒng)

2010-03-02 15:09:26

Linux mount

2010-04-07 18:42:42

Unix命令

2009-10-13 14:31:26

:Linux系統(tǒng)磁盤系統(tǒng)管理

2020-07-22 14:53:06

Linux系統(tǒng)虛擬文件

2017-08-17 10:03:06

磁盤系統(tǒng)實(shí)例

2010-03-05 17:43:00

Linux XFS文件

2024-03-11 10:30:31

Linux文件系統(tǒng)

2011-01-13 14:10:30

Linux文件系統(tǒng)

2023-09-03 17:09:58

LinuxSAN LUN磁盤

2023-09-05 15:17:48

LinuxLUN磁盤

2020-07-28 08:00:03

存儲數(shù)據(jù)技術(shù)

2018-08-24 10:10:25

Linux文件系統(tǒng)技術(shù)

2019-09-20 10:04:45

Linux系統(tǒng)虛擬文件

2021-06-06 16:55:22

Linux文件系統(tǒng)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號