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

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

存儲 存儲設(shè)備
VFS采用了面向?qū)ο蟮脑O(shè)計思路,將一系列概念抽象出來作為對象而存在,它們包含數(shù)據(jù)的同時也包含了操作這些數(shù)據(jù)的方法。

[[407591]]

掛載到linux的VFS中

vfs對象

VFS采用了面向?qū)ο蟮脑O(shè)計思路,將一系列概念抽象出來作為對象而存在,它們包含數(shù)據(jù)的同時也包含了操作這些數(shù)據(jù)的方法。當(dāng)然,這些對象都只能用數(shù)據(jù)結(jié)構(gòu)來表示,而不可能超出C語言的范疇,不過即使在C++里面數(shù)據(jù)結(jié)構(gòu)和類的區(qū)別也僅僅在于類的成員默認私有,數(shù)據(jù)結(jié)構(gòu)的成員默認公有。VFS主要有如下4個對象類型。

(1)超級塊(struct super_block)。超級塊對象代表一個己安裝的文件系統(tǒng),存儲該文件系統(tǒng)的有關(guān)信息,比如文件系統(tǒng)的類型、大小、狀態(tài)等。對基于磁盤的文件系統(tǒng),這類對象通常存放在磁盤上的特定扇區(qū)。對于并非基于磁盤的文件系統(tǒng)(比如基于內(nèi)存的文件系統(tǒng)sysfs),它們會現(xiàn)場創(chuàng)建超級塊對象并將其保存在內(nèi)存中。

(2)索引節(jié)點(struct inode)。索引節(jié)點對象代表存儲設(shè)備上的一個實際的物理文件,存儲該文件的有關(guān)信息。Linux將文件的相關(guān)信息,比如訪問權(quán)限、大小、創(chuàng)建時間等信息,與文件本身區(qū)分開來。文件的相關(guān)信息又被稱為文件的元數(shù)據(jù)。

(3)目錄項(struct dentry)。目錄項對象描述了文件系統(tǒng)的層次結(jié)構(gòu),一個路徑的各個組成部分,不管是目錄(VFS將目錄當(dāng)作文件來處理)還是普通的文件,都是一個目錄項對象。比如,打開文件/home/test/test.c時,內(nèi)核將為目錄/、home、test和文件test.c都創(chuàng)建一個目錄項對象。

(4)文件(struct file)。文件對象代表已經(jīng)被進程打開的文件,主要用于建立進程和文件之間的對應(yīng)關(guān)系。它由open()系統(tǒng)調(diào)用創(chuàng)建,由close()系統(tǒng)調(diào)用銷毀,且僅當(dāng)進程訪問文件期間存在于內(nèi)存之中。同一個物理文件可能存在多個對應(yīng)的文件對象,但其對應(yīng)的索引節(jié)點對象卻是惟一的。

除了上述4個主要對象外,VFS還包含了其他很多對象,比如用于描述各種文件系統(tǒng)類型的struct file_system_type,用于描述文件系統(tǒng)安裝點的struct vfsmount等。

VFS各個對象間的關(guān)系不是孤立的,進程描述符的files字段記錄了進程打開的所有文件,這些文件的文件對象指針保存在struct file_struct的fd_array數(shù)組里。通過文件的file對象可以獲得它對應(yīng)的目錄項對象,再由目錄項對象的d_inode字段可以獲得它的inode對象,這樣就建立了文件對象與物理文件之間的關(guān)聯(lián)。一個文件被打開的時候,它的file對象是使用dentry、inode、vfsmount對象中的信息填充的,比如它對應(yīng)的文件操作f_op由inode對象的i_fop字段得到。

文件系統(tǒng)的掛載

內(nèi)核是不是支持某種類型的文件系統(tǒng),需要我們進行注冊才能知道。例如,咱們的 ext4 文件系統(tǒng),就需要通過 register_filesystem 進行注冊,傳入的參數(shù)是 ext4_fs_type,表示注冊的是 ext4 類型的文件系統(tǒng)。這里面最重要的一個成員變量就是 ext4_mount。記住它,這個我們后面還會用。

  1. 如果一種文件系統(tǒng)的類型曾經(jīng)在內(nèi)核注冊過,這就說明允許你掛載并且使用這個文件系統(tǒng)。 
  2. register_filesystem(&ext4_fs_type); 
  3.  
  4. static struct file_system_type ext4_fs_type = { 
  5.   .owner    = THIS_MODULE, 
  6.   .name    = "ext4"
  7.   .mount    = ext4_mount, 
  8.   .kill_sb  = kill_block_super, 
  9.   .fs_flags  = FS_REQUIRES_DEV, 
  10. }; 

ext4文件系統(tǒng)的掛載是通過ext4_mount完成的,后者調(diào)用mount_bdev(block device)實現(xiàn),mount_bdev判斷兩次掛載是否為同一個文件系統(tǒng)的依據(jù)是:是否為同一個塊設(shè)備(test_bdev_super),也就是同一個塊設(shè)備只有一個super_block與之對應(yīng),即使掛載多次。

  1. static struct dentry *ext4_mount(struct file_system_type *fs_type, int flags, 
  2.          const char *dev_name, void *data) 
  3.  return mount_bdev(fs_type, flags, dev_name, data, ext4_fill_super); 
  4.  
  5.  
  6. struct dentry *mount_bdev(struct file_system_type *fs_type, 
  7.  int flags, const char *dev_name, void *data, 
  8.  int (*fill_super)(struct super_block *, void *, int)) 
  9.  struct block_device *bdev; 
  10.  struct super_block *s; 
  11.  fmode_t mode = FMODE_READ | FMODE_EXCL; 
  12.  int error = 0; 
  13.  
  14.  
  15.  if (!(flags & MS_RDONLY)) 
  16.   mode |= FMODE_WRITE; 
  17.  
  18.  獲取設(shè)備 
  19.  bdev = blkdev_get_by_path(dev_name, mode, fs_type); 
  20.  if (IS_ERR(bdev)) 
  21.   return ERR_CAST(bdev); 
  22.  
  23.  
  24.  /* 
  25.   * once the super is inserted into the list by sget, s_umount 
  26.   * will protect the lockfs code from trying to start a snapshot 
  27.   * while we are mounting 
  28.   */ 
  29.  mutex_lock(&bdev->bd_fsfreeze_mutex); 
  30.  if (bdev->bd_fsfreeze_count > 0) { 
  31.   mutex_unlock(&bdev->bd_fsfreeze_mutex); 
  32.   error = -EBUSY; 
  33.   goto error_bdev; 
  34.  } 
  35.  s = sget(fs_type, test_bdev_super, set_bdev_super, flags | MS_NOSEC, 
  36.    bdev); 
  37.  mutex_unlock(&bdev->bd_fsfreeze_mutex); 
  38.  if (IS_ERR(s)) 
  39.   goto error_s; 
  40.  
  41.  
  42.  if (s->s_root) { 
  43.   if ((flags ^ s->s_flags) & MS_RDONLY) { 
  44.    deactivate_locked_super(s); 
  45.    error = -EBUSY; 
  46.    goto error_bdev; 
  47.   } 
  48.  
  49.  
  50.   /* 
  51.    * s_umount nests inside bd_mutex during 
  52.    * __invalidate_device().  blkdev_put() acquires 
  53.    * bd_mutex and can't be called under s_umount.  Drop 
  54.    * s_umount temporarily.  This is safe as we're 
  55.    * holding an active reference. 
  56.    */ 
  57.   up_write(&s->s_umount); 
  58.   blkdev_put(bdev, mode); 
  59.   down_write(&s->s_umount); 
  60.  } else { 
  61.   s->s_mode = mode; 
  62.   snprintf(s->s_id, sizeof(s->s_id), "%pg", bdev); 
  63.   sb_set_blocksize(s, block_size(bdev)); 
  64.   error = fill_super(s, data, flags & MS_SILENT ? 1 : 0); 
  65.   if (error) { 
  66.    deactivate_locked_super(s); 
  67.    goto error; 
  68.   } 
  69.  
  70.  
  71.   s->s_flags |= MS_ACTIVE; 
  72.   bdev->bd_super = s; 
  73.  } 
  74.  
  75.  
  76.  return dget(s->s_root); 
  77.  
  78.  
  79. error_s: 
  80.  error = PTR_ERR(s); 
  81. error_bdev: 
  82.  blkdev_put(bdev, mode); 
  83. error: 
  84.  return ERR_PTR(error); 

掛載ext4文件系統(tǒng)最終由ext4_fill_super完成,它會讀取磁盤中的ext4_super_block,創(chuàng)建并初始化ext4_sb_info對象,建立它們和super_block的關(guān)系。ext4_sb_info的結(jié)構(gòu)如下:

它的實現(xiàn)比較復(fù)雜,主要邏輯如下:

ext4_sb_info的建立是在ext4_fill_super函數(shù)中完成的,代碼如下:

  1. struct ext4_sb_info { 
  2.  struct buffer_head * s_sbh; /* Buffer containing the super block */ 
  3.  struct ext4_super_block *s_es; /* Pointer to the super block in the buffer */ 
  4.  struct buffer_head **s_group_desc; 
  5. }; 
  6.   
  7. static int ext4_fill_super(struct super_block *sb, void *data, int silent) 
  8.  struct ext4_sb_info *sbi; 
  9.  struct buffer_head *bh; 
  10.  struct ext4_super_block *es = NULL
  11.     //1 
  12.  bh = sb_bread_unmovable(sb, logical_sb_block) 
  13.     //2 
  14.  es = (struct ext4_super_block *) (bh->b_data + offset); 
  15.     sbi->s_sbh = bh; 
  16.  sbi->s_es = es; 
  17.  sb->s_fs_info = sbi; 
  18.  sbi->s_sb = sb; 
  19.     //3 
  20.     blocks_count = (ext4_blocks_count(es) - 
  21.    le32_to_cpu(es->s_first_data_block) + 
  22.    EXT4_BLOCKS_PER_GROUP(sb) - 1); 
  23.  do_div(blocks_count, EXT4_BLOCKS_PER_GROUP(sb)); 
  24.  sbi->s_groups_count = blocks_count; 
  25.  sbi->s_blockfile_groups = min_t(ext4_group_t, sbi->s_groups_count, 
  26.    (EXT4_MAX_BLOCK_FILE_PHYS / EXT4_BLOCKS_PER_GROUP(sb))); 
  27.  db_count = (sbi->s_groups_count + EXT4_DESC_PER_BLOCK(sb) - 1) / 
  28.      EXT4_DESC_PER_BLOCK(sb); 
  29.  sbi->s_group_desc = ext4_kvmalloc(db_count * 
  30.        sizeof(struct buffer_head *), 
  31.        GFP_KERNEL); 
  32.   for (i = 0; i < db_count; i++) { 
  33.   block = descriptor_loc(sb, logical_sb_block, i); 
  34.   sbi->s_group_desc[i] = sb_bread_unmovable(sb, block); 
  35.     } 
  36.     //4 
  37.     if (!ext4_check_descriptors(sb, logical_sb_block, &first_not_zeroed)) { 
  38.   ret = -EFSCORRUPTED; 
  39.   goto error; 
  40.  } 
  41.     //5 
  42.     root = ext4_iget(sb, EXT4_ROOT_INO); 
  43.     //6 
  44.     if (ext4_setup_super(sb, es, sb->s_flags & MS_RDONLY)) 
  45.   sb->s_flags |= MS_RDONLY; 
  46.  
  47.  if (sbi->s_inode_size > EXT4_GOOD_OLD_INODE_SIZE) { 
  48.   sbi->s_want_extra_isize = sizeof(struct ext4_inode) - 
  49.            EXT4_GOOD_OLD_INODE_SIZE; 
  50.   if (ext4_has_feature_extra_isize(sb)) { 
  51.    if (sbi->s_want_extra_isize < 
  52.        le16_to_cpu(es->s_want_extra_isize)) 
  53.     sbi->s_want_extra_isize = 
  54.      le16_to_cpu(es->s_want_extra_isize); 
  55.    if (sbi->s_want_extra_isize < 
  56.        le16_to_cpu(es->s_min_extra_isize)) 
  57.     sbi->s_want_extra_isize = 
  58.      le16_to_cpu(es->s_min_extra_isize); 
  59.   } 
  60.     ext4_set_resv_clusters(sb); 
  61.     err = ext4_setup_system_zone(sb); 
  62.     ext4_ext_init(sb); 
  63.  err = ext4_mb_init(sb); 
  64.     block = ext4_count_free_clusters(sb); 
  65.  ext4_free_blocks_count_set(sbi->s_es,  
  66.        EXT4_C2B(sbi, block)); 
  67.  err = percpu_counter_init(&sbi->s_freeclusters_counter, block, 
  68.       GFP_KERNEL); 
  69.  if (!err) { 
  70.   unsigned long freei = ext4_count_free_inodes(sb); 
  71.   sbi->s_es->s_free_inodes_count = cpu_to_le32(freei); 
  72.   err = percpu_counter_init(&sbi->s_freeinodes_counter, freei, 
  73.        GFP_KERNEL); 
  74.  } 
  75.   err = percpu_counter_init(&sbi->s_dirs_counter, 
  76.        ext4_count_dirs(sb), GFP_KERNEL); 
  77.   err = percpu_counter_init(&sbi->s_dirtyclusters_counter, 0, 
  78.        GFP_KERNEL); 
  79.   err = percpu_init_rwsem(&sbi->s_journal_flag_rwsem); 
  80.  
  81.         return 0; 
  82.  } 

ext4_fill_super主要分六步,均用標(biāo)號標(biāo)出。

第1步,讀取ext4_super_block對象,此時并不知道文件系統(tǒng)的block大小,也不知道它起始于第幾個block,只知道它起始于磁盤的第1024字節(jié)(前1024字節(jié)存放x86啟動信息等)。所以在第1步中先給定一個假設(shè)值,一般假設(shè)block大小為1024字節(jié),ext4_super_block始于block 1(sb_block)。由sb_min_blocksize計算得到的block大小如果小于1024,就以它作為新的block大小得到block號logical_sb_block和block內(nèi)的偏移量offset。讀取logical_sb_block的內(nèi)容,加上計算得到的偏移量,得到的就是ext4_super_block對象(es),但因為block大小可能小于1024,所以有可能讀到的只是ext4_super_block的一部分,所以為了保險起見,接下來只能訪問它的一部分字段,主要是一些簡單的驗證工作。所幸s_log_block_size字段的偏移量0x18并不大,步驟1完成后,可以得到實際的block大小(2^(10+s_log_block_size))。

第2步,block大小最小為1024,最大為65536,我的磁盤中為4096,所以步驟2中會重新計算logical_sb_block和offset分別為0和1024。然后讀取block 0,得到的數(shù)據(jù)加上1024就是完整的ext4_super_block對象。

第3步,根據(jù)得到es為ext4_sb_info字段賦值,代碼段中保留了s_group_desc字段的賦值過程,其余字段省略。

第4步,檢查所有的group descriptors數(shù)據(jù)的合法性,初始化flex_bg相關(guān)的信息。

第5步,調(diào)用ext4_iget獲取ext4的root文件,并調(diào)用d_make_root創(chuàng)建對應(yīng)的dentry,為sb->s_root賦值。

第6步,調(diào)用ext4_setup_super,將控制權(quán)轉(zhuǎn)移到ext4_setup_super,它將進行幾項最后的檢查并輸出適當(dāng)?shù)木嫘畔?。最后將超級快的變更?nèi)容寫回到磁盤上,更新掛載計數(shù)器和上一次掛載的日期。

這樣就將磁盤掛載到linux的VFS文件文件系統(tǒng)中了。其中,file_system_type用于描述具體文件系統(tǒng)的類型,struct vfsmount用于描述一個文件系統(tǒng)的安裝實例。Linux所支持的文件系統(tǒng),都會有且僅有一個file_system_type結(jié)構(gòu)(比如,Ext2對應(yīng)ext2_fs_type,Ext3對應(yīng)ext3_fs_type,Ext4對應(yīng)ext4_fs_type),而不管它有零個或多個實例被安裝到系統(tǒng)中。每當(dāng)一個文件系統(tǒng)被安裝時,就會有一個vfsmount結(jié)構(gòu)被創(chuàng)建,它代表了該文件系統(tǒng)的一個安裝實例,也代表了該文件系統(tǒng)的一個安裝點。下圖是超級塊、安裝點和具體的文件系統(tǒng)之間的關(guān)系。不同類型的文件系統(tǒng)通過next字段形成一個鏈表,同一種文件系統(tǒng)類型的超級塊通過s_instances字段鏈接在一起,并掛入fs_supers鏈表中。

關(guān)于ext4還有很多內(nèi)容,源碼鏈接:https://elixir.bootlin.com/linux/v4.8/source/fs/ext4/,有興趣的大家可以去看看。

恢復(fù)刪除的文件并不神秘

存儲介質(zhì)上的數(shù)據(jù)可以分為兩部分:表征文件的數(shù)據(jù)(可以稱為元數(shù)據(jù),metadata)和文件的內(nèi)容。不僅僅ext4文件系統(tǒng)如此,多數(shù)基于磁盤的文件系統(tǒng)都離不開這兩部分。為了恢復(fù)刪除的文件,需要先了解刪除的數(shù)據(jù)屬于哪個類型,多數(shù)文件系統(tǒng)刪除的是文件的信息,也就是表示文件和它所屬目錄的關(guān)系、文件本身信息的數(shù)據(jù),至于文件的內(nèi)容,一般是不會覆蓋的。這么做最大的優(yōu)點是效率高,比如我們在ext4文件系統(tǒng)中,刪除一個幾個G字節(jié)大小的文件并不會比刪除幾個字節(jié)的文件所用的時間長很多。缺點也是明顯的,就是所謂的刪除并沒有對文件的內(nèi)容造成影響,只要沒有被后續(xù)的文件覆蓋,就有被恢復(fù)的可能,有安全的風(fēng)險。

 

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

2021-06-21 14:52:45

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

2021-06-22 15:16:01

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

2018-01-10 12:42:09

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

2009-10-12 11:14:51

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

2010-03-02 15:09:26

Linux mount

2016-12-27 10:48:59

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

2010-04-07 18:42:42

Unix命令

2021-06-29 07:47:22

文件系統(tǒng)磁盤

2009-10-13 14:31:26

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

2017-08-17 10:03:06

磁盤系統(tǒng)實例

2020-07-22 14:53:06

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

2023-09-03 17:09:58

LinuxSAN LUN磁盤

2023-09-05 15:17:48

LinuxLUN磁盤

2011-01-13 14:10:30

Linux文件系統(tǒng)

2024-03-11 10:30:31

Linux文件系統(tǒng)

2018-08-24 10:10:25

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

2019-09-20 10:04:45

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

2020-07-28 08:00:03

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

2021-05-31 07:50:59

Linux文件系統(tǒng)

2013-05-27 14:46:06

文件系統(tǒng)分布式文件系統(tǒng)
點贊
收藏

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