首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

UNIX 系統編程小結 (一):文件 I/O 深度解析

     編輯:關於C語言

從《APUE》第 3 章文件 I/O 入手,結合內核視角梳理 read/write 無緩衝本質、文件描述符表、文件表、v-node 表三層關係、O_APPEND 原子追加語義、dup/dup2 共享文件表差異、lseek 僅修改偏移不觸發 I/O 等核心知識點,並附帶可運行驗證代碼。

核心概念:三層表結構

進程 A                    內核
+------------------+      +-------------------------+
| fd 0,1,2,3,4...  | ---> | 文件描述符表 (每進程)  |
+------------------+      |  fd -> 指向 文件表項   |
                          +-----------+-------------+
                                      |
                                      v
                          +-------------------------+
                          | 文件表 (文件表項)        |  <-- 存放: 文件狀態標誌、當前偏移量、指向 v-node 指針
                          |  每次 open() 生成新項    |
                          +-----------+-------------+
                                      |
                                      v
                          +-------------------------+
                          | v-node 表 (每文件一項)   |  <-- 存放: i-node 資訊、鎖狀態、文件系統操作向量
                          +-------------------------+

無緩衝 I/O vs 標準 I/O

  • read/write:用戶態無緩衝 — 直接發起系統調用,讀滿/寫滿/遇 EOF/錯誤即返回。內核仍有頁面緩存 (delayed write)。
  • fread/fwrite:用戶態帶緩衝 (FILE*) — 以塊為單位調用 read/write,減少系統調用次數。

文件共享機制實驗

場景 1:兩次 open() → 兩個獨立文件表項

int fd1 = open("temp", O_RDWR);
int fd2 = open("temp", O_RDWR);
// fd1, fd2 各自擁有獨立「當前偏移量」
// write(fd1) 不影響 fd2 的偏移

場景 2:dup2(fd1, fd2) → 共享同一文件表項

int fd1 = open("temp", O_RDWR);
int fd2 = dup2(fd1, fd2); // 或 dup(fd1)
// fd1, fd2 共享「當前偏移量」
// write(fd1) 導致 fd2 讀取位置同步前移

O_APPEND 原子追加語義驗證

結論:O_APPEND 只影響 write,不影響 lseek 設置的偏移量,也不影響 read。

// temp 內容: "aaaaabbbbbccccc"
int fd = open("temp", O_RDWR | O_APPEND);
lseek(fd, 5, SEEK_SET);          // 偏移設為 5
read(fd, buf, 5);                // 讀到 "bbbbb"  (lseek 生效)
write(fd, "abcde", 5);           // 實際寫入文件末尾 (O_APPEND 生效)
// 文件變為: "aaaaabbbbbcccccabcde"

內核實現:每次 write 前,內核檢測 O_APPEND 旗標,強制將文件表項的偏移量設為文件當前大小,再寫入 — 原子操作,多進程並發追加不會覆蓋。

系統調用速查表

調用關鍵行為備註
lseek(fd, off, SEEK_SET)僅修改文件表項偏移,無 I/O返回新偏移
open(file, O_RDWR|O_APPEND)原子追加模式write 前強制偏移=文件末尾
fcntl(fd, F_GETFL)/F_SETFL獲取/設置文件狀態標誌可動態切換 O_APPEND/O_NONBLOCK
dup(fd)復制 fd,共享文件表項原子操作
dup2(fd1, fd2)指定新 fd 編號,關閉舊 fd2原子;close+fcntl 非原子
creat(file, mode)等價 open(file, O_CREAT|O_WRONLY|O_TRUNC, mode)若文件存在則截斷

筆記精選 (摘自 APUE + 實踐)

  1. off_t = long int;size_t = unsigned int;ssize_t = signed size_t。
  2. 進程結束時內核自動關閉所有 fd (除 0/1/2)。
  3. cat 不直接讀 stdin,需 cat - file 或 cat /dev/fd/0 file。
  4. O_SYNC 每次 write 同步到磁盤;fsync/fdatasync 一次性同步。
  5. 2>&1 將 fd 2 重定向到 fd 1 指向的文件 (合併 stderr/stdout)。
  6. 硬鏈接目錄可能形成循環,多數 UNIX 禁止 link(dir)。
  7. 符號鏈接可指向不存在路徑,ln -s ../foo bar 即使 ../foo 不存在也成功。

來源:Neil的博客

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved