在開始深入探討 Linux 檔案系統之前,我們得先搞懂 Linux 最核心的設計哲學:"Everything Is A File"。
想像一下,Linux 就像一間超整齊的圖書館,而「Everything Is A File」就是它的終極整理法則。不管是硬碟、印表機、網路卡,還是記憶體使用狀況,在 Linux 眼中通通都是「檔案」,都能用同樣的方式來存取和操作。就像樂高積木有統一接面一樣,這種設計讓系統各個組件可以輕鬆靈活的去搭配使用。
當我們用 OpenBMC 開發嵌入式系統時,這個概念就變得超重要了!想要讀取溫度感測器、調整風扇轉速,或是管理電源狀態?在 Linux 世界裡,這些硬體操作通通都是透過檔案介面來搞定。舉個例子,當你需要跟 USB 串列埠溝通時,其實就是在讀寫 /dev/ttyUSB0 這個檔案而已。
這種設計的好處就像瑞士刀一樣萬用:同一個程式只要會「開啟、讀取、寫入」這三招,既可以處理一般的文字檔案,也可以直接跟硬體設備對話。所以說,了解 Linux 的檔案系統架構,就像打通了任督二脈一樣!一旦你掌握了這套邏輯,不管是韌體的操作、存取還是控制,你都會有明確的方向,知道該往哪裡下手。(講得好像很簡單,實際上還是有很多細節需要注意)
一:檔案路徑與存取機制
絕對路徑與相對路徑的差異
在 Linux 檔案系統中,每個檔案都透過路徑來定位。想像檔案系統就像一棟大樓,我們有兩種方式告訴別人怎麼找到某個房間:
絕對路徑就像完整地址,從根目錄(/)開始,例如 /path/to/file。就像說「台北市信義區市府路1號3樓301室」一樣,不管你現在在哪裡,按照這個地址都能找到同一個地方。每個斜線分隔一個目錄層級,開頭的斜線代表從檔案階層的最頂端開始。
相對路徑不以斜線開頭,例如 path/to/file,就像用「從這裡出發」的方式指路,從你當前的工作目錄開始算起。相對路徑讓腳本和程式更有彈性,不會被綁死在特定的目錄結構上。
檔案內容與操作特性
在 Linux 中,普通檔案(regular file)本質上就是位元組資料流,系統對檔案內容不做任何假設或限制。這種抽象化設計讓檔案系統能統一處理各種資料類型,從純文字到二進位執行檔都用同樣的機制管理。
Linux 檔案系統為每個開啟的檔案維護檔案位置指標(file offset)。當程式執行讀取操作時,核心會自動追蹤當前位置並遞增偏移量,讓下次讀取能從正確的位置開始。這種自動化的位置管理機制讓開發者能專注於業務邏輯,而不需要手動管理檔案指標。
檔案支援 truncate 操作,可以截斷到指定長度,或清空內容重新開始。比較特殊的是,檔案也能被「拉長」到比原始大小更大的尺寸。這種稀疏檔案(sparse file)的設計讓我們可以定義一個邏輯上很大的檔案,但實際上不佔用相應的磁碟空間,直到真正寫入資料時才分配儲存區塊。這個特性在處理大型資料集或虛擬化環境中特別有用。
二:Linux 檔案系統目錄架構
根目錄結構與 FHS 標準
Linux 檔案系統遵循檔案系統階層標準(Filesystem Hierarchy Standard, FHS)。這個標準定義了 Linux 作業系統中主要目錄的結構與內容,讓使用者能夠了解已安裝的軟體通常會放在哪個目錄下。
根目錄(/)是所有 Linux 檔案系統的起始點。每個檔案和目錄都從根目錄開始,只有 root 使用者有權限在這個目錄下進行寫入操作。需要注意的是,/root 是 root 使用者的家目錄,它與根目錄 / 是不同的概念。
重要系統目錄詳解
Linux 的目錄結構遵循 Filesystem Hierarchy Standard (FHS),每個目錄都有特定的用途和意義。從 /bin、/etc、/var 到 /proc,每個目錄背後都有深層的設計邏輯。
關於 Linux 目錄結構的詳細說明,建議大家仔細閱讀鳥哥的 Linux 私房菜中的「Linux 的檔案權限與目錄配置」章節。
而在 OpenBMC 系統中,這些目錄結構有其特殊的應用場景。建議也參考 OpenBMC 的 Flash Layout 文件,了解 BMC 系統中檔案系統的配置和各目錄的實際用途。
三:inode 機制與檔案系統內部運作
inode 的核心概念
在 Linux 檔案系統中,每個檔案最終都透過一個叫做 inode(Index Node)的資料結構來管理。把 inode 想像成檔案的「身分證」,它記錄了這個檔案的所有重要身家資料。
inode 就像一本檔案的履歷表,裡面詳細記載著:
- 檔案在磁碟上的實際位置
- 時間戳記(建立、修改、存取時間)
- 檔案擁有者是誰
- 檔案有多大
- 存取權限設定
這個「履歷表」相當精簡,通常只有大約 128 位元組。但這裡有個有趣的地方:檔案名稱竟然不在 inode 裡面! 聽起來很奇怪對吧?就像身分證上記錄了你的所有資料,但唯獨沒有寫你的名字一樣。
目錄與檔案名稱的對應關係
那麼,既然檔案名稱不在 inode 裡,系統是怎麼知道「某某檔案」對應到哪個 inode 的呢?答案就是「目錄」!
目錄其實也是一種特殊的檔案,它有自己的 inode,但裡面裝的不是一般資料,而是一張「通訊錄」。這張通訊錄記錄著:檔案名稱 → inode 編號的對應關係。
想像目錄就像飯店櫃檯的房客名單,上面寫著「張三住301號房」、「李四住302號房」。當你說要找「張三」時,櫃檯就查名單,然後告訴你去301號房。目錄的運作方式就是這樣,從根目錄(/)開始,一層一層建立起整個檔案系統的階層結構。
inode 設計的優點
為什麼要把檔案名稱從 inode 中分離出來?這種設計有個好處:同一個檔案可以有多個名字!想想看,一個人的身分證資料(年齡、住址、指紋等)是固定的,但他可能有小名、綽號、英文名等不同稱呼。inode 就像身分證資料,記錄檔案的所有屬性(大小、權限、磁碟位置等),而檔案名稱就像這些不同的稱呼。
這就是「連結(link)」功能的基礎!透過這種設計,我們可以在不同地方用不同名稱存取同一個檔案,但實際上讀取到的內容完全一樣,因為它們都指向同一個 inode。
四:檔案連結機制
Hard Links的機制
Hard Links就像給同一個人辦了好幾張不同名字的會員卡,但這些卡片全部指向同一個帳戶。多個檔案名稱可以指向相同的 inode,也就是說,它們其實是完全相同的檔案,擁有相同的內容、權限和所有屬性。
不過Hard Links有個重要限制:只能在同一個檔案系統內使用。為什麼?因為 inode 編號是檔案系統內部的「身分證號碼」,每個檔案系統都有自己的編號系統。就像台灣的身分證字號在美國沒有意義一樣,A 檔案系統的 inode 123 在 B 檔案系統中根本不存在。
Hard Links在概念上跟目錄項目完全一樣,它們的運作方式也相同。這帶來了一個很棒的特性:引用計數(reference counting)。
想像每個 inode 都有一個「被多少人需要」的計數器。當你建立一個硬連結時,計數器就 +1;當你刪除一個檔案名稱時,計數器就 -1。只有當計數器歸零時,系統才會真正刪除檔案內容。
所以如果你建立了Hard Links後刪除原始檔案,別擔心!檔案內容還是好好的存在,因為還有其他「名字」在引用它。
Symbolic Links的特性
Symbolic Links就像「地址便條紙」,它不直接指向檔案本體,而是寫著「去某某路徑找檔案」。符號連結有自己的 inode,但這個 inode 指向的磁碟位置只存放一個路徑字串。
當系統要透過Symbolic Links存取檔案時,就像按照便條紙上的地址去找東西:先讀取Symbolic Links的內容(路徑),然後根據這個路徑再去找真正的檔案。所以存取符號連結需要多一個步驟,效能上會稍微慢一點點。
五:特殊檔案與設備管理
特殊檔案的概念
到目前為止,我們討論的都是普通檔案,但 Linux 中還有特殊檔案的概念。如前面提到的 dev/ttyUSB0 就是一個例子。特殊檔案的作用是將硬體設備映射到「一切皆檔案」的範式中。
特殊檔案實際上是以檔案形式表示的核心物件。它們可以是字元設備或區塊設備,也可以是命名管道或套接字。
字元設備檔案
字元設備用於任何時候與設備的互動都是線性位元組佇列的情況。一個很好的例子是鍵盤。當你在鍵盤上按鍵時,會有一系列與你按鍵時的條目相關的位元組序列,所以這將是字元設備的一個很好的例子。
一般系統常見的字元設備:
- 串列埠(
/dev/ttyS0、/dev/ttyUSB0) - 終端機設備(
/dev/console、/dev/tty) - 隨機數產生器(
/dev/random、/dev/urandom) - null 設備(
/dev/null) - 鍵盤、滑鼠等輸入設備
在 OpenBMC 系統中常用的字元設備:
- UART 控制器(
/dev/ttyS0- 通常用於 console) - I²C 控制器(
/dev/i2c-0、/dev/i2c-1- 用於感測器通訊) - SPI 控制器(
/dev/spidev0.0- 用於 Flash 存取) - 看門狗計時器(
/dev/watchdog) - GPIO 設備(透過
/sys/class/gpio/介面存取)
區塊設備檔案
區塊設備就像「批發商」,專門處理大量資料的批次存取。它們以「區塊」為單位傳輸資料,通常一個區塊是 512 位元組或更大。
區塊設備的特性包括:
- 提供緩衝存取硬體設備
- 允許隨機存取(可以跳到任何位置讀寫資料)
- 資料以區塊為單位傳輸
- 常見例子:硬碟、SSD、USB 隨身碟、SD 卡
在 OpenBMC 中的應用整合
除了 /dev/ 目錄外,現代 Linux 系統還透過 sysfs 檔案系統(掛載在 /sys/)來管理設備:
/sys/bus/i2c/devices/- I²C 設備資訊/sys/class/hwmon/- 硬體監控感測器/sys/class/gpio/- GPIO 控制介面
在 OpenBMC 系統中,這些特殊檔案讓 BMC 能夠與各種硬體無縫互動:
- 感測器讀取:透過
/dev/i2c-*字元設備存取溫度、電壓感測器 - GPIO 控制:透過
/sys/class/gpio或/dev/gpiochip*控制 LED、開關 - Console 通信:透過
/dev/ttyS*字元設備進行串列埠除錯 - 儲存管理:透過
/dev/mmcblk*區塊設備存取 eMMC 或 SD 卡
理解這些設備檔案的工作原理對於 OpenBMC 開發者來說我覺得算重要,因為它們是系統與硬體互動的主要介面。
六:OpenBMC 檔案系統架構
OpenBMC 的檔案系統設計哲學
OpenBMC 作為一個專為基板管理控制器設計的 Linux 發行版,採用了獨特的檔案系統架構來滿足嵌入式系統的特殊需求。這個架構必須在有限的儲存空間(通常少於 32MB 的快閃記憶體)和記憶體(通常少於 256MB RAM)環境中提供可靠的系統管理功能。
唯讀根檔案系統與覆蓋層設計
OpenBMC 採用唯讀根檔案系統的設計理念。大部分檔案系統內容,包括所有可執行檔案和靜態資料檔案,都儲存在使用 xz 壓縮的唯讀 squashfs 檔案系統中。這種設計帶來了幾個重要優勢:
空間效率:透過壓縮,能夠在有限的快閃記憶體中儲存更多的內容。
系統穩定性:唯讀檔案系統防止意外的檔案損壞,這在工業環境中特別重要。
更新安全性:每個檔案系統更新映像都必須是獨立的 squashfs 映像,因為不支援增量內容合併。
分層儲存架構
OpenBMC 支援多種檔案系統類型的程式碼更新:
- JFFS2 on MTD 分割區
這是 OpenBMC 的預設檔案系統配置。它使用區塊模擬驅動程式(mtdblock)將唯讀 squashfs 檔案系統內容儲存在 MTD 分割區中。第二個 MTD 分割區使用 JFFS2 檔案系統掛載為讀寫。
這個讀寫檔案系統掛載在整個檔案系統空間上,允許所有檔案和目錄被寫入。這種檔案系統堆疊需要從 initramfs 執行掛載。initramfs 由基於 busybox 的基本系統和三個自定義腳本(init、shutdown 和 update)組成,這些腳本按名稱定位 MTD 分割區。 - UBI on MTD 分割區
squashfs 內容儲存在使用 UBI 區塊模擬驅動程式(ubiblock)的靜態 UBI 卷中。為了儲存檔案更新,UBIFS 卷用於/var並使用 overlayfs 掛載在/etc和/home目錄上。
記憶體最佳化策略
在程式碼更新模式下,squashfs 映像和讀寫檔案系統中的白名單檔案被 initramfs 複製到 RAM 中,並用於組裝根 overlayfs 實例,留下快閃記憶體空閒以在執行時修改安裝映像。
這種設計在基於 AST2400 和 AST2500 系統單晶片控制器的 BMC 系統中特別有效,這些控制器支援 1 到 2GB 的 DDR RAM,而附加的快閃儲存通常為幾十 MB,因此將檔案系統暫存到 RAM 不是問題。
OpenBMC 特有的目錄結構
OpenBMC 努力遵循檔案系統階層標準(FHS)。具體來說:
/run:儲存當前開機臨時的資料。/var:儲存大部分應用程式資料。/etc:繼續儲存一些資訊在系統配置資料目錄;這主要是傳統配置,如網路位址、使用者識別和 ssh 主機金鑰。
tmpfs 的使用:/tmp、/run 等使用 tmpfs,而 /dev、/proc 和 /sys 由它們正常的核心特殊檔案系統支援,如 FHS 所指定。
