Linux 檔案系統:從 Everything is a File 聊到 OpenBMC

更新 發佈閱讀 15 分鐘

在開始深入探討 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 所指定。



留言
avatar-img
L'Angolo di Embedded
25會員
26內容數
這裡會有一些我對於OpenBMC, Embedded Software的學習與經驗分享, 本來只在Line社群跟大家互動, 但是有夥伴提出想要看到歷史文章的需求, 於是我決定把它放到這裡, 努力磨練自己的技術和文筆。
L'Angolo di Embedded 的其他內容
2025/09/22
一起探討嵌入式系統開發中至關重要的建置系統與GNU工具鏈,涵蓋預處理、編譯、組譯、連結、定位與載入等步驟,並比較原生編譯與交叉編譯,最後介紹Make和Meson建置系統,以及提升嵌入式程式碼開發效率的技巧。
2025/09/22
一起探討嵌入式系統開發中至關重要的建置系統與GNU工具鏈,涵蓋預處理、編譯、組譯、連結、定位與載入等步驟,並比較原生編譯與交叉編譯,最後介紹Make和Meson建置系統,以及提升嵌入式程式碼開發效率的技巧。
2025/09/16
D-Bus基本工具busctl、dbus-monitor、dbus-send的使用方法與實例,並涵蓋Method、Property、Signal、Interface、D-Bus Signature等核心概念及進階容器型別應用,搭配實作範例與練習,助你輕鬆掌握D-Bus程式間通訊技巧。
Thumbnail
2025/09/16
D-Bus基本工具busctl、dbus-monitor、dbus-send的使用方法與實例,並涵蓋Method、Property、Signal、Interface、D-Bus Signature等核心概念及進階容器型別應用,搭配實作範例與練習,助你輕鬆掌握D-Bus程式間通訊技巧。
Thumbnail
2025/09/15
D-Bus是Linux系統中進程間通信的關鍵技術,它提供了一種標準化、高效的機制,讓不同的程序能夠方便地互相溝通協作。本文將用通俗易懂的語言解釋D-Bus的核心概念,包括Bus、Bus Name、Object Path和Interface等,並通過故事方式幫助你理解其工作原理。
2025/09/15
D-Bus是Linux系統中進程間通信的關鍵技術,它提供了一種標準化、高效的機制,讓不同的程序能夠方便地互相溝通協作。本文將用通俗易懂的語言解釋D-Bus的核心概念,包括Bus、Bus Name、Object Path和Interface等,並通過故事方式幫助你理解其工作原理。
看更多