在昨天的內容中,我們了解了 network namespace
與 system call 以及 proc
檔案系統之間的關聯。為了更深入理解 proc
檔案系統,我們首先需要了解 Linux 檔案系統的基礎架構,也就是虛擬檔案系統 (Virtual File System, VFS)。
VFS
是 Linux Kernel 中的一層抽象,透過 system call 和 VFS
的抽象,Linux 隔離了應用程式與實際的檔案系統,使得應用程式能夠以通用的 system call 來操作、讀取和寫入不同底層實現的檔案系統。
在 VFS
系統中,有幾個重要的元素:inode
、dentry
和 file
。
首先,inode
代表的是一個檔案,並且提供了該檔案的 metadata。
// include/linux/fs.h
struct inode {
umode_t i_mode; // 存取權限
kuid_t i_uid; // 檔案擁有者
kgid_t i_gid; // 檔案擁有群
...
unsigned long i_ino; // inode id
...
loff_t i_size; // 檔案大小
struct timespec64 i_atime; // 存取時間
struct timespec64 i_mtime; // 修改時間
...
} __randomize_layout;
在 inode
結構中,包含了如存取權限 (i_mode
)、檔案擁有者 (i_uid
, i_gid
)、檔案大小 (i_size
)、存取時間 (i_atime
)、修改時間 (i_mtime
) 等資訊。另一個關鍵欄位是 inode number (i_ino)
,每個 inode
都有一個唯一的 inode number,在單個檔案系統中是唯一的。
接下來是 dentry
,dentry
是 directory entry
的縮寫,代表的是一個目錄或檔案。
// include/linux/dcache.h
struct dentry {
struct dentry *d_parent; // 父目錄
struct qstr d_name; // 目錄或檔案名稱
struct inode *d_inode; // 指向目錄或檔案的 inode
...
struct list_head d_child; // 子目錄或檔案的清單
...
} __randomize_layout;
dentry
中,最重要的欄位之一是 d_name
,它表示目錄或檔案的名稱。在 Linux 檔案系統中,所有的檔案和目錄是以樹狀結構展開的,所有檔案都從根目錄 /
開始。透過 d_parent
指標以及 d_child
List,我們可以在整個檔案系統中移動。例如,當我們想存取 /var/log/dpkg.log
時,Kernel 會從根目錄的 dentry
開始,逐步經過 var
和 log
,直到找到 dpkg.log
的 dentry
。
值得注意的是,dentry
只代表路徑而不代表實際的檔案。因此,dentry
結構內的 d_inode
指標會指向實際代表檔案的 inode
。另外不論是實際保存資料的檔案或著是子目錄,從系統的角度來看都是檔案,所以都會有inode。
這樣的結構也意味著,可能有多個 dentry
指向相同的 inode
。這種情況通常發生在我們使用 ln
指令建立硬連結之後,兩個不同路徑在底層指向同一個檔案。由於檔案權限等屬性保存在 inode
中,修改其中一個檔案的權限會影響到所有指向同一 inode
的路徑。
// include/linux/fs.h
struct file {
...
struct path f_path; // path
struct inode *f_inode; // 指向 inode
...
};
// include/linux/path.h
struct path {
struct vfsmount *mnt;
struct dentry *dentry; // 指向 dentry
} __randomize_layout;
最後一個重要的資料結構是 file
。儘管結構名稱為 file
,實際上是 inode
代表檔案本身,而 file
代表的是某個 process 已經打開的檔案。當某個 process 透過 open
之類的 system call 開啟一個檔案時,系統會為該 process 建立一個 file
資料結構,並且該結構會保存在該 process 的 task_struct
中。
// include/linux/sched.h
struct task_struct {
...
/* Open file information: */
struct files_struct *files;
...
}
// include/linux/fdtable.h
struct files_struct {
...
struct file __rcu * fd_array[NR_OPEN_DEFAULT];
}
在 Linux Kernel 中,一個 process 的資訊都會保存在 task_struct
資料結構中,其中 files_struct
用來維護 process 打開的檔案。files_struct
中的 fd_array
陣列用來保存所有打開的檔案。當我們透過 file descriptor 進行檔案操作時,系統會從 fd_array
中取得相應的 file
資料結構,然後對應操作該檔案。
因此,當我們使用 system call、檔案路徑或檔案描述子來操作檔案系統時,實際上是在操作 inode
、dentry
和 file
這些資料結構。
今天我們介紹了 Linux VFS
中幾個重要的資料結構,包括 inode
、dentry
和 file
。這些結構是 Linux 檔案系統運作的抽象元件。明天,我們將進一步探討這些資料結構是如何在實際操作中被使用的。