從編程視角看 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