個人學習-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檔案系統進行總結整理;
-
Linux檔案系統的組織方式;
-
Linux的VFS機制和統一檔案模型(common file model);
-
Linux系統IO緩沖機制;
3. Linux檔案組織方式:
3.1 基礎知識:
? 老調重彈:Linux設計的思路,就是一切皆檔案,因此Linux中的一切檔案都以檔案格式保存,而檔案根據各自不同的功能分為一下幾類:
-
普通檔案:
-,常規的檔案型別,在Linux中,以.開頭的檔案為隱藏檔案;
-
目錄檔案;
d, 目錄檔案,目錄檔案實際包含兩部分Inode和entry(實際所有檔案都是這樣,不同的是,目錄中保存了相關檔案的資訊);
Linux通過Inode結構體存盤目錄的基礎資訊,系統呼叫stat(),用來訪問這個結構和相應資訊,

? (圖1 源自參考文獻[7])
-
字符設備檔案;
? c, character device,字符設備是以字符為最小單位進行資料互動的檔案,用以驅動字符互動的應用,如鍵盤(有文章提到oracle是以字符為格式進行資料傳輸的,記個ToDo);
? 而對于字符設備而言,僅支持順序訪問資料,不支持隨機訪問,
-
塊設備檔案;
B,block device driver
? 塊設備傳輸,實際是以固定大小進行資料傳輸的檔案型別,和字符設備不同的是,block支持我們對設備隨機訪問,最典型的例子即硬碟,作業系統/資料庫/其他和磁盤互動應用,實際可以根據自己的規則,去隨機訪問磁盤的位置,去在該位置寫入資料,而通過塊設備進行讀寫時,也需要以bolck為最小單位進行操作(實際上,無論是OS還是DBS,都是以Page去容納多個Block,然后通過Page進行資料加載,然后以block進行資料決議的)
-
符號鏈接;(軟鏈接,硬鏈接實際是生成了一個相應檔案,該檔案名和原始檔案名指向同一個inode):
? 符號鏈接可以理解成一種快捷方式,允許我們通過符號鏈接快速訪問目標檔案,
-
套接字;
S,socket,用于網路通信的檔案,通過套接字API形成的一個簡單協議族,成對出現進行通信,
-
管道;
? 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和緩沖區之間的互動;
傳統的(無快取)檔案讀寫方式

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()時;
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/508732.html
標籤:Linux
上一篇:Ubuntu 通過本機代理修復 NuGet 還原 error NU1301 失敗
下一篇:超級熱鍵大全
