文件描述符

文件描述符

內核(kernel)利用文件描述符(file descriptor)來訪問文件。文件描述符是非負整數。打開現存文件或新建文件時,內核會返回一個文件描述符。讀寫文件也需要使用文件描述符來指定待讀寫的文件。

簡介


文件描述符
文件描述符
文件描述符在形式上是一個非負整數。實際上,它是一個索引值,指向內核為每一個進程所維護的該進程打開文件的記錄表。當程序打開一個現有文件或者創建一個新文件時,內核向進程返回一個文件描述符。在程序設計中,一些涉及底層的程序編寫往往會圍繞著文件描述符展開。但是文件描述符這一概念往往只適用於UNIX、Linux這樣的操作系統。
習慣上,標準輸入(standard input)的文件描述符是0,標準輸出(standard output)是1,標準錯誤(standard error)是2。儘管這種習慣並非Unix內核的特性,但是因為一些shell和很多應用程序都使用這種習慣,因此,如果內核不遵循這種習慣的話,很多應用程序將不能使用。
POSIX定義了STDIN_FILENO、STDOUT_FILENO和STDERR_FILENO來代替0、1、2。這三個符號常量的定義位於頭文件unistd.h。
文件描述符的有效範圍是0到OPEN_MAX。一般來說,每個進程最多可以打開64個文件(0—63)。對FreeBSD5.2.1、Mac OSX10.3和Solaris9來說,每個進程最多可以打開文件的多少取決於系統內存的大小,int的大小,以及系統管理員設定的限制。Linux2.4.22強制規定最多不能超過1,048,576。
文件描述符是由無符號整數表示的句柄,進程使用它來標識打開的文件。文件描述符與包括相關信息(如文件的打開模式、文件的位置類型、文件的初始類型等)的文件對象相關聯,這些信息被稱作文件的上下文。
如何創建文件描述符
進程獲取文件描述符最常見的方法是通過本機子常式open或create獲取或者通過從父進程繼承。后一種方法允許子進程同樣能夠訪問由父進程使用的文件。文件描述符對於每個進程一般是唯一的。當用fork子常式創建某個子進程時,該子進程會獲得其父進程所有文件描述符的副本,這些文件描述符在執行fork時打開。在由fcntl、dup和dup2子常式複製或拷貝某個進程時,會發生同樣的複製過程。
對於每個進程,操作系統內核在u_block結構中維護文件描述符表,所有的文件描述符都在該表中建立索引。

特點


優點

文件描述符的好處主要有兩個:
基於文件描述符的I/O操作兼容POSIX標準。
在UNIX、Linux的系統調用中,大量的系統調用都是依賴於文件描述符。
例如,下面的代碼就示範了如何基於文件描述符來讀取當前目錄下的一個指定文件,並把文件內容列印至Console中。
此外,在Linux系列的操作系統上,由於Linux的設計思想便是把一切設備都視作文件。因此,文件描述符為在該系列平台上進行設備相關的編程實際上提供了一個統一的方法。
#include
#include
#include
#include
#include
#include
#include
int main(void){ int fd; int numbytes; char path[] = "file"; char buf[256]; 
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);}

缺點

文件描述符的概念存在兩大缺點:
在非UNIX/Linux操作系統上(如Windows NT),無法基於這一概念進行編程。
由於文件描述符在形式上不過是個整數,當代碼量增大時,會使編程者難以分清哪些整數意味著數據,哪些意味著文件描述符。因此,完成的代碼可讀性也就會變得很差。

定義數量


如何在不同平台上定義文件描述符的數量
文件描述符極限以及可分配給進程的最大大小由資源限制來定義。這些值應當按照在WebLogicServer文檔中建議的、特定於操作系統的文件描述符值來設置:
對於WLS8.1:調整硬體、操作系統和網路性能
對於WLS7.0:調整硬體、操作系統和網路性能
對於WLS6.1:調整硬體、操作系統和網路性能
Unix和Linux都有文件描述符。不過,二者的主要區別在於如何設置文件描述符的硬極限值、預設值和配置過程。
/usr/bin/ulimit實用程序定義允許單個進程使用的文件描述符的數量。它的最大值在rlim_fd_max中定義,在預設情況下,它設置為65,536。只有root用戶才能修改這些內核值。
Linux
管理用戶可以在etc/security/limits.conf配置文件中設置他們的文件描述符極限,如下例所示。
softnofile1024
hardnofile4096
系統級文件描述符極限還可以通過將以下三行添加到/etc/rc.d/rc.local啟動腳本中來設置:
#Increasesystem-widefiledescriptorlimit.
echo4096>/proc/sys/fs/file-max
echo16384>/proc/sys/fs/inode-max
Windows
在Windows操作系統上,文件描述符被稱作文件句柄。在Windows2000伺服器上,打開文件的句柄極限設置為16,384。此數量可以在任務管理器的性能摘要中監視。
HP-UX
nfile定義打開文件的最大數量。此值通常由以下公式來確定:((NPROC*2)+1000),其中NPROC通常為:((MAXUSERS*5)+64)。如果MAXUSERS等於400,則經過計算得到此值為5128。通常可以將此值設高一些。maxfiles是每個進程的軟文件極限,maxfiles_lim是每個進程的硬文件極限。
文件描述符極限在/etc/security/limits文件中設置,它的預設值是2000。此極限可以通過ulimit命令或setrlimit子常式來更改。最大大小由OPEN_MAX常數來定義。

解決方法


對於ANSI C規範中定義的標準庫的文件I/O操作。ANSI C規範給出了一個解決方法,就是使用FILE結構體的指針。事實上,UNIX/Linux平台上的FILE結構體的實現中往往都是封裝了文件描述符變數在其中。
在UNIX/Linux平台上,對於控制台(Console)的標準輸入,標準輸出,標準錯誤輸出也對應了三個文件描述符。它們分別是0,1,2。在實際編程中,如果要操作這三個文件描述符時,建議使用頭文件中定義的三個宏來表示:STDIN_FILENO, STDOUT_FILENO以及STDERR_FILENO。與文件描述符相關的操作
文件描述符的生成
open(), open64(), creat(), creat64()
socket()
socketpair()
pipe()
與單一文件描述符相關的操作
read(), write()
recv(), send()
recvmsg(),sendmsg()
sendfile()
lseek(), lseek64()
fstat(), fstat64()
fchmod()
fchown()
與複數文件描述符相關的操作
select(), pselect()
poll()
與文件描述符表相關的操作
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()

區別


文件描述符:在linux系統中打開文件就會獲得文件描述符,它是個很小的非負整數。每個進程在PCB(Process Control Block)中保存著一份文件描述符表,文件描述符就是這個表的索引,每個表項都有一個指向已打開文件的指針。
文件指針:C語言中使用文件指針做為I/O的句柄。文件指針指向進程用戶區中的一個被稱為FILE結構的數據結構。FILE結構包括一個緩衝區和一個文件描述符。而文件描述符是文件描述符表的一個索引,因此從某種意義上說文件指針就是句柄的句柄(在Windows系統上,文件描述符被稱作文件句柄)。