Windows XP Windows 7 Windows 2003 Windows Vista Windows 綜合教學 Linux 系統教學
Windows 10 Windows 8 Windows 2008 Windows NT Windows Server 電腦軟體教學
您现在的位置: Windows教程網 >> Windows教程 >  >> Linux 系統教學 >> 關於 Linux

Linux 檔案 I/O 系統程式設計完整指南:open/read/write/lseek/close 核心 API 深度解析

編輯:關於 Linux

Linux 檔案 I/O 是系統程式設計基石,open/read/write/lseek/close 五大核心系統呼叫構成無緩衝 I/O (Unbuffered I/O) 基礎,直接與核心互動,效能高、可控性強。本文深度解析各 API 簽名、旗標、返回值、錯誤處理、檔案描述符管理、原子操作與實戰程式碼範例。

檔案描述符 基礎

  • 非負整數,核心為每個進程維護檔案描述符表。
  • 三個預設開啟:0: STDIN_FILENO (標準輸入)、1: STDOUT_FILENO (標準輸出)、2: STDERR_FILENO (標準錯誤)。
  • open 返回最小未使用描述符,特性常用於重導向實現。

核心 API 詳細解析

1. open / creat - 開啟或建立檔案

#include 
#include 
#include 

int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);  // 需 O_CREAT 時提供 mode

// 等效於 open(path, O_WRONLY|O_CREAT|O_TRUNC, mode)
int creat(const char *pathname, mode_t mode);

flags 必選模式 (三選一)

  • O_RDONLY:唯讀
  • O_WRONLY:唯寫
  • O_RDWR:讀寫

flags 可選組合

2. close - 關閉檔案描述符

#include 
int close(int fd);
  • 釋放描述符,解除進程與檔案關聯。
  • 自動釋放該進程持有的該檔案所有記錄鎖 (fcntl 鎖)。
  • 進程終止時核心自動關閉所有開啟描述符。
  • 返回 0 成功,-1 失敗 (如 EBADF 描述符無效)。

3. lseek - 重新定位檔案偏移量

#include 
#include 
off_t lseek(int fd, off_t offset, int whence);

whence 取值

  • SEEK_SET:從檔案開頭偏移 offset 位元組
  • SEEK_CUR:從當前位置偏移 offset 位元組
  • SEEK_END:從檔案末尾偏移 offset 位元組 (通常為負值或 0)
  • 返回新偏移量 (相對檔案開頭),-1 失敗。
  • 偏移量可超過檔案大小,形成「洞」 (Sparse File),不佔用實體磁碟空間。
  • O_APPEND 開啟時 lseek 不影響寫入位置 (寫入永遠在末尾)。

4. read - 讀取資料

#include 
ssize_t read(int fd, void *buf, size_t count);
  • 從 fd 讀取最多 count 位元組到 buf。
  • 返回實際讀取位元組數:
旗標作用注意事項
O_CREAT檔案不存在則建立,需第三參數 modemode 受 umask 影響,實際權限 = mode & ~umask
O_EXCL配合 O_CREAT,檔案存在則報錯 (EEXIST)原子性檢查-建立,防競爭條件
O_TRUNC檔案存在且為寫入模式開啟時,截斷長度為 0危險:資料清空,不可恢復
O_APPEND每次寫入追加到檔案末尾核心保證原子追加,多進程安全
O_SYNC同步 I/O,寫入等待資料寫入磁碟效能低,保證資料持久化
O_DSYNC資料同步,不含元資料比 O_SYNC 輕量
O_NONBLOCK非阻塞模式FIFO/管道/裝置檔案常用
O_CLOEXEC執行 exec 時自動關閉防檔案描述符洩漏到子進程
O_DIRECTORY路徑必須為目錄防符號連結攻擊
O_NOFOLLOW不跟隨符號連結安全開啟
返回值含義
> 0成功讀取位元組數 (可能小於 count,如管道/終端/檔案末尾)
0EOF (檔案末尾)
-1錯誤,errno 指示原因 (EINTR, EIO, EBADF...)
  • 必須迴圈處理短讀:
ssize_t n;
while ((n = read(fd, buf, sizeof(buf))) > 0) {
    // 處理 n 位元組
}
if (n < 0) perror("read");

5. write - 寫入資料

#include 
ssize_t write(int fd, const void *buf, size_t count);
  • 將 buf 前 count 位元組寫入 fd。
  • 返回實際寫入位元組數,通常等於 count;-1 錯誤 (如磁碟滿 ENOSPC、超過檔案大小限制 EFBIG)。
  • 同樣需迴圈處理短寫 (罕見,除非非阻塞/訊號中斷)。

實戰範例:建立檔案、寫入、定位、讀取

#include 
#include 
#include 
#include 
#include 

int main() {
    const char *path = "demo.io";
    const char *msg = "Hello, Linux File I/O!\n";
    char buf[128];
    int fd;

    // 1. 開啟/建立:讀寫、建立、截斷
    fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0644);
    if (fd < 0) { perror("open"); return 1; }

    // 2. 寫入
    size_t len = strlen(msg);
    ssize_t written = write(fd, msg, len);
    if (written != len) { perror("write"); close(fd); return 1; }
    printf("Written %zd bytes\n", written);

    // 3. 定位到開頭
    off_t pos = lseek(fd, 0, SEEK_SET);
    if (pos < 0) { perror("lseek"); close(fd); return 1; }
    printf("Seeked to offset %ld\n", pos);

    // 4. 讀取
    ssize_t n = read(fd, buf, sizeof(buf) - 1);
    if (n < 0) { perror("read"); close(fd); return 1; }
    buf[n] = '\0';
    printf("Read %zd bytes: %s", n, buf);

    // 5. 關閉
    close(fd);
    return 0;
}

編譯執行:gcc demo.c -o demo && ./demo

進階主題

原子操作與競爭條件

  • O_CREAT | O_EXCL:原子性「檢查存在並建立」,用於鎖檔案實現。
  • O_APPEND:核心保證多進程並發寫入時,每次寫入都原子追加到當前末尾,無需額外鎖。
  • rename():同檔案系統內原子替換,常用於配置檔案熱更新 (寫臨時檔 → rename)。

檔案描述符傳遞 (SCM_RIGHTS)

// Unix Domain Socket 透過 sendmsg/recvmsg 傳遞 fd
// 實現進程間共享開啟檔案描述符,高效零拷貝

非阻塞 I/O 與多路複用

  • O_NONBLOCK + select/poll/epoll 實現高併發網路伺服器。
  • 普通檔案通常不支援非阻塞 (磁碟 I/O 本質阻塞),需用 io_uring (Linux 5.1+) 或線程池。

直接 I/O (O_DIRECT)

  • 繞過頁面快取,資料直接在用戶緩衝區與磁碟傳輸。
  • 需記憶體對齊 (通常 512B/4K),緩衝區大小為磁區大小整數倍。
  • 適用資料庫引擎、大檔案傳輸等自行管理快取的場景。

錯誤處理最佳實踐

// 統一錯誤處理巨集
#define CHECK(call) \
    do { if ((call) < 0) { perror(#call); exit(EXIT_FAILURE); } } while(0)

// 使用
CHECK(fd = open("file", O_RDONLY));
CHECK(write(fd, buf, len));
CHECK(close(fd));

無緩衝 I/O vs 標準 I/O (stdio)

特性無緩衝 I/O (syscall)標準 I/O (FILE*)
緩衝無 (直接核心)用戶空間緩衝 (全緩衝/行緩衝/無緩衝)
APIopen/read/write/lseek/closefopen/fread/fwrite/fseek/fclose
效能系統呼叫開銷大,適合大塊/精確控制緩衝減少系統呼叫,適合小量頻繁 I/O
可移植性POSIX 標準,類 Unix 通用C 標準庫,跨平台 (含 Windows)
格式化無fprintf/fscanf 格式化強大

掌握無緩衝 I/O 核心 API,是深入理解 Linux 核心、開發高效能儲存引擎、除錯複雜 I/O 問題的必經之路。

Copyright © Windows教程網 All Rights Reserved