從編程視角看 Linux 文件系統

一、引言

Linux 文件系統是 Linux 操作系統的核心組件之一,它爲用戶和應用程序提供了統一的文件訪問接口,屏蔽了底層存儲設備的差異。從編程視角來看,理解 Linux 文件系統的結構和原理對於開發高效、可靠的文件操作程序至關重要。

POSIX 接口介紹

POSIXPortable Operating System Interface)接口是 Linux 系統中用於文件操作的一組標準接口,它爲應用程序提供了統一的文件訪問方式。以下是一些常用的 POSIX 接口:

1. open() 接口

open() 函數用於打開一個文件,其函數原型爲:

int open(const char *pathname, int flags, mode_t mode);

2. close() 接口

close() 函數用於關閉一個已經打開的文件,其函數原型爲:

int close(int fd);

3. read() 接口

read() 函數用於從文件中讀取數據,其函數原型爲:

ssize_t read(int fd, void *buf, size_t count);

4. write() 接口

write() 函數用於向文件中寫入數據,其函數原型爲:

ssize_t write(int fd, const void *buf, size_t count);

三、Linux 文件編程示例

以下是一個簡單的文件讀寫示例,展示瞭如何使用 POSIX 接口進行文件操作:

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

intmain() {
    int fd;
    char *filename = "example.txt";
    char buf[128];

    // 打開文件
    fd = open(filename, O_RDWR | O_CREAT, 0644);
    if (fd < 0) {
        perror("Failed to open file");
        exit(EXIT_FAILURE);
    }

    // 寫入數據
    constchar *data = "Hello, Linux file system!";
    ssize_t written = write(fd, data, strlen(data));
    if (written < 0) {
        perror("Failed to write to file");
        close(fd);
        exit(EXIT_FAILURE);
    }

    // 讀取數據
    ssize_t read_bytes = read(fd, buf, sizeof(buf));
    if (read_bytes < 0) {
        perror("Failed to read from file");
        close(fd);
        exit(EXIT_FAILURE);
    }

    buf[read_bytes] = '\0';
    printf("Read from file: %s\n", buf);

    // 關閉文件
    close(fd);
    return0;
}

四、文件 IO Stack 介紹

Linux 文件 IO Stack 是一個分層的結構,從用戶空間到內核空間再到硬件設備,主要包括以下幾個層次:

1. LibC 層

LibC 是用戶空間的標準 C 庫,它是 GNU C Library 的一部分,封裝了 POSIX 接口,爲應用程序提供方便的文件操作函數。例如,fopen()fread() 和 fwrite() 等函數都是 LibC 提供的。

2. POSIX 接口層

POSIX 接口層定義了標準的文件操作接口,如 open()close()read() 和 write() 等。這些接口在用戶空間和內核空間之間起到了橋樑作用。

3. VFS(虛擬文件系統)層

VFS 是 Linux 內核中的一個抽象層,它爲各種具體的文件系統(如 ext4XFS 等)提供了統一的接口。

4. 具體文件系統層(XFS)

XFS 是一種高性能的基於塊的(Block-based)文件系統。

5. 塊設備層

塊設備層負責與磁盤等存儲設備進行交互,實現文件數據的讀寫操作。它使用塊設備驅動程序(Block Device Driver)來管理磁盤設備。

五、VFS/XFS/Disks 原理介紹

1. VFS 數據結構

VFS 使用以下關鍵數據結構來管理文件系統:

關係

2. XFS 原理

XFS 是一種高性能的文件系統,它具有以下特點:

XFS 在磁盤上的 layout

XFS 文件系統在磁盤上的佈局通常包括以下幾個部分:

  1. 1. 超級塊(Superblock)

    struct xfs_super_block {
        __be32 sb_magicnum;       /* 魔術數,用於標識文件系統 */
        __be32 sb_blocksize;      /* 文件系統的塊大小 */
        __be64 sb_dblocks;        /* 文件系統中的總塊數 */
        __be64 sb_rblocks;        /* 預留塊數 */
        __be64 sb_rextents;       /* 預留空間的總擴展數 */
        __u8 sb_uuid[16];         /* 文件系統的 UUID */
        __be32 sb_logstart;       /* 日誌區域的起始塊號 */
        /* 其他字段 */
    };
  1. 2. 日誌區域(Log)
  1. 3. 可變長度數據區域(AGF and AGFL)
  1. 4. B+ 樹索引區域(B+ Tree Indexes)
  1. 5. 用戶數據區域(User Data Area)

3. 磁盤(Disks)原理

磁盤是一種塊設備,它使用塊作爲基本的存儲單位。磁盤的物理結構包括盤片、磁頭、磁道和扇區。磁盤的尋址方式包括 CHSCylinder-Head-Sector)地址和 LBALogical Block Address)地址。

六、IO Stack 的讀寫流程

1. read 操作流程

  1. 1. 用戶空間發起 read 請求:應用程序調用 read() 函數,請求從文件中讀取數據。

  2. 2. LibC 庫處理LibC 庫對 read() 請求進行初步封裝和處理,如參數檢查、緩衝區管理等。

  3. 3. 進入內核 POSIX 接口層:進入內核後,POSIX 接口層將請求轉交給 VFS 層。

  4. 4. VFS 層解析和轉發VFS 層通過文件系統的掛載點信息,確定目標文件所屬的具體文件系統類型(如 XFS)。

  5. 5. 具體文件系統處理讀請求XFS 文件系統根據請求的文件偏移量和大小,從磁盤上讀取相應的數據塊,並通過緩衝區管理機制將數據傳遞給 VFS 層。文件系統可能會使用緩存(如頁緩存)來提高讀取性能

  6. 6. VFS 層返回數據VFS 層將數據返回給 POSIX 接口層,再由 POSIX 接口層返回給用戶空間的 LibC 庫。

  7. 7. LibC 庫返回數據LibC 庫將讀取到的數據返回給應用程序,完成整個 read 操作流程。

2. write 操作流程

  1. 1. 用戶空間發起 write 請求:應用程序調用 write() 函數,將數據寫入文件。

  2. 2. LibC 庫處理LibC 庫對 write() 請求進行初步封裝和處理,如參數檢查、緩衝區管理等。

  3. 3. 進入內核 POSIX 接口層:進入內核後,POSIX 接口層將請求轉交給 VFS 層。

  4. 4. VFS 層解析和轉發VFS 層通過文件系統的掛載點信息,確定目標文件所屬的具體文件系統類型(如 XFS)。

  5. 5. 具體文件系統處理寫請求XFS 文件系統接收寫入請求,將數據寫入緩衝區,並根據需要同步到磁盤。文件系統可能會使用緩存(如頁緩存)來提高寫入性能

  6. 6. VFS 層處理完成並返回寫入結果VFS 層將寫入結果返回給 POSIX 接口層,再由 POSIX 接口層返回給用戶空間的 LibC 庫。

  7. 7. LibC 庫返回結果LibC 庫將寫入結果返回給應用程序,完成整個 write 操作流程。

七、Linux Storage IO Stack

網址:https://www.thomas-krenn.com/en/wiki/Linux_Storage_Stack_Diagram

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/tH-xhZ9JXcfAVw_X9tc_9Q