在 Linux 內核中,每個進程由 task_struct 描述,其中指向 files_struct 的指針構成了文件描述符表。用戶程式無法直接訪問內核表項,只能持有文件描述符——即表項索引(非負整數:0、1、2、3…)。本文通過圖解剖析文件描述符從分配到讀寫的完整內核路徑。
task_struct (進程描述符/PCB)
└─ files_struct *files → 文件描述符表 (fd table)
├─ fd[0] → struct file * → stdin (標準輸入)
├─ fd[1] → struct file * → stdout (標準輸出)
├─ fd[2] → struct file * → stderr (標準錯誤)
├─ fd[3] → struct file * → 打開的文件 A
└─ fd[n] → struct file * → 打開的文件 N
fd,分配 struct file,初始化 f_pos=0、f_flags、f_op(文件操作函數表),調用文件系統 inode->i_fop->open(),將 fd 返回用戶態。fd → 內核通過 files_struct->fd[fd] 找到 struct file * → 調用 f_op->read()/write() → 複製數據至/自用戶緩衝區 → 更新 f_pos → 返回實際字節數。struct file(引用計數歸零時觸發 f_op->release()),fd 表項置空,該 fd 可被復用。struct file,共享文件偏移量 f_pos、狀態標誌。這是 Shell 管道、重定向、守護進程標準流重定向的基礎。FD_CLOEXEC(close-on-exec),文件描述符跨 exec 保留。| 維度 | 文件描述符 | 文件指標 |
|---|---|---|
| 本質 | 內核 fd 表索引 | 用戶態緩衝 I/O 封裝 |
| 頭文件 | <unistd.h>, <fcntl.h> | <stdio.h> |
| API | open/read/write/close/lseek/ioctl | fopen/fread/fwrite/fclose/fseek/printf |
| 緩衝 | 無(直接系統調用) | 全緩衝/行緩衝/無緩衝 |
| 線程安全 | 原子操作 | 需自行加鎖(或用 flockfile) |
| 轉換 | fileno(FILE*) → fd | fdopen(fd, mode) → FILE* |