提出前提
每一個檔案描述符會與一個打開檔案相對應,同時,不同的檔案描述符也會指向同一個檔案。相同的檔案可以被不同的進程打開也可以在同一個進程中被多次打開。系統為每一個進程維護了一個檔案描述符表,該表的值都是從0開始的,所以在不同的進程中你會看到相同的檔案描述符,這種情況下相同檔案描述符有可能指向同一個檔案,也有可能指向不同的檔案。具體情況要具體分析,要理解具體其概況如何,需要查看由核心維護的3個數據結構。
1.進程級的檔案描述符表;
2.系統級的打開檔案描述符表;
3.檔案系統的i-node表。
簡介
檔案描述符在形式上是一個非負整數。實際上,它是一個索引值,指向
核心為每一個進程所維護的該進程打開檔案的記錄表。當程式打開一個現有檔案或者創建一個新檔案時,核心向進程返回一個檔案描述符。在程式設計中,一些涉及底層的程式編寫往往會圍繞著檔案描述符展開。但是檔案描述符這一概念往往只適用於
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)。對於 FreeBSD 5.2.1、Mac OS X 10.3 和 Solaris 9 來說,每個進程最多可以打開檔案的多少取決於
系統記憶體的大小,int 的大小,以及系統管理員設定的限制。Linux 2.4.22 強制規定最多不能超過 1,048,576 。
檔案描述符是由
無符號整數表示的句柄,進程使用它來標識打開的檔案。檔案描述符與包括相關信息(如檔案的打開模式、檔案的位置類型、檔案的初始類型等)的檔案對象相關聯,這些信息被稱作檔案的上下文。
如何創建檔案描述符
進程獲取檔案描述符最常見的方法是通過本機
子例程open或create獲取或者通過從
父進程繼承。後一種方法允許子進程同樣能夠訪問由父進程使用的檔案。檔案描述符對於每個進程一般是特定的。當用fork子例程創建某個子進程時,該子進程會獲得其父進程所有檔案描述符的副本,這些檔案描述符在執行fork時打開。在由fcntl、dup和
dup2子例程複製或拷貝某個進程時,會發生同樣的複製過程。
對於每個進程,作業系統
核心在u_block結構中維護檔案描述符表,所有的檔案描述符都在該表中建立索引。
特點
優點
檔案描述符的好處主要有兩個:
例如,下面的代碼就示範了如何基於檔案描述符來讀取當前目錄下的一個指定檔案,並把檔案內容列印至Console中。
此外,在Linux系列的作業系統上,由於Linux的設計思想便是把一切設備都視作檔案。因此,檔案描述符為在該系列平台上進行設備相關的編程實際上提供了一個統一的方法。
#include <sys/stat.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);
缺點
檔案描述符的概念存在兩大缺點:
由於檔案描述符在形式上不過是個整數,當代碼量增大時,會使編程者難以分清哪些整數意味著數據,哪些意味著檔案描述符。因此,完成的代碼可讀性也就會變得很差。
定義數量
如何在不同平台上定義檔案描述符的數量
檔案描述符極限以及可分配給進程的最大大小由資源限制來定義。這些值應當按照在WebLogicServer文檔中建議的、特定於作業系統的檔案描述符值來設定:
對於WLS8.1:調整硬體、作業系統和網路性能
對於WLS7.0:調整硬體、作業系統和網路性能
對於WLS6.1:調整硬體、作業系統和網路性能
Unix和Linux都有檔案描述符。不過,二者的主要區別在於如何設定檔案描述符的硬極限值、預設值和配置過程。
Solaris
/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是每個進程的硬檔案極限。
AIX
檔案描述符極限在/etc/security/limits檔案中設定,它的預設值是2000。此極限可以通過ulimit命令或setrlimit子例程來更改。最大大小由OPEN_MAX常數來定義。
解決方法
對於ANSI C規範中定義的標準庫的檔案I/O操作。ANSI C規範給出了一個解決方法,就是使用FILE
結構體的
指針。事實上,UNIX/Linux平台上的FILE結構體的實現中往往都是封裝了檔案描述符變數在其中。
在UNIX/Linux平台上,對於控制台(Console)的標準輸入,標準輸出,標準錯誤輸出也對應了三個檔案描述符。它們分別是0,1,2。在實際編程中,如果要操作這三個檔案描述符時,建議使用<unistd.h>頭檔案中定義的三個宏來表示: STDIN_FILENO, STDOUT_FILENO以及STDERR_FILENO。 與檔案描述符相關的操作
檔案描述符的生成
open(), open64(), creat(), creat64()
socket()
pipe()
與單一檔案描述符相關的操作
read(), write()
recv(), send()
sendfile()
lseek(), lseek64()
與複數檔案描述符相關的操作
select(), pselect()
poll()
與檔案描述符表相關的操作
close()
dup()
fcntl (F_DUPFD)
fcntl (F_GETFD and F_SETFD)
改變進程狀態的操作
mmap()
與檔案加鎖的操作
flock()
fcntl (F_GETLK, F_SETLK and F_SETLKW)
與套接字相關的操作
connect()
bind()
listen()
accept()
getsockname()
getpeername()
getsockopt(), setsockopt()
shutdown()
檔案描述符與檔案指針的區別
檔案描述符:在linux系統中打開檔案就會獲得檔案描述符,它是個很小的非負整數。每個進程在PCB(Process Control Block)中保存著一份檔案描述符表,檔案描述符就是這個表的索引,每個表項都有一個指向已打開檔案的指針。
檔案指針:C語言中使用檔案指針做為I/O的句柄。檔案指針指向進程用戶區中的一個被稱為FILE結構的數據結構。FILE結構包括一個緩衝區和一個檔案描述符。而檔案描述符是檔案描述符表的一個索引,因此從某種意義上說檔案指針就是句柄的句柄(在Windows系統上,檔案描述符被稱作檔案句柄)。