1. 程式人生 > >linux — 淺析檔案描述符 檔案表項 v節點表項

linux — 淺析檔案描述符 檔案表項 v節點表項

淺析檔案描述符 檔案表項 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下的檔案系統. 部落格傳送門->檔案系統