來源:公司資(zī)訊 | 2021.08.19
和 CPU、内存相同,磁盤和文件體(tǐ)系的辦理,也是操作體(tǐ)系最核心的功能。
磁盤爲體(tǐ)系供給了最基本的持久化存儲。
文件體(tǐ)系則在磁盤的基礎上,供給了一(yī)個用來辦理文件的樹(shù)狀結構。
那麽,磁盤和文件體(tǐ)系是怎麽作業的呢?又(yòu)有哪些目标能夠衡量它們的功能呢?
索引節點和目錄項
文件體(tǐ)系,本身是對存儲設備上的文件,進行安排辦理的機制。安排方法不同,就會構成不同的文件體(tǐ)系。
咱們要記住最重要的一(yī)點,在 Linux 中(zhōng)一(yī)切皆文件。不隻普通的文件和目錄,就連塊設備、 套接字、管道等,也都要經過一(yī)緻的文件體(tǐ)系來辦理。
爲了方便辦理,Linux 文件體(tǐ)系爲每個文件都分(fēn)配兩個數據結構,索引節點(index node)和目錄項(directory entry)。它們首要用來記載文件的元信息和目錄結構。
索引節點,簡稱爲 inode,用來記載文件的元數據,比方 inode 編号、文件巨細、拜訪 權限、修正日期、數據的位置等。索引節點和文件一(yī)一(yī)對應,它跟文件内容相同,都會 被持久化存儲到磁盤中(zhōng)。所以記住,索引節點同樣占用磁盤空間。
目錄項,簡稱爲 dentry,用來記載文件的名字、索引節點指針以及與其他目錄項的相關 聯系。多個相關的目錄項,就構成了文件體(tǐ)系的目錄結構。不過,不同于索引節點,目錄項是由内核保護的一(yī)個内存數據結構,所以一(yī)般也被叫做目錄項緩存。
換句話(huà)說,索引節點是每個文件的唯一(yī)标志(zhì),而目錄項保護的正是文件體(tǐ)系的樹(shù)狀結構。目錄項和索引節點的聯系是多對一(yī),你能夠簡單了解爲,一(yī)個文件能夠有多個别号。
舉個比方,經過硬鏈接爲文件創建的别号,就會對應不同的目錄項,不過這些目錄項本質上仍是鏈接同一(yī)個文件,所以,它們的索引節點相同。
索引節點和目錄項紀錄了文件的元數據,以及文件間的目錄聯系,那麽具體(tǐ)來說,文件數據到底是怎麽存儲的呢?是不是直接寫到磁盤中(zhōng)就好了呢?
實際上,磁盤讀寫的最小(xiǎo)單位是扇區,然而扇區隻要 512B 巨細,假如每次都讀寫這麽小(xiǎo)的單位,功率必定很低。所以,文件體(tǐ)系又(yòu)把接連的扇區組成了邏輯塊,然後每次都以邏 輯塊爲最小(xiǎo)單元,來辦理數據。常見的邏輯塊巨細爲 4KB,也便是由接連的 8 個扇區組成。
爲了協助咱們了解目錄項、索引節點以及文件數據的聯系,畫了一(yī)張示意圖。咱們能夠對照着這張圖,來回憶剛剛講過的内容,把知(zhī)識和細節串聯起來。
不過,這裏有兩點需要咱們注意:
榜首,目錄項本身便是一(yī)個内存緩存,而索引節點則是存儲在磁盤中(zhōng)的數據。在前面的 Buffer 和 Cache 原理中(zhōng),我(wǒ)從前提到過,爲了協調慢(màn)速磁盤與快速 CPU 的功能差異,文 件内容會緩存到頁緩存 Cache 中(zhōng)。那麽,咱們也應該想到,這些索引節點天然也會緩存到内存中(zhōng),加快文件的拜訪。
第二,磁盤在履行文件體(tǐ)系格式化時,會被分(fēn)紅三個存儲區域,超級塊、索引節點區和數
據塊區。其間,
超級塊,存儲整個文件體(tǐ)系的狀态。
索引節點區,用來存儲索引節點。
數據塊區,則用來存儲文件數據。
虛拟文件體(tǐ)系
目錄項、索引節點、邏輯塊以及超級塊,構成了 Linux 文件體(tǐ)系的四大(dà)基本要素。不過, 爲了支撐各種不同的文件體(tǐ)系,Linux 内核在用戶進程和文件體(tǐ)系的中(zhōng)心,又(yòu)引入了一(yī)個籠統層,也便是虛拟文件體(tǐ)系 VFS(Virtual File System)。
VFS 定義了一(yī)組一(yī)切文件體(tǐ)系都支撐的數據結構和規範接口。這樣,用戶進程和内核中(zhōng)的其他子體(tǐ)系,隻需要跟 VFS 供給的一(yī)緻接口進行交互就能夠了,而不需要再關心底層各種文件體(tǐ)系的完結細節。
這裏,下(xià)圖是 Linux 文件體(tǐ)系的架構圖,幫咱們更好地了解體(tǐ)系調用、VFS、緩存、文 件體(tǐ)系以及塊存儲之間的聯系。
經過這張圖,能夠看到,在 VFS 的下(xià)方,Linux 支撐各種各樣的文件體(tǐ)系,如 Ext4、 XFS、NFS 等等。依照存儲位置的不同,這些文件體(tǐ)系能夠分(fēn)爲三類。
榜首類是依據磁盤的文件體(tǐ)系,也便是把數據直接存儲在計算機本地挂載的磁盤中(zhōng)。常見的 Ext4、XFS、OverlayFS 等,都是這類文件體(tǐ)系。
第二類是依據内存的文件體(tǐ)系,也便是咱們常說的虛拟文件體(tǐ)系。這類文件體(tǐ)系,不需要任何磁盤分(fēn)配存儲空間,但會占用内存。咱們經常用到的 /proc 文件體(tǐ)系,其實便是 一(yī)種最常見的虛拟文件體(tǐ)系。此外(wài),/sys 文件體(tǐ)系也歸于這一(yī)類,首要向用戶空間導出層次化的内核目标。
第三類是網絡文件體(tǐ)系,也便是用來拜訪其他計算機數據的文件體(tǐ)系,比方 NFS、 SMB、iSCSI 等。
這些文件體(tǐ)系,要先挂載到 VFS 目錄樹(shù)中(zhōng)的某個子目錄(稱爲挂載點),然後才幹拜訪其間的文件。拿榜首類,也便是依據磁盤的文件體(tǐ)系爲例,在裝置體(tǐ)系時,要先挂載一(yī)個根 目錄(/),在根目錄下(xià)再把其他文件體(tǐ)系(比方其他的磁盤分(fēn)區、/proc 文件體(tǐ)系、/sys 文件體(tǐ)系、NFS 等)挂載進來。
文件體(tǐ)系 I/O
把文件體(tǐ)系挂載到挂載點後,你就能經過挂載點,再去(qù)拜訪它辦理的文件了。VFS 供給了一(yī)組規範的文件拜訪接口。這些接口以體(tǐ)系調用的方法,供給給應用程序運用。
就拿 cat 命令來說,它首要調用 open() ,打開(kāi)一(yī)個文件;然後調用 read() ,讀取文件的内容;最終再調用 write() ,把文件内容輸出到控制台的規範輸出中(zhōng):
int open(const char *pathname, int flags, mode_t mode);
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);
文件讀寫方法的各種差異,導緻 I/O 的分(fēn)類多種多樣。最常見的有,緩沖與非緩沖 I/O、 直接與非直接 I/O、堵塞與非堵塞 I/O、同步與異步 I/O 等。 接下(xià)來,咱們就詳細看這四種分(fēn)類。
榜首種,依據是否使用規範庫緩存,能夠把文件 I/O 分(fēn)爲緩沖 I/O 與非緩沖 I/O。
緩沖 I/O,是指使用規範庫緩存來加快文件的拜訪,而規範庫内部再經過體(tǐ)系調度拜訪文件。
非緩沖 I/O,是指直接經過體(tǐ)系調用來拜訪文件,不再經過規範庫緩存。
注意,這裏所說的“緩沖”,是目規範庫内部完結的緩存。比方說,你可能見到過,很多程序遇到換行時才真實輸出,而換行前的内容,其實便是被規範庫暫時緩存了起來。
無論緩沖 I/O 還對錯緩沖 I/O,它們最終仍是要經過體(tǐ)系調用來拜訪文件。咱們知(zhī)道,體(tǐ)系調用後,還會經過頁緩存,來削減磁盤的 I/O 操作。
第二,依據是否使用操作體(tǐ)系的頁緩存,能夠把文件 I/O 分(fēn)爲直接 I/O 與非直接 I/O。
直接 I/O,是指越過操作體(tǐ)系的頁緩存,直接跟文件體(tǐ)系交互來拜訪文件。
非直接 I/O 正好相反,文件讀寫時,先要經過體(tǐ)系的頁緩存,然後再由内核或額外(wài)的體(tǐ)系調用,真實寫入磁盤。
想要完結直接 I/O,需要你在體(tǐ)系調用中(zhōng),指定 O_DIRECT 标志(zhì)。假如沒有設置過,默許 的對錯直接 I/O。
不過要注意,直接 I/O、非直接 I/O,本質上仍是和文件體(tǐ)系交互。假如是在數據庫等場景中(zhōng),還會看到,越過文件體(tǐ)系讀寫磁盤的狀況,也便是咱們一(yī)般所說的裸 I/O。
第三,依據應用程序是否堵塞本身運轉,能夠把文件 I/O 分(fēn)爲堵塞 I/O 和非堵塞 I/O
所謂堵塞 I/O,是指應用程序履行 I/O 操作後,假如沒有取得呼應,就會堵塞當前線程,天然就不能履行其他任務。
所謂非堵塞 I/O,是指應用程序履行 I/O 操作後,不會堵塞當前的線程,能夠持續履行其他的任務,随後再經過輪詢或許事情告知(zhī)的方法,獲取調用的結果。
比方說,拜訪管道或許網絡套接字時,設置 O_NONBLOCK 标志(zhì),就表明用非堵塞方法拜訪;而假如不做任何設置,默許的便是堵塞拜訪。
第四,依據是否等待呼應結果,能夠把文件 I/O 分(fēn)爲同步和異步 I/O
所謂同步 I/O,是指應用程序履行 I/O 操作後,要一(yī)向比及整個 I/O 完結後,才幹取得 I/O 呼應。
所謂異步 I/O,是指應用程序履行 I/O 操作後,不必等待完結和完結後的呼應,而是持續履行就能夠。比及這次 I/O 完結後,呼應會用事情告知(zhī)的方法,告知(zhī)應用程序。
例如,在操作文件時,假如設置了 O_SYNC 或許 O_DSYNC 标志(zhì),就代表同步 I/O。假如設置了 O_DSYNC,就要等文件數據寫入磁盤後,才幹返回;而 O_SYNC,則是在 O_DSYNC 基礎上,要求文件元數據也要寫入磁盤後,才幹返回。
再比方,在拜訪管道或許網絡套接字時,設置了 O_ASYNC 選項後,相應的 I/O 便是異步I/O。這樣,内核會再經過 SIGIO 或許 SIGPOLL,來告知(zhī)進程文件是否可讀寫。
咱們可能發現了,這裏的很多概念也經常出現在網絡編程中(zhōng)。比方非堵塞 I/O,一(yī)般會跟 select/poll 配合,用在網絡套接字的 I/O 中(zhōng)。
這下(xià)咱們也應該能夠了解,“Linux 一(yī)切皆文件”的深刻含義。無論是普通文件和塊設備、仍是網絡套接字和管道等,它們都經過一(yī)緻的 VFS 接口來拜訪。