淺析文件描述符 文件表項 v節點表項符
文件描述符在Linux編程裏隨處可見,設備讀寫,網絡通信,進程通信. 可是文件描述符到底是什麼? 文件描述符是一個簡單的整數,
用以標明每一個被進程所打開的文件和socket. 第一個打開的文件時0,第二個是1,以此類推. linux操作系統通常給每個進程能打開
的文件數量強加一個限制.文件描述符的限制可能會極大的影響性能,當用戶用戶完了所有的文件描述符之後,它不能接收新的連接.
也就是說,用完文件描述符導致拒絕服務.
所以呢,我們可以嘗試修改文件描述符的個數 使用 ulimit -HSn xxxx 來修改文件描述符個數.
在Unix系統支持在不同進程間共享打開文件. 在介紹dup之前. 我們先介紹內核表示打開的文件使用的三種數據結構.
內核使用三種數據結構表示打開的文件,他們之間的關係決定了在文件共享方面一個進程對另一個進程可能產生的影響.
1.每一個進程在進程表中都有一個記錄表項,記錄項中包含有一張打開的文件描述符,可將其視爲一個矢量,每個描述項佔用一項.
與每個文件描述符相關聯的是:
1)文件描述符標誌(close-on-exec)
2)指向一個文件表項的指針.
注:其中close-on-exec標誌,如果某個文件符設置了該標誌,fcntl(fd,F_SETFD,1),則在該進程調用exec函數之前爲exec族函數釋放
對應的文件描述符
2.內核爲所有打開文件維持一張文件表,每個文件表項包括:
1).文件狀態標誌:讀,寫,添寫,同步,非阻塞等.
2).當前文件偏移量.
3).指向該文件v節點表項的指針.
3.每個打開文件都有個v節點結構,v節點包含了文件類型和對此文件進行各種操作的函數指針. 對於大多數文件,v節點還包含了該文件
的i節點. 這些信息是在打開文件時從磁盤上讀入內存的,所以所有關於文件的信息都是隨時快速可供使用的.(linux下沒有使用v節點
,而是使用了通用i節點結構. 雖然兩種實現有所不同,但是在概念上,v節點與i節點是一樣的. 兩者都指向文件系統特有的i節點結構)
現在我們來大致看看這個結構吧:
創建v節點結構的目的是對在一個計算機系統上的多文件系統類型提供支持. 這一工作是Peter Weinberger和bill joy(sun公司)分
別獨立完成的. sun把這種文件系統成爲虛擬文件系統,把與文件系統無關的i節點部分成爲v節點,當各個製造商的實現增加了對
sun的網絡文件系統的支持時,他們都廣泛採用了v節點結構. linux沒有講相關數據結構分爲i節點和v節點,而是採用了一個與文
件系統相關的i節點和一個與文件系統無關的i節點.如果兩個獨立進程各自打開了同一個文件,則就會出現下圖的這種情況:
我們假定第一個進程在文件描述符3上打開該文件,而另一個進程在文件描述符4上打開該文件. 打開該文件的每個進程都獲得各
自的一個文件表項,但對一個給定的文件只有一個v節點表項.之所以每個進程都獲得自己的文件表項,是因爲這可以使每一個進
程都有它自己的對該文件的當前偏移量.給出了這些數據之後,現在對前面所述的操作進一步說明.
1.在完成每個write後,在文件表項中的當前文件偏移量既增加所寫入的字節數. 如果這導致當前文件偏移量超出了自己當前文
件長度. 則將i節點表項中的當前文件長度設置爲當前文件偏移量(文件加長).
2.如果用O_APPEND標誌打開一個文件,則相應標誌也被設置到文件表項的文件狀態標誌中. 每次對這種具有追加寫標誌的文件執
行寫操作時,文件表項中的當前文件偏移量首先會被設置爲i節點表項中的文件長度,這就使得每次寫入的數據都追加到文件的當
前尾端處.
3.若一個文件用lseek定位到文件當前的尾端,則文件表項中的當前文件偏移量被設置爲i節點表項中的當前文件長度.
4.lessk函數只修改文件表項中的當前文件偏移量,不進行任何I/O操作.
可能有多個文件描述符項指向同一文件表項. 比如下面的dup函數,或者fork後也會出現同樣的情況,此時的父進程,子進程各自
的每一個打開文件描述符共享同一個文件表項. 注意,文件描述符標誌和文件狀態標誌在作用範圍方面的區別,前者只用於一
個進程的一個描述符,而後者則應用於指向該給定文件表項的任何進程中的所有描述符. 接下來我們來認識一下函數dup 和 dup2.
下面兩個函數都可以用來複制一個現有的文件描述符.
#include<unistd.h>
int dup(int fd);
int dup2(int fd,int fd2);
//兩函數的返回值:若成功,返回新的文件描述符. 如出錯返回-1.
由dup返回的新文件描述符一定是當前可用文件描述符中的最小數值. 對於dup2,可以用fds參數指定新描述符的值. 如果fd2已經打
開,則現將其關閉.如若fd等於fd2,則dup2返回fd2,而不關閉它. 否則,fd2的FD_CLOEXEC文件描述符標誌就被清除了,這樣fd2在進
程調用exec時是打開狀態.這些函數返回的新的文件描述符與參數fd共享同一個文件表項. 效果如下圖.
這個dup最常用的地方就是我們的文件重定向,fcntl與它具有同樣功能的還有函數,該函數可以改變已經打開文件的屬性.
#include<fcntl.h>
int fcntl(int fd,int cmd,.../*int arg*/):
返回值:若成功,則依賴於cmd; 若出錯返回-1
現在我們繼續思考,fork()出來的子進程和父進程的文件描述符關係是什麼樣子呢?? 我們首先分析一下: 同一個文件描述符的文件
表項是父子進程共用的資源,所以父子進程共用同一份文件表項,共用一份V節點表,共用同一個i節點表. 那麼我們來看看到底是不是
這樣:
fcntl函數是非常重要的文件控制函數,它的功能和選項實在太多了. 所以我這裏這裏只是提一下,接下來就需要我們繼續努力啦!!!
fcntl有如下5種功能:
1.複製一個已有的描述符(cmd = F_DUPFD或F_DUPFD_COLOEXEC).
2.獲取/設置文件描述符標誌(cmd = F_GETFD或F_SETFD)
3.獲取/設置文件狀態標誌(cmd=F_GETFL或F_SETEL)
4.獲取/設置異步I/O所有權(cmd = F_GETOWN或F_SETOWN)
5.獲取/設置記錄鎖(cmd = F_GETLK,F_SETLK或F_SETLKW)
對於這些函數的具體使用還需要下去多用用多看文檔即可.
當然如果你想對i節點有更深的瞭解,你那你必須瞭解Linux下的文件系統. 博客傳送門->文件系統