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

Linux文件IO系統編程全攻略:open/read/write/close/lseek/fcntl/mmap完整指南

     編輯:關於C語言

Linux文件IO系統編程全攻略:open/read/write/close/lseek/fcntl/mmap完整指南

Linux系統將一切設備抽象為文件,文件操作是系統編程的基石。本文系統梳理Linux文件IO的核心概念、系統調用族(open/close/read/write/lseek/fstat/fcntl/mmap/ioctl)的原型、參數、返回值、錯誤處理及最佳實踐,並提供完整可編譯的示例代碼。

核心概念:文件描述符

  • 本質:非負整數,內核進程文件表的索引
  • 範圍:0 ~ OPEN_MAX-1(通常1024),有限資源,用完必須關閉
  • 三大預分配:
    • 0: STDIN_FILENO (標準輸入)
    • 1: STDOUT_FILENO (標準輸出)
    • 2: STDERR_FILENO (標準錯誤)
  • 作用:用戶空間與內核空間交互的句柄,所有IO操作通過它進行

文件打開與創建:open() / creat()

函數原型

#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)

關鍵 flags 參數詳解

標志含義備註
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);
}

文件關閉:close()

#include <unistd.h>
int close(int fd);
// 成功返回0,失敗返回-1
// 關閉後fd可被復用,未關閉導致文件描述符洩漏

文件讀取:read()

#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");

文件寫入:write()

#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;
}

文件偏移控制:lseek()

#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))

文件狀態獲取:fstat()

#include <sys/stat.h>
int fstat(int fd, struct stat *buf);
// 關鍵字段:st_mode(類型權限), st_size(大小), st_uid/gid, st_atim/mtim/ctim

內存映射文件:mmap() / munmap()

#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(私有寫時複製)
// 優勢:大文件隨機訪問性能高、進程間共享內存

文件控制操作:fcntl()

#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: 記錄鎖(區域鎖)操作

設備控制:ioctl()

#include <sys/ioctl.h>
int ioctl(int fd, unsigned long request, ...);
// 針對特定設備的控制命令(如終端、磁盤、網卡配置)
// 高度依賴設備驅動,非通用文件操作

最佳實踐總結

  1. 錯誤檢查:每個系統調用必須檢查返回值,使用 perror 或 strerror(errno) 輸出錯誤信息
  2. 資源管理:使用 goto cleanup 或 RAII 風格確保所有路徑關閉文件描述符
  3. 原子操作:多進程環境下使用 O_EXCL、O_APPEND、fcntl 記錄鎖保證原子性
  4. 大文件支持:編譯時定義 _FILE_OFFSET_BITS=64,使用 off_t / lseek64
  5. 緩衝區大小:BUFSIZ (通常8192) 或 4096 (頁大小) 為宜

總結

Linux文件IO系統調用雖然接口簡單,但正確處理邊界條件(部分讀寫、信號中斷、並發控制、大文件)需要深厚的系統編程功力。掌握本文覆蓋的核心API族、錯誤處理模式、內存映射技巧,即可應對絕大多數文件操作場景。建議結合 man 2 open 等手冊頁深入研讀每個調用的細節。

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