檔案描述子

文件描述符File descriptor)是計算機科學中的一個術語,是一個用於表述指向文件的引用的抽象化概念。

概要

文件描述符在形式上是一個非負整數。實際上,它是一個索引值,指向內核為每一個進程所維護的該進程打開文件的記錄表。當程序打開一個現有文件或者創建一個新文件時,內核向進程傳回一個文件描述符。在程序設計中,一些涉及底層的程序編寫往往會圍繞着文件描述符展開。但是文件描述符這一概念往往只適用於UNIXLinux這樣的操作系統。

每個Unix進程(除了可能的守護進程)應均有三個標準的POSIX文件描述符,對應於三個標準流

整數值 名稱 <unistd.h>符號常量[1] <stdio.h>文件流[2]
0 Standard input STDIN_FILENO stdin
1 Standard output STDOUT_FILENO stdout
2 Standard error STDERR_FILENO stderr

優點

文件描述符的優點主要有兩個:

  • 基於文件描述符的I/O操作兼容POSIX標準。
  • 在UNIX、Linux的系統調用中,大量的系統調用都是依賴於文件描述符。

例如,下面的代碼就示範了如何基於文件描述符來讀取當前目錄下的一個指定文件,並把文件內容打印至主控臺。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main (void){
    int fd;
    int numbytes;
    char path[] = "file";
    char buf[256];

    /*
     * O_CREAT:如果文件不存在则创建
     * O_RDONLY:以只读模式打开文件
     */
    fd = open(path, O_CREAT | O_RDONLY, 0644);
    if(fd < 0){
        perror("open()");
        exit(EXIT_FAILURE);
    }

    memset(buf, 0x00, 256);
    while((numbytes = read(fd, buf, 255)) > 0){
        printf("%d bytes read: %s", numbytes, buf);
        memset(buf, 0x00, 256);
    }
    close (fd);
    exit(EXIT_SUCCESS);
}

此外,在Linux系列的操作系統上,由於Linux的設計思想便是把一切設備都視作文件。因此,檔案描述子的存在提供了程式操作裝置的統一方法。

缺點

文件描述符的概念存在兩大缺點:

  • 在非UNIX/Linux 操作系統上(如Windows),無法基於這一概念進行編程——事實上,Windows下的文件描述符和信號量互斥鎖內核對象一樣都記作HANDLE。
  • 由於文件描述符在形式上不過是個整數,當代碼量增大時,會使編程者難以分清哪些整數意味着數據,哪些意味着文件描述符。因此,完成的代碼可讀性也就會變得很差,這一點一般通過使用名稱有文字意義的魔術數字進行替換來解決。

其他

  • 對於ANSI C規範中定義的標準庫的文件I/O操作。ANSI C規範給出了一個解決方法,就是使用FILE結構體的指針。事實上,UNIX/Linux平台上的FILE結構體的實作中往往都是封裝了文件描述符變量在其中。
  • 在UNIX/Linux平台上,對於控制台(Console)的標準輸入標準輸出標準錯誤輸出也對應了三個文件描述符。它們分別是0,1,2。在實際編程中,如果要操作這三個文件描述符時,建議使用<unistd.h>頭文件中定義的三個巨集來表示:STDIN_FILENO, STDOUT_FILENO以及STDERR_FILENO.
  • 對於一個進程而言,文件描述符的變化範圍為0~OPEN_MAX[註 1].

與文件描述符相關的操作

文件描述符的生成

  • open(), creat()
  • socket()
  • socketpair()
  • pipe()

與單一文件描述符相關的操作

  • read(), write()
  • recv(), send()
  • recvmsg(), sendmsg()
  • sendfile()
  • lseek()
  • fstat()
  • fchmod()
  • fchown()

與複數文件描述符相關的操作

  • select(), pselect()
  • poll(),epoll()

與文件描述符表相關的操作

  • close()
  • dup()
  • dup2()
  • fcntl (F_DUPFD)
  • fcntl (F_GETFD and F_SETFD)

改變進程狀態的操作

  • fchdir()
  • mmap()

與文件加鎖的操作

  • flock()
  • fcntl (F_GETLK, F_SETLK and F_SETLKW)
  • lockf()

與套接字相關的操作

  • connect()
  • bind()
  • listen()
  • accept()
  • getsockname()
  • getpeername()
  • getsockopt(), setsockopt()
  • shutdown()

其他

  • ioctl()

注釋

  1. ^ OPEN_MAX定義在頭文件limits.h中。

參考文獻

  1. ^ The Open Group Base Specifications Issue 6, IEEE Std 1003.1, 2004 Edition. [2016-10-24]. (原始內容存檔於2021-03-13). 
  2. ^ The IEEE and The Open Group. <stdio.h>. The Open Group Base Specifications Issue 6, IEEE Std 1003.1, 2004 Edition. [May 31, 2014]. (原始內容存檔於2020-09-23).