Linux文件系統解析
文件系統定義
在計算機出現之前其實就有文件系統的概念了,此時的文件系統指的是用于管理(存儲和檢索)紙質文件的系統,而在計算機發明之后,文件系統逐漸指的是管理存儲介質的系統,它通過簡單的接口給用戶,方便用戶使用存儲設備。
在學習 Linux 的時候,我們通常會看到這樣一句話,Linux中一切皆文件,也就是說,不管是普通的文件和目錄,還是包括塊設備、管道、socket等,也都是交給文件系統去管理的。文件系統是操作系統中負責管理持久數據的子系統,換言之,也就是負責把用戶的文件存到磁盤硬件中,它是一個磁盤上的目錄結構,是一個組織文件的方法,并且在一個磁盤上,可以包含一個或者多個文件系統。
下面,我們從用戶的角度和操作系統的角度兩個層面來闡述文件系統的相關概念。
用戶角度
要認識 Linux 的文件系統,從分區和目錄結構說起,首先我們先來看下windows,這是大多數人使用最多的一個操作系統,當打開我的電腦的時候,映入眼簾的是大致是這樣一個一張圖:
也就是說在 windows 下,磁盤被分為了 C 盤, D 盤。。。。這樣的一個目錄結構。那對于 Linux呢,它的目錄結構是長啥樣,它有一個根目錄,而系統下的所有目錄都是從根目錄分離出去的,我們可以在 Ubuntu終端運行如下命令來查看Linux的目錄結構。
- tree -L 1 /
上述中,tree表示將當前目錄以樹的結構展示,-L表示的是要顯示當目錄的第幾層,1表示的是要顯示到第一層,最后面的 /表示的也就是Linux的根目錄,也就是說當前命令就是顯示根目錄下第一層目錄的信息,最終得到的結果如下所示:
為了更好地理解每個目錄所代表的意思,我們看如下所示的內容:
- /
- |----bin ----------------> 文件系統的起始位置,稱之為根
- |----boot ----------------> 存放系統啟動時讀取的文件,包括系統核心文件
- |----dev ----------------> 存放設備文件接口,如打印機,硬盤等外圍設備
- |----etc ----------------> 存放與系統設置和管理相關的文件,如用戶賬號、密碼等
- | |
- |----home -----------------> 存放用戶專屬目錄
- |----lib -----------------> 存放一些共享的函數庫
- |----misc -----------------> 一個空目錄,供管理員存放公共雜物
- |----proc -----------------> 存放系統核心和執行程序之間的信息
- |----root -----------------> 系統管理員(超級用戶)專用目錄
- |----sbin -----------------> 與 /bin 類似,存放用于系統引導和管理命令,通常供 root 使用
- |----tmp -----------------> 臨時目錄,供任何用戶存放臨時文件
- |----usr -----------------> 此目錄包含許多子目錄,用來存放系統命令和程序等信息
- |----var -----------------> 存放經常變動的文件,如日志文件,臨時文件,電子郵箱
說到這,就有必要再說一下 Linux下的路徑問題了,在Linux中,Linux的路徑分為絕對路徑和相對路徑
- 絕對路徑:指的是一個文件或者目錄從根目錄開始的完整的路徑
- 相對路徑:是指一個文件或者目錄相對于向前工作目錄的路徑
- 任何不以/和~開始的路徑均為相對路徑
說完了路徑,接下來要敘述的就是 Linux的文件類型的,Linux 內一切皆文件,那么對于 Linux 來說,其具有哪些文件類型呢,其主要有如下四種:
- 普通文件
- 目錄文件
- 鏈接文件:其作用類似于 windows下的快捷方式,它本身不包含內容,而是指向其他的文件或目錄
- 設備文件:存放在 /dev目錄下,如:hda,hdb,sda。。。
最后,在平時使用操作系統的時候,可能會涉及到掛載的操作,那掛載是什么意思呢?Linux啟動的時候,首先掛載的是根文件系統,之后可以自動或者手動掛載其他文件系統,這些文件系統要掛載到掛載點上,與虛擬文件系統和通用塊設備層建立聯系。
掛載,指的就是將設備文件中的頂級目錄連接到 Linux 根目錄下的某一目錄(最好是空目錄),訪問此目錄就等同于訪問設備文件。
上述就是基于用戶的角度對文件系統進行了一個概述,接下來從操作系統的角度,更進一步地闡述操作系統。
操作系統角度
文件系統的層次
在上述中,闡述掛載的時候說到一個概念,就是說 Linux在啟動的時候,首先掛載的是根文件系統,然后再自動或者手動掛載其他文件系統,這也是Linux中支持不同文件系統的原因,而支持各種不同文件系統的這種機制又是什么呢?說到這里,就有必要提到Linux的虛擬文件系統了,再敘述它的概念之前,我們先以宏觀的角度來看一下 Linux下的文件系統的一個結構:
由上圖可以知道,整個文件系統體系分為了三個層面,用戶層,內核層,硬件層,用戶層是通過API通過系統調用調用的方式訪問虛擬文件系統。在內核層,我們可以看到虛擬文件系統下連接了各種類型的文件系統,其是對不同的文件系統的抽象,為上層應用提供了統一的 API 接口;上圖內核層還有一層是各個文件系統之下的一層,這一層的作用是隱藏了不同硬件設備之間的細節,為內核提供了統一的 IO 操作接口。下面我們對整個文件系統從下到上對各個層的作用進行一個闡述:
- Device Driver(硬盤驅動):常見的硬盤類型有PATA,SATA,在Linux中,對于硬盤提供的驅動模塊一般都存放在內核目錄樹drivers/ata中,而對于一般通用的硬盤驅動,可能會直接被編譯到內核中。
- 通用塊設備層(General Block Device Layer):不同的硬盤,會提供不同的 IO 接口,對于內核來講,這種雜亂的接口是不利于管理的,因此就把這些接口進行了抽象,形成了一個統一的對外接口,這樣就不管你是什么存儲設備,操作他們的IO接口并沒有什么區別。
- 文件系統層:目前大多數Linux使用的是ex4,與此同時,btrfs也呼之欲出
- 虛擬文件系統:正如不同的存儲設備具有不同的 IO 接口,那么不同的文件系統也具有不用的 API,內核想實現的是不管是什么文件系統,都采用的是相同的 API 進行操作,所以 VFS 就做了一個抽象,提供了統一的 API 接口,使之可以對不同的文件系統采用同樣的操作。
文件的使用
上述中,我們介紹了文件系統的層次,那么基于這樣一個層次,我們又應該如何使用文件呢?下圖是一個使用文件的流程圖:
與其對應的代碼也比較簡單:
- fd = open(name, flag); /* 打開文件 */
- ...
- write(fd, ...); /* 寫數據 */
- ...
- close(fd); /* 關閉文件 */
上述就是往一個文件中寫數據的步驟,使用open系統調用打開文件,open的參數中包含文件的路徑名和文件名,使用write寫數據,其中write使用open所返回的文件描述符,使用完文件后,用close系統關閉文件,避免資源的泄露。
在打開了一個文件后,操作系統會跟蹤進程打開的所有文件,也就是說操作系統為每個進程維護一個打開文件表,文件表里的每一項代表的是文件描述符,所以說文件描述符是打開文件的標識。
操作系統在打開文件表中維護著打開文件的狀態和信息:
- 文件指針:系統跟蹤上次讀寫位置作為當前文件位置的指針,這個指針對于打開文件的某個進程來說是唯一的;
- 文件打開計數器:文件關閉時,操作系統必須重用其打開文件表目錄,否則表內空間就不夠。因為多個進程可能打開同一個文件,所以系統在刪除打開文件條目之前,必須等待最后一個進程關閉文件,該計數器跟蹤打開和關閉數量,當該計數為 0 時,系統關閉文件,刪除該條目;
- 文件磁盤位置:大多數文件操作都需要系統修改文件的數據,該信息保存在內存中,以免每個操作都從磁盤中讀取;
- 訪問權限:每個進程打開文件都需要又一個訪問模式(創建、只讀、讀寫、添加等),該信息保存在進程的打開文件表中。
文件系統的IO類型
根據文件系統的讀寫差異,可以將IO分為四種類型:
- 緩沖 I/O:是指利用標準庫緩存來加速文件的訪問,而標準庫內部再通過系統調度訪問文件。
- 非緩沖I/O:是指直接通過系統調用來訪問文件,不再經過標準庫緩存
此處標準庫緩存指的是利用棧、隊列等一些數據結構進行的資源調度,而不是頁緩存。無論是否是緩沖IO,都會通過系統調用頁緩存來減少IO次數
根據是否利用操作系統的頁緩存,可以把文件I/O分為直接I/O與非直接I/O
- 直接I/O:是指跳過操作系統的頁緩存,直接跟文件系統交互來訪問文件
- 非直接I/O:文件讀寫的時候,先要經過系統的頁緩存,然后再由內核或者是額外的系統調用,真正寫入存儲設備
通常,我們的 IO 都是非直接I/O
根據應用程序是都阻塞自身運行,可以把文件 I/O 分為阻塞 I/O 和非阻塞 I/O
- 阻塞I/O,是指應用程序執行 I/O 操作之后,如果沒有獲得響應,就會阻塞當前的線程,自然不能執行其他任務
- 非阻塞I/O,是指應用程序執行 I/O 操作之后,不會阻塞當前的線程,可以繼續執行其他的任務,隨后再通過輪詢或者事件通知的形式,獲得調用的結果
通常情況下I/O都是阻塞的。網絡編程中是非阻塞的I/O,用在網絡套接字的 I/O 中
根據是否等待響應結果,把文件分為同步IO和異步IO
- 同步IO:應用程序在執行IO操作之后,要一直等到整個IO完成后,才獲得 IO 響應
- 異步IO:是指應用程序在執行IO操作之后,不用等待完成,可以繼續做之后的事情,等到 IO 完成的時候,會通過事件通知的方式,告訴應用程序
文件的存儲
Linux中所有文件都有一個唯一與之對應的索引節點,索引節點記錄了文件的元數據,操作系統不是通過文件名,而是通過索引節點來管理文件,用目錄項來描述文件之間的關系。
索引節點,也被稱之為是inode,用來記錄文件的元數據,元數據就包括:node編號、文件大小、訪問權限、修改日期、數據的位置等。
目錄項,也被稱為dentry,用來記錄文件的名字、索引節點指針及與其他目錄項的關聯關系。多個關聯的目錄項,也就構成了文件系統的目錄結構。
因此,索引節點相當的于文件的指針,目錄項維護著文件的樹型關系
下面是文件存儲各個部分邏輯關系的一個示意圖:
上圖中,超級塊用來存儲著整個文件系統的狀態,索引節點區用來存儲索引節點,數據塊區用來存儲文件的數據,他們之間的關系在圖中也很清除了,就不進行贅述了。
總結
Linux是一個很龐大也很優秀的系統,在嵌入式行業也應用廣泛,筆者對于 Linux的接觸不深,這也是最近對于學習 Linux文件系統時的一個總結,如果文中出現問題,歡迎各位及時給我提出來呀,我將不勝感激~
本文轉載自微信公眾號「wenzi嵌入式軟件」,可以通過以下二維碼關注。轉載本文請聯系wenzi嵌入式軟件公眾號。