當前位置:
首頁 > 知識 > 文件描述符與FILE

文件描述符與FILE

1. 文件描述符(重點)

在Linux系統中一切皆可以看成是文件,文件又可分為:普通文件、目錄文件、鏈接文件和設備文件。文件描述符(file descriptor)是內核為了高效管理已被打開的文件所創建的索引,其是一個非負整數(通常是小整數),用於指代被打開的文件,所有執行I/O操作的系統調用都通過文件描述符。程序剛剛啟動的時候,0是標準輸入,1是標準輸出,2是標準錯誤。如果此時去打開一個新的文件,它的文件描述符會是3。

1.1概念介紹

文件描述符的操作(如: open,creat,close,read))返回的是一個文件描述符,它是int類型的整數,即fd,其本質是文件描述符表中的下標,它起到一個索引的作用,進程通過PCB中的文件描述符表找到該fd所指向的文件指針filp。每個進程在PCB(Process Control Block)即進程式控制制塊中都保存著一份文件描述符表,文件描述符就是這個表的索引,文件描述表中每個表項都有一個指向已打開文件的指針; 已打開的文件在內核中用file結構體表示,文件描述符表中的指針指向file結構體。每打開一個文件,fd默認從最小的未被使用的下標開始分配。文件描述符的缺點:不能移植到UNIX以外的系統上去,也不直觀。

下面畫張圖來表示它們之間的關係:

文件描述符與FILE

而每個文件中又主要包含以下這些信息:

文件描述符與FILE

1.2圖表解釋

file結構體中維護File Status Flag(file結構體的成員f_flags)和當前讀寫位置(file結構體的成員f_pos)。在上圖中,進程1和進程2都打開同一文件,但是對應不同的file結構體,因此可以有不同的File Status Flag和讀寫位置。file結構體中比較重要的成員還有f_count,表示引用計數(Reference Count),後面我們會講到,dupfork等系統調用會導致多個文件描述符指向同一個file結構體,例如有fd1fd2都引用同一個file結構體,那麼它的引用計數就是2,當close(fd1)時並不會釋放file結構體,而只是把引用計數減到1,如果再close(fd2),引用計數就會減到0同時釋放file結構體,這才真的關閉了文件。

每個file結構體都指向一個file_operations結構體,這個結構體的成員都是函數指針,指向實現各種文件操作的內核函數。比如在用戶程序中read一個文件描述符,read通過系統調用進入內核,然後找到這個文件描述符所指向的file結構體,找到file結構體所指向的file_operations結構體,調用它的read成員所指向的內核函數以完成用戶請求。在用戶程序中調用lseekreadwriteioctlopen等函數,最終都由內核調用file_operations的各成員所指向的內核函數完成用戶請求。file_operations結構體中的release成員用於完成用戶程序的close請求,之所以叫release而不叫close是因為它不一定真的關閉文件,而是減少引用計數,只有引用計數減到0才關閉文件。對於同一個文件系統上打開的常規文件來說,readwrite等文件操作的步驟和方法應該是一樣的,調用的函數應該是相同的,所以圖中的三個打開文件的file結構體指向同一個file_operations結構體。如果打開一個字元設備文件,那麼它的readwrite操作肯定和常規文件不一樣,不是讀寫磁碟的數據塊而是讀寫硬體設備,所以file結構體應該指向不同的file_operations結構體,其中的各種文件操作函數由該設備的驅動程序實現。

每個file結構體都有一個指向dentry結構體的指針,「dentry」是directory entry(目錄項)的縮寫。我們傳給openstat等函數的參數的是一個路徑,例如/home/akaedu/a,需要根據路徑找到文件的inode。為了減少讀盤次數,內核緩存了目錄的樹狀結構,稱為dentry cache,其中每個節點是一個dentry結構體,只要沿著路徑各部分的dentry搜索即可,從根目錄/找到home目錄,然後找到akaedu目錄,然後找到文件a。dentry cache只保存最近訪問過的目錄項,如果要找的目錄項在cache中沒有,就要從磁碟讀到內存中。

每個dentry結構體都有一個指針指向inode結構體。inode結構體保存著從磁碟inode讀上來的信息。在上圖的例子中,有兩個dentry,分別表示/home/akaedu/a/home/akaedu/b,它們都指向同一個inode,說明這兩個文件互為硬鏈接。inode結構體中保存著從磁碟分區的inode讀上來信息,例如所有者、文件大小、文件類型和許可權位等。每個inode結構體都有一個指向inode_operations結構體的指針,後者也是一組函數指針指向一些完成文件目錄操作的內核函數。和file_operations不同,inode_operations所指向的不是針對某一個文件進行操作的函數,而是影響文件和目錄布局的函數,例如添加刪除文件和目錄、跟蹤符號鏈接等等,屬於同一文件系統的各inode結構體可以指向同一個inode_operations結構體。

inode結構體有一個指向super_block結構體的指針。super_block結構體保存著從磁碟分區的超級塊讀上來的信息,例如文件系統類型、塊大小等。super_block結構體的s_root成員是一個指向dentry的指針,表示這個文件系統的根目錄被mount到哪裡,在上圖的例子中這個分區被mount/home目錄下。

filedentryinodesuper_block這幾個結構體組成了VFS(虛擬文件系統VFS,Virtual Filesystem)的核心概念。

1.3對文件描述符的操作

(1).查看Linux文件描述符

1 [root@localhost ~]# sysctl -a | grep -i file-max --color
3 fs.file-max = 392036
5 [root@localhost ~]# cat /proc/sys/fs/file-max
7 392036
9 [root@localhost ~]# ulimit -n
11 1024
13 [root@localhost ~]#

Linux下最大文件描述符的限制有兩個方面,一個是用戶級的限制,另外一個則是系統級限制。

系統級限制:sysctl命令和proc文件系統中查看到的數值是一樣的,這屬於系統級限制,它是限制所有用戶打開文件描述符的總和

用戶級限制:ulimit命令看到的是用戶級的最大文件描述符限制,也就是說每一個用戶登錄後執行的程序佔用文件描述符的總數不能超過這個限制

(2).修改文件描述符的值

1 [root@localhost ~]# ulimit-SHn 10240
2 [root@localhost ~]# ulimit -n
3 10240
4 [root@localhost ~]#

以上的修改只對當前會話起作用,是臨時性的,如果需要永久修改,則要修改如下:

1 [root@localhost ~]# grep -vE"^$|^#" /etc/security/limits.conf
2 * hard nofile 4096
3 [root@localhost ~]#

1 //默認配置文件中只有hard選項,soft 指的是當前系統生效的設置值,hard 表明系統中所能設定的最大值
2 [root@localhost ~]# grep -vE"^$|^#" /etc/security/limits.conf
3 * hard nofile 10240
4 * soft nofile 10240
5 [root@localhost ~]#
6 // soft<=hard soft的限制不能比hard限制高

(3).修改系統限制

1 [root@localhost ~]# sysctl -wfs.file-max=400000
2 fs.file-max = 400000
3 [root@localhost ~]# echo350000 > /proc/sys/fs/file-max //重啟後失效
4 [root@localhost ~]# cat /proc/sys/fs/file-max
5 350000
6 [root@localhost ~]#

//以上是臨時修改文件描述符//永久修改把fs.file-max=400000添加到/etc/sysctl.conf中,使用sysctl -p即可

1.4用程序查看文件描述符

下面的程序,打開/home/shenlan/hello.c文件,如果此目錄下沒有hello.c文件,程序自動創建,程序中返回的文件描述符為3。因為進程啟動時,打開了標準輸入(0)、標準輸出(1)和標準出錯處理(2)三個文件,fd默認從最小的未被使用的下標開始分配,因此返回的文件描述符為3。

1 #include
2 #include
3 #include
4 #include
5 #include
6 int main
7 {
8 int fd;
9 if((fd = open("/home/shenlan/fd.c",O_CREAT|O_WRONLY|O_TRUNC,0611))<0){ 10 perror("openfile fd.c error! "); 11 exit(1); 12 } 13 else{ 14 printf("openfile fd.c success:%d ",fd); 15 } 16 if(close(fd) < 0){ 17 perror("closefile fd.c error! "); 18 exit(1); 19 } 20 else 21 printf("closefile fd.c success! "); 22 exit(0); 23 }

文件描述符與FILE

執行結果:

1.5進程打開一個文件的具體流程

進程通過系統調用open來打開一個文件,實質上是獲得一個文件描述符,以便進程通過文件描述符為連接對文件進行其他操作。進程打開文件時,會為該文件創建一個file對象,並把該file對象存入進程打開文件表中(文件描述符數組),進而確定了所打開文件的文件描述符。 open操作在內核里通過sys_open實現的,sys_open將創建文件的dentry、inode和file對象,並在file_struct結構體的進程打開文件表fd_array[NR_OPEN_DEFAULT]中尋找一個空閑表項,然後返回這個表項的下標(索引),即文件描述符。創建文件的file對象時,將file對象的f_op指向了所屬文件系統的操作函數集file_operations,而該函數集又來自具體文件的i節點,於是虛擬文件系統就與實際文件系統的操作銜接起來了。

2.C標準庫中的FILE結構和文件描述符

C語言中使用的是文件指針而不是文件描述符做為I/O的句柄."文件指針(file pointer)"指向進程用戶區中的一個被稱為FILE結構的數據結構。FILE結構包括一個緩衝區和一個文件描述符值.而文件描述符值是文件描述符表中的一個索引.從某種意義上說文件指針就是句柄的句柄。流(如: fopen)返回的是一個FILE結構指針, FILE結構是包含有文件描述符的,FILE結構函數可以看作是對fd直接操作的系統調用的封裝, 它的優點是帶有I/O緩存。

從文件描述符fd 到文件流 FILE* 的函數是 FILE* fdopen(int filedes,const char* mode);

文件描述符與FILE

早期的C標準庫中,FILE在stdio.h中定義;Turbo C中,參見譚浩強的《C程序設計》,FILE結構體中包含成員fd,即文件描述符。亦可以在安裝的Ubuntu系統的/usr/include/stdio.h中找到struct _IO_FILE結構體,這個結構體比較複雜,我們只關心需要的部分-文件描述符,但是在這個的結構體中,我們並沒有發現與文件描述符相關的諸如fd成員變數。此時,類型為int的_fileno結構體成員引起了我們的注意,但是不能確定其為文件描述符。因此寫個程序測試是最好的辦法,可以用以下的代碼測試:

1 #include
2 #include
3 #include
4 #include
5 #include
6 int main
7 {
8 char buf[50] = {"ILOVE this game!"};
9 FILE *myfile;
10
11 myfile = fopen("2.txt","w+");
12 if(!myfile){
13 printf("error:openfile failed!
");
14 }
15 printf("The openedfile"s descriptor is %d
",myfile->_fileno);
16 if(write(myfile->_fileno,buf,50)< 0){ 17 perror("error:writefile failed! "); 18 exit(1); 19 }else{ 20 printf("writefile successed! "); 21 } 22 exit(0); 23 }

程序中,使用fopen函數以讀寫打開2.txt文件,如果不存在2.txt文件,則創建此文件。並將其返回的FILE指針myfile。使用printf向標準終端列印出myfile->_fileno的值,並將myfile->_fileno作為文件描述符傳遞給write系統調用,向打開的文件寫入緩衝區數據。然後使用cat命令查看2.txt的內容。執行的結果如圖所示。_fileno的值為3,因為標準輸入、輸出、出錯為0、1、2。輸出結果如下:

因此,_fileno成員即為操作系統打開文件返回的句柄(windows系統)或文件描述符。深入學習可以閱讀人民郵電出版社《C標準庫》。當然還可以閱讀/glibc-2.9/manual/io.txti文件。Linux中,文件的描述符分配是從小到大逐個查詢文件描述符是否已經使用,然後再分配,也可以寫程序測試。

文件描述符表也稱文件描述符數組,其中存放了一個進程所打開的所有文件。文件描述符數組包含在進程打開的文件表files_struct結構中。在/include/linux/fdtable.h中定義,為一個指向file類型的指針數組---fd_array[NR_OPEN_DEFAULT],其中NR_OPEN_DEFAULT也在fdtable.h中定義,這是一個和具體的CPU體系結構有關的變數,#define NR_OPEN_DEFAULTBITS_PER_LONG。

FILE結構和文件描述符、file結構之間的關係可以用下圖來表示:

文件描述符與FILE

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 科技優家 的精彩文章:

cookie的路徑問題
使用 keepalived 的ip漂移搭建主從web
Vulkan Tutorial 18 重構交換鏈
用PHP和Ajax進行前後台數據交互——以用戶登錄為例
資料庫的三大範式以及五大約束

TAG:科技優家 |

您可能感興趣

Bash 中的 & 符號和文件描述符
Python用於NLP :處理文本和PDF文件
iOS最新beta版系統描述文件以及屏蔽更新文件
plink PED 文件格式介紹
iOSbeta3測試版描述文件
ASP.NET Web Forms XML 文件
記一次利用BLIND OOB XXE漏洞獲取文件系統訪問許可權的測試
Python實現TFTP文件傳輸
FCC 文件證實 iPhone X 存在金色版本
文件數據IO操作
文件內容搜索神奇-PowerGREP4
Perl 文件操作
Python實現讀取PDF文件案例
MySQL-默認配置文件
iOS 鈴聲文件製作
美國 FCC 文件解密,蘋果 iPhone X 金色版實拍照現身
如何從iOS中捕獲TAR文件
Virsh KVM文件管理
iOS12預覽版描述文件
Chrome將修正文件系統API漏洞