Linux 支援進程間共享打開文件,這背後是 VFS(虛擬文件系統)精心設計的四大對象體系:inode、dentry、file、files_struct。本文從核心數據結構、引用計數、APUE 經典三張表對照、實際共享場景(fork、dup、線程)五個維度,徹底釐清「誰共享什麼、共享到什麼程度」。
| 對象 | 結構體 | 生命週期 | 核心職責 |
|---|---|---|---|
| inode | struct inode | 文件存在即存在,內存中按需創建/回收 | 文件元數據:權限、擁有者、時間戳、數據塊指針、操作函數表 |
| dentry | struct dentry | 路徑查找時創建,dcache 緩存 | 目錄項:路徑分量→inode 映射,支援快速路徑解析 |
| file | struct file | open() 創建,close() 最後引用釋放銷毀 | 打開文件實例:文件偏移量、狀態標誌、操作函數表指針 |
| files_struct | struct files_struct | 進程創建時複製/共享,進程退出銷毀 | 進程級文件描述符表:fd → file* 映射、close_on_exec 位圖 |
task_struct 持有 files_struct *files 與 fs_struct *fs(cwd、root、umask)。CLONE_FILES 共享 files_struct,CLONE_FS 共享 fs_struct。files_struct->fd_array[]):每進程私有,fd 為索引,內含 close_on_exec 標誌與 file* 指針。struct file 實例):全系統共享,含 文件偏移量、狀態標誌(O_APPEND 等)、inode* 指針。struct inode):文件系統級,含文件類型、權限、數據塊位置等持久屬性。關鍵圖示關係:
多個 fd(同一進程或不同進程)可指向同一 file → 共享偏移量;
多個 file 可指向同一 inode → 共享元數據、數據塊;
dentry 將路徑名解析為 inode。
| 層級 | 計數器 | 含義 | 變化時機 |
|---|---|---|---|
| 1 | files_struct->count | 共享此 fd 表的進程/線程數 | clone(CLONE_FILES)++, exit()-- |
| 2 | fs_struct->count | 共享 fs 結構的進程/線程數 | clone(CLONE_FS)++, exit()-- |
| 3 | file->f_count | 指向此 file 的 fd 數 | open()=1, dup()++, fork()++, close()-- (歸0銷毀) |
| 4 | dentry->d_count | 引用此 dentry 的 file 數 | 每打開文件建立 dentry 引用 |
| 5 | inode->i_count | 引用此 inode 的 dentry 數 | 每 dentry 對應一 inode 引用 |
files_struct 指針(引用計數+1),fd 表共享。file → 共享文件偏移量。close(),僅遞減 f_count,另一方仍可繼續讀寫同一偏移量。file → 共享偏移量。dup2(fd, 1) 讓 stdout 指向 fd 所指文件。pthread_create 預設 CLONE_FILES | CLONE_FS → 共享 fd 表與 cwd/umask。read/write 同一 fd 直接共享偏移量,需自行加鎖協調。open() → 各自擁有獨立 file 實例 → 各自獨立偏移量。inode → 讀寫同一物理數據,需靠文件鎖(fcntl/flock)協調。files_struct->close_on_exec 位圖,每 bit 對應一 fd。execve() 時內核遍歷位圖,自動關閉標記位的 fd。fcntl(fd, F_SETFD, FD_CLOEXEC) 設定。struct dinode {
ushort di_mode; // 類型+權限
short di_nlink; // 硬鏈接數
ushort di_uid; // 擁有者 uid
ushort di_gid; // 組 gid
off_t di_size; // 文件大小
char di_addr[40]; // 數據區起始地址
time_t di_atime; // 最後訪問
time_t di_mtime; // 最後修改內容
time_t di_ctime; // 最後修改屬性
};
Linux 文件共享本質是多層引用計數精密協作:
files_struct 決定 fd 表共享範圍 → file 決定偏移量共享範圍 → inode 決定數據共享範圍。
理解這三層邊界,即可精準控制 fork、dup、線程、獨立進程等場景下的文件共享行為,避免「以為共享卻不共享、以為獨立卻共享」的經典 Bug。