檔案描述符與FILE,檔案描述符FILE

來源:互聯網
上載者:User

檔案描述符與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以外的系統上去,也不直觀。

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

 而每個檔案中又主要包含以下這些資訊:

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 -n11 102413 [root@localhost ~]#

Linux下最大檔案描述符的限制有兩個方面,一個是使用者級的限制,另外一個則是系統級限制。

系統級限制:sysctl命令和proc檔案系統中查看到的數值是一樣的,這屬於系統級限制,它是限制所有使用者開啟檔案描述符的總和

使用者級限制:ulimit命令看到的是使用者級的最大檔案描述符限制,也就是說每一個使用者登入後執行的程式佔用檔案描述符的總數不能超過這個限制

(2).修改檔案描述符的值

1 [root@localhost ~]# ulimit-SHn 102402 [root@localhost ~]# ulimit  -n3 102404 [root@localhost ~]#

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

1 [root@localhost ~]# grep -vE'^$|^#' /etc/security/limits.conf2 *                hard nofile                  40963 [root@localhost ~]#
1 //預設設定檔中只有hard選項,soft 指的是當前系統生效的設定值,hard 表明系統中所能設定的最大值2 [root@localhost ~]# grep -vE'^$|^#' /etc/security/limits.conf3 *      hard         nofile       102404 *      soft         nofile      102405 [root@localhost ~]#6 // soft<=hard soft的限制不能比hard限制高

(3).修改系統限制

1 [root@localhost ~]# sysctl -wfs.file-max=4000002 fs.file-max = 4000003 [root@localhost ~]# echo350000 > /proc/sys/fs/file-max  //重啟後失效4 [root@localhost ~]# cat /proc/sys/fs/file-max5 3500006 [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<stdio.h> 2 #include<sys/types.h> 3 #include<sys/stat.h> 4 #include<fcntl.h> 5 #include<stdlib.h> 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!\n");11               exit(1);12        }13        else{14               printf("openfile fd.c success:%d\n",fd);15        }16        if(close(fd) < 0){17               perror("closefile fd.c error!\n");18               exit(1);19        }20        else21               printf("closefile fd.c success!\n");22        exit(0);23 }

執行結果:

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);

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

 1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<sys/types.h> 4 #include<sys/stat.h> 5 #include<fcntl.h> 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!\n");14        }15        printf("The openedfile's descriptor is %d\n",myfile->_fileno);16        if(write(myfile->_fileno,buf,50)< 0){17               perror("error:writefile failed!\n");18               exit(1);19        }else{20               printf("writefile successed!\n");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結構之間的關係可以用來表示:

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.