Linux系統將一切設備抽象為文件,文件操作是系統編程的基石。本文系統梳理Linux文件IO的核心概念、系統調用族(open/close/read/write/lseek/fstat/fcntl/mmap/ioctl)的原型、參數、返回值、錯誤處理及最佳實踐,並提供完整可編譯的示例代碼。
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int open(const char *pathname, int flags); int open(const char *pathname, int flags, mode_t mode); // O_CREAT時必需 int creat(const char *pathname, mode_t mode); // 等同 open(path, O_CREAT|O_WRONLY|O_TRUNC, mode)
| 標志 | 含義 | 備註 |
|---|---|---|
O_RDONLY | 只讀打開 | 值通常為0,三種訪問模式必選其一 |
O_WRONLY | 只寫打開 | 值通常為1 |
O_RDWR | 讀寫打開 | 值通常為2 |
O_CREAT | 文件不存在則創建 | 需配合 mode 參數 |
O_EXCL | 與O_CREAT同用,文件存在則報錯 | 原子性檢查+創建 |
O_TRUNC | 截斷文件長度為0 | 慎用,數據清零 |
O_APPEND | 每次寫入追加到文件末尾 | 原子性追加 |
O_NONBLOCK | 非阻塞模式 | 管道/設備/網絡常用 |
int fd = open("test.txt", O_RDWR | O_CREAT, 0644);
if (fd == -1) {
perror("open failed");
exit(EXIT_FAILURE);
}
#include <unistd.h> int close(int fd); // 成功返回0,失敗返回-1 // 關閉後fd可被復用,未關閉導致文件描述符洩漏
#include <unistd.h> ssize_t read(int fd, void *buf, size_t count); // 返回:實際讀取字節數(0=EOF, -1=錯誤, >0=實際數) // 注意:可能少於count(文件尾、信號中斷、管道/網絡緩衝區限制)
ssize_t n;
while ((n = read(fd, buf, sizeof(buf))) > 0) {
// 處理 n 字節數據
}
if (n == -1) perror("read error");
#include <unistd.h> ssize_t write(int fd, const void *buf, size_t count); // 返回:成功寫入字節數(-1=錯誤) // 注意:可能部分寫入,健壯代碼需循環寫入剩餘數據
#include <fcntl.h> #include <unistd.h> #include <stdio.h>
#define BUFSIZE 4096
int main() {
int fd_in = open("src.txt", O_RDONLY);
int fd_out = open("dst.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd_in==-1 || fd_out==-1) { perror("open"); return 1; }
char buf[BUFSIZE];
ssize_t n;
while ((n = read(fd_in, buf, BUFSIZE)) > 0) {
char *p = buf;
while (n > 0) {
ssize_t w = write(fd_out, p, n);
if (w == -1) { perror("write"); return 1; }
p += w; n -= w;
}
}
close(fd_in); close(fd_out);
return 0;
}
#include <sys/types.h> #include <unistd.h> off_t lseek(int fd, off_t offset, int whence); // whence: SEEK_SET(開頭), SEEK_CUR(當前), SEEK_END(結尾) // 返回:新偏移量(相對開頭), -1=錯誤 // 用途:隨機訪問、獲取文件大小(lseek(fd,0,SEEK_END))
#include <sys/stat.h> int fstat(int fd, struct stat *buf); // 關鍵字段:st_mode(類型權限), st_size(大小), st_uid/gid, st_atim/mtim/ctim
#include <sys/mman.h> void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); int munmap(void *addr, size_t length); // prot: PROT_READ/PROT_WRITE/PROT_EXEC // flags: MAP_SHARED(共享寫回文件), MAP_PRIVATE(私有寫時複製) // 優勢:大文件隨機訪問性能高、進程間共享內存
#include <fcntl.h> int fcntl(int fd, int cmd, ... /* arg */); // 常用命令: // F_GETFL/F_SETFL: 獲取/設置文件狀態標志(O_NONBLOCK, O_APPEND等) // F_GETFD/F_SETFD: 獲取/設置文件描述符標志(FD_CLOEXEC) // F_SETLK/F_SETLKW/F_GETLK: 記錄鎖(區域鎖)操作
#include <sys/ioctl.h> int ioctl(int fd, unsigned long request, ...); // 針對特定設備的控制命令(如終端、磁盤、網卡配置) // 高度依賴設備驅動,非通用文件操作
perror 或 strerror(errno) 輸出錯誤信息goto cleanup 或 RAII 風格確保所有路徑關閉文件描述符O_EXCL、O_APPEND、fcntl 記錄鎖保證原子性_FILE_OFFSET_BITS=64,使用 off_t / lseek64BUFSIZ (通常8192) 或 4096 (頁大小) 為宜Linux文件IO系統調用雖然接口簡單,但正確處理邊界條件(部分讀寫、信號中斷、並發控制、大文件)需要深厚的系統編程功力。掌握本文覆蓋的核心API族、錯誤處理模式、內存映射技巧,即可應對絕大多數文件操作場景。建議結合 man 2 open 等手冊頁深入研讀每個調用的細節。