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 文件描述符與 dup/dup2 系統調用深度解析

編輯:Linux 教學

前言

文件描述符 (File Descriptor, FD) 是 Linux 進程訪問文件、網路套接字、管道等資源的核心抽象。理解 FD 的分配機制、標準流 (0/1/2)、dup 系列系統調用的複製語義,是掌握 I/O 重定向、管道通信、守護進程編程的基石。

一、Linux 文件類型與描述符基礎

1. 四大文件類型

類型首字符說明常見命令
普通文件-文本、二進制、腳本、數據cp, rm, vim
目錄文件d包含檔名與 inode 指針mkdir, ls, rmdir
鏈接文件l符號鏈接 (軟鏈接),指向目標路徑ln -s
設備文件b/c塊設備 (磁碟) / 字符設備 (終端、網卡)mknod, dd

2. 文件描述符本質

  • **非負整數**,進程內核打開文件表的索引。
  • **進程私有**:每個進程獨立擁有 FD 表 (默認上限 1024,可 `ulimit -n` 調整)。
  • **三大預設 FD**:
    FD宏定義預設設備用途
    0STDIN_FILENO鍵盤/終端標準輸入
    1STDOUT_FILENO螢幕/終端標準輸出
    2STDERR_FILENO螢幕/終端標準錯誤

3. 查看進程 FD

# 系統級限制
ulimit -n

# 查看特定進程已打開 FD
ls -l /proc//fd/

# 系統全局 FD 使用統計
sysctl -a | grep fs.file-nr

二、dup 與 dup2 系統調用

函數原型

#include 
int dup(int oldfd);           // 返回最小可用 FD
int dup2(int oldfd, int newfd); // 指定新 FD,若被佔用先關閉

核心語義:共享「打開文件描述」

  • 複製的是 **FD 表項指向的同一個「打開文件描述」 (open file description)**,包含:文件偏移量、存取模式 (O_RDONLY 等)、狀態旗標 (O_APPEND 等)。
  • **不共享**:FD 旗標 (FD_CLOEXEC),每個 FD 獨立持有。
  • 任一 FD 執行 `lseek`、`read`、`write` 移動偏移量,**另一 FD 可見**。

代碼示範

int fd1 = open("test.txt", O_RDWR | O_CREAT, 0644);
int fd2 = dup(fd1);           // fd2 = 3 (假設)
write(fd1, "hello", 5);       // 偏移量 → 5
write(fd2, "world", 5);       // 實際寫入位置 5-10,非 0-5
// 文件內容:helloworld

三、實戰應用場景

1. I/O 重定向 (Shell 內部實現)

// 將標準輸出重定向到文件
int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
dup2(fd, STDOUT_FILENO);  // fd=3 → newfd=1
close(fd);                // 關閉原 fd,STDOUT 現指向 log.txt
printf("This goes to log.txt\n");

2. 管道通信 (父子進程)

int pipefd[2];
pipe(pipefd);              // pipefd[0] 讀端, pipefd[1] 寫端
pid_t pid = fork();
if (pid == 0) {            // 子進程
    close(pipefd[0]);
    dup2(pipefd[1], STDOUT_FILENO); // 標準輸出 → 管道寫端
    close(pipefd[1]);
    execlp("ls", "ls", "-l", NULL);
} else {                   // 父進程
    close(pipefd[1]);
    dup2(pipefd[0], STDIN_FILENO);  // 標準輸入 → 管道讀端
    close(pipefd[0]);
    execlp("wc", "wc", "-l", NULL);
}

3. 守護進程脫離終端

// 雙重 fork 後,重定向 0/1/2 到 /dev/null
int fd = open("/dev/null", O_RDWR);
dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
if (fd > 2) close(fd);

4. 保存/恢復標準流

int saved_stdout = dup(STDOUT_FILENO);  // 備份
freopen("temp.log", "w", stdout);       // 臨時重定向
// ... 執行輸出日誌的庫函數 ...
dup2(saved_stdout, STDOUT_FILENO);      // 恢復
close(saved_stdout);

四、進階:fcntl 與 FD 旗標

// 獲取/設置 FD 旗標 (如 FD_CLOEXEC)
int flags = fcntl(fd, F_GETFD);
fcntl(fd, F_SETFD, flags | FD_CLOEXEC);  // exec 時自動關閉

// 獲取/設置文件狀態旗標 (O_APPEND, O_NONBLOCK 等)
int flags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, flags | O_APPEND);

五、總結對比表

調用新 FD 分配原子性典型用途
dup(old)內核選最小可用是簡單複製、備份標準流
dup2(old, new)指定 new (佔用先關閉)是精確重定向 0/1/2、管道綁定
fcntl(old, F_DUPFD, min)≥ min 的最小可用是指定範圍分配、避開低位 FD

結語

dup/dup2 看似簡單,實則是 Unix 「一切皆文件」哲學下 I/O 多路複用、重定向、進程間通信的核心樞紐。深入理解「打開文件描述」與「文件描述符」的區別,能避免並發寫入偏移量競爭、FD 洩漏等隱蔽 Bug,是系統級編程必修內功。

Copyright © Windows教程網 All Rights Reserved