從編程視角看 Linux 文件系統
一、引言
Linux
文件系統是 Linux
操作系統的核心組件之一,它爲用戶和應用程序提供了統一的文件訪問接口,屏蔽了底層存儲設備的差異。從編程視角來看,理解 Linux
文件系統的結構和原理對於開發高效、可靠的文件操作程序至關重要。
POSIX 接口介紹
POSIX(Portable Operating System Interface
)接口是 Linux
系統中用於文件操作的一組標準接口,它爲應用程序提供了統一的文件訪問方式。以下是一些常用的 POSIX
接口:
1. open() 接口
open()
函數用於打開一個文件,其函數原型爲:
int open(const char *pathname, int flags, mode_t mode);
-
•
pathname
:文件路徑。 -
•
flags
:文件打開的標誌,如O_RDONLY
(只讀)、O_WRONLY
(只寫)、O_RDWR
(讀寫)等。 -
•
mode
:當創建新文件時的權限(可選)。該參數用於指定文件的權限,當創建新文件時生效;對於已存在的文件,mode
參數通常被忽略,但某些系統可能會使用它進行權限檢查。
2. close() 接口
close()
函數用於關閉一個已經打開的文件,其函數原型爲:
int close(int fd);
- •
fd
:文件描述符。
3. read() 接口
read()
函數用於從文件中讀取數據,其函數原型爲:
ssize_t read(int fd, void *buf, size_t count);
-
•
fd
:文件描述符。 -
•
buf
:讀取數據的緩衝區。 -
•
count
:讀取的字節數。
4. write() 接口
write()
函數用於向文件中寫入數據,其函數原型爲:
ssize_t write(int fd, const void *buf, size_t count);
-
•
fd
:文件描述符。 -
•
buf
:要寫入的數據緩衝區。 -
•
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
內核中的一個抽象層,它爲各種具體的文件系統(如 ext4
、XFS
等)提供了統一的接口。
4. 具體文件系統層(XFS)
XFS
是一種高性能的基於塊的(Block-based)文件系統。
5. 塊設備層
塊設備層負責與磁盤等存儲設備進行交互,實現文件數據的讀寫操作。它使用塊設備驅動程序(Block Device Driver
)來管理磁盤設備。
五、VFS/XFS/Disks 原理介紹
1. VFS 數據結構
VFS
使用以下關鍵數據結構來管理文件系統:
-
• 超級塊(super_block):存儲文件系統的元數據,如文件系統的類型、塊大小、塊數量等。超級塊是整個文件系統的元數據存儲點,包含了文件系統的全局信息,如類型、大小等,是文件系統的核心配置結構。
-
• 索引節點(inode):存儲文件的元數據,如文件大小、權限、創建時間等。索引節點(
inode
)與文件一一對應,每個文件都有一個唯一的inode
,存儲了文件的元數據,如文件大小、權限、訪問時間等。 -
• 目錄項(dentry):存儲文件名和索引節點的映射關係。
-
• 文件對象(file):代表一個打開的文件,是進程與文件交互的接口。
關係
-
• 超級塊(super_block):
-
• 超級塊是整個文件系統的元數據存儲點,它包含了文件系統的全局信息,如類型、大小等。通過超級塊,
VFS
可以識別和管理不同的文件系統。 -
• 索引節點(inode):
-
• 索引節點(
inode
)與文件一一對應,每個文件都有一個唯一的inode
。inode
存儲了文件的元數據,如文件大小、權限、訪問時間等。它是文件系統中文件的抽象表示,不包含文件名。 -
• 目錄項(dentry):
-
• 目錄項(
dentry
)將文件名與inode
連接起來。它存在於目錄中,包含文件名和指向該文件inode
的指針。這種映射關係使得用戶可以通過文件名訪問文件,而文件系統則通過inode
管理文件的實際數據和元數據。 -
• 文件對象(file):
-
• 文件對象(
file
)代表一個打開的文件,它是進程與文件交互的接口。file
對象包含文件的當前讀寫位置、文件標誌等信息,並通過dentry
和inode
與文件系統中的文件建立聯繫。
2. XFS 原理
XFS 是一種高性能的文件系統,它具有以下特點:
-
• 日誌機制:XFS 使用日誌來記錄文件系統的元數據操作,以保證文件系統的完整性。
-
• 元數據管理:XFS 使用位圖(Bitmap)來管理文件系統的元數據,包括 inode 位圖和塊位圖。
-
• 數據佈局:XFS 使用塊組(Block Group)來組織數據,每個塊組包含一定數量的塊。塊組可以獨立管理,包含數據、元數據和日誌信息,用於提高數據存儲和訪問效率。
XFS 在磁盤上的 layout
XFS
文件系統在磁盤上的佈局通常包括以下幾個部分:
-
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; /* 日誌區域的起始塊號 */ /* 其他字段 */ };
- • 位於磁盤的固定位置(通常是第 0 塊),包含文件系統的元數據,如文件系統版本、UUID、塊大小、日誌信息等。它是 XFS 文件系統的核心結構,記錄了文件系統的整體狀態和配置。
- 2. 日誌區域(Log)
- • 用於記錄文件系統的元數據操作,以保證文件系統的完整性。日誌通常位於磁盤的特定區域,可以是內嵌的(位於文件系統內部)或獨立的(位於單獨的設備上)。
- 3. 可變長度數據區域(AGF and AGFL)
- • 可變長度數據區域(
Allocation Group Free and Freelist
)用於管理文件系統的空間分配。它包含每個分配組的空閒空間信息,幫助文件系統高效地分配和回收空間。
- 4. B+ 樹索引區域(B+ Tree Indexes)
- • B+ 樹索引區域用於快速查找和管理文件系統中的數據。XFS 使用 B+ 樹來存儲文件的元數據,如 inode 和目錄項,以提高數據的訪問效率。
- 5. 用戶數據區域(User Data Area)
- • 這是文件系統中最大的區域,用於存儲用戶數據。它被劃分爲多個分配組(
Allocation Groups
),每個分配組包含多個塊組(Block Groups
)。每個塊組可以獨立管理,包括存儲數據、元數據和日誌信息。
3. 磁盤(Disks)原理
磁盤是一種塊設備,它使用塊作爲基本的存儲單位。磁盤的物理結構包括盤片、磁頭、磁道和扇區。磁盤的尋址方式包括 CHS
(Cylinder-Head-Sector
)地址和 LBA
(Logical Block Address
)地址。
六、IO Stack 的讀寫流程
1. read 操作流程
-
1. 用戶空間發起 read 請求:應用程序調用
read()
函數,請求從文件中讀取數據。 -
2. LibC 庫處理:
LibC
庫對read()
請求進行初步封裝和處理,如參數檢查、緩衝區管理等。 -
3. 進入內核 POSIX 接口層:進入內核後,
POSIX
接口層將請求轉交給VFS
層。 -
4. VFS 層解析和轉發:
VFS
層通過文件系統的掛載點信息,確定目標文件所屬的具體文件系統類型(如XFS
)。 -
5. 具體文件系統處理讀請求:
XFS
文件系統根據請求的文件偏移量和大小,從磁盤上讀取相應的數據塊,並通過緩衝區管理機制將數據傳遞給VFS
層。文件系統可能會使用緩存(如頁緩存)來提高讀取性能。 -
6. VFS 層返回數據:
VFS
層將數據返回給POSIX
接口層,再由POSIX
接口層返回給用戶空間的LibC
庫。 -
7. LibC 庫返回數據:
LibC
庫將讀取到的數據返回給應用程序,完成整個read
操作流程。
2. write 操作流程
-
1. 用戶空間發起 write 請求:應用程序調用
write()
函數,將數據寫入文件。 -
2. LibC 庫處理:
LibC
庫對write()
請求進行初步封裝和處理,如參數檢查、緩衝區管理等。 -
3. 進入內核 POSIX 接口層:進入內核後,
POSIX
接口層將請求轉交給VFS
層。 -
4. VFS 層解析和轉發:
VFS
層通過文件系統的掛載點信息,確定目標文件所屬的具體文件系統類型(如XFS
)。 -
5. 具體文件系統處理寫請求:
XFS
文件系統接收寫入請求,將數據寫入緩衝區,並根據需要同步到磁盤。文件系統可能會使用緩存(如頁緩存)來提高寫入性能。 -
6. VFS 層處理完成並返回寫入結果:
VFS
層將寫入結果返回給POSIX
接口層,再由POSIX
接口層返回給用戶空間的LibC
庫。 -
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