個人學習-Linux文件系統架構

来源:https://www.cnblogs.com/Albert-lihai/archive/2022/09/15/16698201.html
-Advertisement-
Play Games

個人學習-Linux文件系統架構 1. 參考文章 [1]https://blog.csdn.net/Holy_666/article/details/86532671 [2]CSDN博主土豆西瓜大芝麻:[Linux的VFS詳解]:https://blog.csdn.net/jinking01/art ...


個人學習-Linux文件系統架構

1. 參考文章

[1]https://blog.csdn.net/Holy_666/article/details/86532671

[2]CSDN博主土豆西瓜大芝麻:[Linux的VFS詳解]:https://blog.csdn.net/jinking01/article/details/90669534

[3]深入理解 Linux的 I/O 系統:https://z.itpub.net/article/detail/9595A9A27188FF73810F07F00DAA08ED

[4]Linux嵌入式的知乎專欄:https://zhuanlan.zhihu.com/p/505338841

[5]博客園博主[李大嘴]:[字元設備和塊設備的區別]https://www.cnblogs.com/qlee/archive/2011/07/27/2118406.html

[6]博客園博主[賽艇隊長]:[Linux文件系統詳述]https://www.cnblogs.com/bellkosmos/p/detail_of_linux_file_system.html

[7]StackExchange:[What are directories, if everything on Linux is a file?]

[8] [The Linux Documentation Project: Filesystem]http://www.tldp.org/LDP/tlk/fs/filesystem.html#tth_sEc9.1.4)

2. 概述:

本文主要從四個角度對Linux文件系統進行總結整理;

  1. Linux文件系統的組織方式;

  2. Linux的VFS機制和統一文件模型(common file model);

  3. Linux系統IO緩衝機制;

3. Linux文件組織方式:

3.1 基礎知識:

​ 老調重彈:Linux設計的思路,就是一切皆文件,因此Linux中的一切文件都以文件格式保存,而文件根據各自不同的功能分為一下幾類:

  1. 普通文件:

    -,常規的文件類型,在Linux中,以.開頭的文件為隱藏文件;

  2. 目錄文件;

    d, 目錄文件,目錄文件實際包含兩部分Inode和entry(實際所有文件都是這樣,不同的是,目錄中保存了相關文件的信息);

    Linux通過Inode結構體存儲目錄的基礎信息,系統調用stat(),用來訪問這個結構和相應信息。

​ (圖1 源自引用文獻[7])

  1. 字元設備文件;

    ​ c, character device,字元設備是以字元為最小單位進行數據交互的文件,用以驅動字元交互的應用,如鍵盤(有文章提到oracle是以字元為格式進行數據傳輸的,記個ToDo);

    ​ 而對於字元設備而言,僅支持順序訪問數據,不支持隨機訪問。

  2. 塊設備文件;

    B,block device driver

    ​ 塊設備傳輸,實際是以固定大小進行數據傳輸的文件類型,和字元設備不同的是,block支持我們對設備隨機訪問。最典型的例子即硬碟,操作系統/資料庫/其他和磁碟交互應用,實際可以根據自己的規則,去隨機訪問磁碟的位置,去在該位置寫入數據。而通過塊設備進行讀寫時,也需要以bolck為最小單位進行操作(實際上,無論是OS還是DBS,都是以Page去容納多個Block,然後通過Page進行數據載入,然後以block進行數據解析的)

  3. 符號鏈接;(軟鏈接,硬鏈接實際是生成了一個相應文件,該文件名和原始文件名指向同一個inode):

    ​ 符號鏈接可以理解成一種快捷方式,允許我們通過符號鏈接快速訪問目標文件。

  4. 套接字;

    S,socket,用於網路通信的文件,通過套接字API形成的一個簡單協議族,成對出現進行通信。

  5. 管道;

    ​ P,進行進程間通信的文件,將一個進程的輸出,通過管道,輸入到另一個文件。

3.2 文件系統的基本組成

​ (Note: 本部分僅介紹基礎部分,磁碟分區,初始化,數據在磁碟上的組織後續再進行補充)

​ Linux操作系統支持很多不同的文件系統,ext2,ext3,XFS,FAT等等,而Linux把對不同文件系統的訪問,交給VFS(virtual file system)來進行。

​ Linux的文件系統會為每個文件分配兩個數據結構:

​ 索引節點(index node) 和 目錄項(directory entry),用來記錄文件的元信息(meta data)如inode編號,文件大小,訪問許可權,修改時間,磁碟位置等。索引節點和磁碟上的每個物理文件相對應,而索引節點本身,也存儲在磁碟上。

​ 目錄項(directory entry)用來記錄文件的名字,索引節點指針,和其他目錄項的層次關係。多個目錄項關聯起來,就形成了目錄結構,和索引節點不同,目錄項是由內核維護的數據結構,緩存在記憶體中;(note:實際上系統啟動時,會將相應的數據,載入到樹形結構中,維護在記憶體中)

目錄項的結構體,dentry

struct dentry {
     atomic_t d_count;        /* 目錄項引用計數器 */
     unsigned int d_flags;    /* 目錄項標誌 */
     struct inode  * d_inode;   /* 與文件名關聯的索引節點 */
     struct dentry * d_parent;       /* 父目錄的目錄項 */
     struct list_head d_hash;        /* 目錄項形成的哈希表 */
     struct list_head d_lru;         /*未使用的 LRU 鏈表 */
     struct list_head d_child;       /*父目錄的子目錄項所形成的鏈表 */
     struct list_head d_subdirs;     /* 該目錄項的子目錄所形成的鏈表*/
     struct list_head d_alias;       /* 索引節點別名的鏈表*/
     int d_mounted;                  /* 目錄項的安裝點 */
     struct qstr d_name;             /* 目錄項名(可快速查找) */
     unsigned long d_time;           /* 由 d_revalidate函數使用 */
     struct dentry_operations  *d_op; /* 目錄項的函數集*/
     struct super_block * d_sb;      /* 目錄項樹的根 (即文件的超級塊)*/
     unsigned long d_vfs_flags; 
     void * d_fsdata;                /* 具體文件系統的數據 */
     unsigned char d_iname[DNAME_INLINE_LEN]; /* 短文件名 */
};

通過stat訪問得到的Inode信息

     struct stat { /* when _DARWIN_FEATURE_64_BIT_INODE is NOT defined */
         dev_t    st_dev;    /* device inode resides on */
         ino_t    st_ino;    /* inode's number */
         mode_t   st_mode;   /* inode protection mode */
         nlink_t  st_nlink;  /* number of hard links to the file */
         uid_t    st_uid;    /* user-id of owner */
         gid_t    st_gid;    /* group-id of owner */
         dev_t    st_rdev;   /* device type, for special file inode */
         struct timespec st_atimespec;  /* time of last access */
         struct timespec st_mtimespec;  /* time of last data modification */
         struct timespec st_ctimespec;  /* time of last file status change */
         off_t    st_size;   /* file size, in bytes */
         quad_t   st_blocks; /* blocks allocated for file */
         u_long   st_blksize;/* optimal file sys I/O ops blocksize */
         u_long   st_flags;  /* user defined flags for file */
         u_long   st_gen;    /* file generation number */
     };

而索引節點,目錄項,以及文件數據間的關係可以用如下結構來表示

​ (圖2 源自引用4)

4. 虛擬文件系統(Virtual File Switch)

當一個用戶應用,通過系統調用函數進行數據讀寫時會發生什麼?

fopen("~");
fwrite("~");
fclose("~");

​ 在我們的直覺上,會認為用戶空間的代碼,通過調用庫函數喚起系統調用,然後交於OS File System直接寫入磁碟(在考慮buffer,page,block,機制後)。但是實際上Linux實際上是將系統調用交付於VFS,即虛擬文件系統,然後由VFS執行後續操作的。

​ (圖3 源自引用2)

​ 這樣做有什麼價值呢?如上文所述,Linux存在不同的file system operator,這些不同的文件系統暴漏給上層的介面可能是不同的,如果將這些介面都提供給OS SCI(stsrem call interface),系統調用會過於複雜。而VFS在文件系統和系統調用間提供了一個抽象層,讓系統調用的POSIX API和不同存儲設備的具體介面實現了分離,實現了一次解耦。

​ (圖4 源自引用2)

4.1 統一文件模型(common file model)

​ 由於VFS將不同的底層介面抽象了統一的標準給系統調用,系統調用層便可以用上文提到的Inode,entry模型將所有文件的模型進行統一。實際上正是VFS層提供了統一的文件模型。

​ VFS通過四種標準模型來構建統一文件模型:

(1)superblock: 存儲文件系統的基本元數據(可以理解成 meta of meta,這詞兒沒查過,是我現編的)。如文件系統的類型,大小,狀態,一起其他元數據的相關信息。

(2)index node(inode):一個用來保存文件相關的元數據。

(3)directory entry: 保存文件名稱和inode的對應關係;

每個dentry存在三種狀態:

  • Used: 和一個inode關聯,正被使用,不能被損壞和丟棄;
  • Unused: 和inode關聯,處於被緩存狀態,沒有被vfs使用;
  • negative: 沒有和具體的inode關聯(實際相當於一個無效路徑);

由於dentry實際是載入在記憶體里的,系統會對dentry存在優化策略:

1.used dentrie list:把使用的dentry串成一個鏈表;

2.LRU鏈表:(least recently used)鏈表,實際就是最常見的頁面置換演算法,找出最久沒有使用的entry,把它從記憶體中釋放。

3.hash table:用哈希表,來維持dentry的高速查詢;

(4) file: 一組邏輯上相關聯的數據,實際就是我們通過open函數返回的數據類型,在我們使用open打開函數後,就從磁碟中載入了對於的數據至記憶體,VFS將數據保存至File模型中,和進程,用戶強關聯。其中包含了打開的flag,文件名稱,當前的便宜。最重要的欄位就是f_op,指向了當前文件所支持的操作集合;

struct file {
    struct dentry *f_dentry;
    struct vfsmount *f_vfsmnt;
    struct file_operations *f_op;
    mode_t f_mode;
    loff_t f_pos;
    struct fown_struct f_owner;
    unsigned int f_uid, f_gid;
    unsigned long f_version;
    ...
}

5. 系統IO

​ 本部分主要討論系統IO和緩衝區之間的交互;

傳統的(無緩存)文件讀寫方式

![截屏2022-09-15 22.12.33](/Users/alberthaoluchen/Library/Application Support/typora-user-images/截屏2022-09-15 22.12.33.png)

1.用戶進程通過read()向kernel進行系統調用,切換上下文,到內核空間。

2.CPU將數據從硬碟or主存拷貝至kernel到讀緩衝區;

3.CPU將讀緩衝區的數據拷貝回用戶緩衝區;

4.上下文從kernelspace 切換回UserSpace,read調用執行返回;

用戶態 <---> 內核態切換兩次;

拷貝操作,兩次;

用戶態的切換和系統拷貝,被切開,相較於連續的操作,這種間斷式的操作更耗時;

高性能優化的I/O

PageCache

​ 頁緩存技術,通過每次從磁碟讀取一個Page單位的數據至緩存,來減少對磁碟的讀寫,來提高系統效率;

​ 當我們進行順序讀寫時,頁緩存能極大提高我們的讀寫速度(實際就是直接在memory上讀寫);

頁緩存的優化策略:

讀策略:

  • 當我們執行read操作時,判斷數據在PageCache上嗎?如果在,我們就不對磁碟進行讀操作;
  • 如果不在,調度I/O去讀磁碟數據,除了目標文件所在的Page外,還會讀多個連續頁到頁緩存中;

寫策略:

​ 當我們執行寫操作時,先寫進頁緩存,此時我們把目標頁標記為臟頁(dirty page),並把這個頁加入臟頁鏈表;

flusher會周期性的回寫臟頁到磁碟,當磁碟數據和記憶體一致時,清楚臟頁標記,

當滿足以下條件時,臟頁會被寫入記憶體:

  • 空閑記憶體低於一個特定閾值;
  • 臟頁在記憶體駐留時間超過閾值時(LRU)
  • 用戶進程調用sync()和fsync()時;

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 業務模塊介紹 現在我們對整體的業務進行介紹以及演示 5. 全鏈路整體架構 上面介紹了為什麼需要全鏈路壓測,下麵來看下全鏈路壓測的整體架構。 ​ 整體架構如下主要是對壓測客戶端的壓測數據染色,全鏈路中間件識別出染色數據,並將正常數據和壓測數據區分開,進行數據隔離,這裡主要涉及到mysql資料庫,Rab ...
  • MyBatis 通過使用內置的日誌工廠提供日誌功能。 在這裡我們對STDOUT_LOGGING和LOG4J進行學習。 一、STDOUT_LOGGING 1.什麼是STDOUT_LOGGING STDOUT_LOGGING是MyBatis的標準日誌配置。STDOUT_LOGGING的使用無需其他的依賴 ...
  • 從提升性能角度來說 提升了對CPU的使用效率:目前生產的伺服器大多數都是多核,標配的機器都是 8C/16G。操作系統會將不同的線程分配給不同的核心處理,理論上,有多少核心就有多少個線程並行執行。如果沒有併發編程,CPU的利用率將極大的浪費,假設當前正在處理耗時的 I/O 操作,那麼整個CPU就會處於... ...
  • 大家好,我是三友~~ 在對於讀寫鎖的認識當中,我們都認為讀時加讀鎖,寫時加寫鎖來保證讀寫和寫寫互斥,從而達到讀寫安全的目的。但是就在我翻Eureka源碼的時候,發現Eureka在使用讀寫鎖時竟然是在讀時加寫鎖,寫時加讀鎖,這波操作屬實震驚到了我,於是我就花了點時間研究了一下Eureka的這波操作。 ...
  • DotnetZip使用方法見此文章https://www.cnblogs.com/pengze0902/p/6124659.html在netframework環境下,使用上面文章中的設置Encoding為Default的方法即可解決中文亂碼問題 但是當我使用.net6創建控制台項目並採用上述代碼時, ...
  • iNeuOS工業互聯網操作系統面向:儀器儀錶、雙碳環保、核能科學與工程和鋼鐵冶金領域頒發第一批技術認證資質,一共21名同志在項目實施過程中表現突出,從iNeuOS的應用、開發及項目過程中的交流都大大促進了項目保質保量的快速交付,特此頒發應用實施和二次開發工程認證。 ...
  • 一:背景 1. 講故事 前段時間有位朋友在微信上找到我,說他的程式出現了記憶體泄漏,能不能幫他看一下,這個問題還是比較經典的,加上好久沒上非托管方面的東西了,這篇就和大家分享一下,話不多說,上 WinDbg 說話。 二:WinDbg 分析 1. 到底是哪裡的泄漏 好的開始就是成功的一半,否則就南轅北轍 ...
  • sed高階用法 sed編輯器 sed是一種流編輯器,流編輯器會在編輯器處理數據之前基於預先提供的一組規則來編輯數據流。 1.sed編輯器工作流程 sed編輯器可以根據命令來處理數據流中的數據,這些命令要麼從命令行中輸入,要麼存儲在一個命令文本文件中。 sed的工作流程主要包括讀取、執行和顯示三個過程 ...
一周排行
    -Advertisement-
    Play Games
  • 概述:在C#中,++i和i++都是自增運算符,其中++i先增加值再返回,而i++先返回值再增加。應用場景根據需求選擇,首碼適合先增後用,尾碼適合先用後增。詳細示例提供清晰的代碼演示這兩者的操作時機和實際應用。 在C#中,++i 和 i++ 都是自增運算符,但它們在操作上有細微的差異,主要體現在操作的 ...
  • 上次發佈了:Taurus.MVC 性能壓力測試(ap 壓測 和 linux 下wrk 壓測):.NET Core 版本,今天計劃準備壓測一下 .NET 版本,來測試並記錄一下 Taurus.MVC 框架在 .NET 版本的性能,以便後續持續優化改進。 為了方便對比,本文章的電腦環境和測試思路,儘量和... ...
  • .NET WebAPI作為一種構建RESTful服務的強大工具,為開發者提供了便捷的方式來定義、處理HTTP請求並返迴響應。在設計API介面時,正確地接收和解析客戶端發送的數據至關重要。.NET WebAPI提供了一系列特性,如[FromRoute]、[FromQuery]和[FromBody],用 ...
  • 原因:我之所以想做這個項目,是因為在之前查找關於C#/WPF相關資料時,我發現講解圖像濾鏡的資源非常稀缺。此外,我註意到許多現有的開源庫主要基於CPU進行圖像渲染。這種方式在處理大量圖像時,會導致CPU的渲染負擔過重。因此,我將在下文中介紹如何通過GPU渲染來有效實現圖像的各種濾鏡效果。 生成的效果 ...
  • 引言 上一章我們介紹了在xUnit單元測試中用xUnit.DependencyInject來使用依賴註入,上一章我們的Sample.Repository倉儲層有一個批量註入的介面沒有做單元測試,今天用這個示例來演示一下如何用Bogus創建模擬數據 ,和 EFCore 的種子數據生成 Bogus 的優 ...
  • 一、前言 在自己的項目中,涉及到實時心率曲線的繪製,項目上的曲線繪製,一般很難找到能直接用的第三方庫,而且有些還是定製化的功能,所以還是自己繪製比較方便。很多人一聽到自己畫就害怕,感覺很難,今天就分享一個完整的實時心率數據繪製心率曲線圖的例子;之前的博客也分享給DrawingVisual繪製曲線的方 ...
  • 如果你在自定義的 Main 方法中直接使用 App 類並啟動應用程式,但發現 App.xaml 中定義的資源沒有被正確載入,那麼問題可能在於如何正確配置 App.xaml 與你的 App 類的交互。 確保 App.xaml 文件中的 x:Class 屬性正確指向你的 App 類。這樣,當你創建 Ap ...
  • 一:背景 1. 講故事 上個月有個朋友在微信上找到我,說他們的軟體在客戶那邊隔幾天就要崩潰一次,一直都沒有找到原因,讓我幫忙看下怎麼回事,確實工控類的軟體環境複雜難搞,朋友手上有一個崩潰的dump,剛好丟給我來分析一下。 二:WinDbg分析 1. 程式為什麼會崩潰 windbg 有一個厲害之處在於 ...
  • 前言 .NET生態中有許多依賴註入容器。在大多數情況下,微軟提供的內置容器在易用性和性能方面都非常優秀。外加ASP.NET Core預設使用內置容器,使用很方便。 但是筆者在使用中一直有一個頭疼的問題:服務工廠無法提供請求的服務類型相關的信息。這在一般情況下並沒有影響,但是內置容器支持註冊開放泛型服 ...
  • 一、前言 在項目開發過程中,DataGrid是經常使用到的一個數據展示控制項,而通常表格的最後一列是作為操作列存在,比如會有編輯、刪除等功能按鈕。但WPF的原始DataGrid中,預設只支持固定左側列,這跟大家習慣性操作列放最後不符,今天就來介紹一種簡單的方式實現固定右側列。(這裡的實現方式參考的大佬 ...