檔案描述子
檔案描述子(File descriptor)是電腦科學中的一個術語,是一個用於表述指向檔案的參照的抽象化概念。
概要
檔案描述子在形式上是一個非負整數。實際上,它是一個索引值,指向內核為每一個行程所維護的該行程打開檔案的記錄表。當程式打開一個現有檔案或者建立一個新檔案時,內核向行程傳回一個檔案描述子。在程式設計中,一些涉及底層的程式編寫往往會圍繞着檔案描述子展開。但是檔案描述子這一概念往往只適用於UNIX、Linux這樣的作業系統。
每個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 |
優點
檔案描述子的優點主要有兩個:
例如,下面的程式碼就示範了如何基於檔案描述子來讀取當前目錄下的一個指定檔案,並把檔案內容列印至主控臺。
#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()