文件描述符與FILE

来源:http://www.cnblogs.com/33debug/archive/2017/06/16/7019945.html
-Advertisement-
Play Games

1. 文件描述符(重點) 在Linux系統中一切皆可以看成是文件,文件又可分為:普通文件、目錄文件、鏈接文件和設備文件。文件描述符(file descriptor)是內核為了高效管理已被打開的文件所創建的索引,其是一個非負整數(通常是小整數),用於指代被打開的文件,所有執行I/O操作的系統調用都通過 ...


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 -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<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        else
21               printf("closefile fd.c success!\n");
22        exit(0);
23 }

執行結果:

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

進程通過系統調用open( )來打開一個文件,實質上是獲得一個文件描述符,以便進程通過文件描述符為連接對文件進行其他操作。進程打開文件時,會為該文件創建一個file對象,並把該file對象存入進程打開文件表中(文件描述符數組),進而確定了所打開文件的文件描述符。        open( )操作在內核里通過sys_open( )實現的,sys_open( )將創建文件的dentryinodefile對象,併在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標準庫中,FILEstdio.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,因為標準輸入、輸出、出錯為012。輸出結果如下:
    因此,_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結構之間的關係可以用下圖來表示:

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1.AMD $ sudo apt-get install fglrx 2.Nvidia $ sudo apt-get install nvidia-current installed after restart your computer ...
  • 命令行編輯的輔助操作: Tab健:自動補齊 Ctrl +U :清空至首行 Ctrl +K: 清空至尾行 Ctrl +L:(或者clear) 清屏 Ctrl +C: 取消執行命令 獲取幫助命令: 內部命令help 例如:help cd 大多數外部命令 --help 使用man命令閱讀手冊 使用info ...
  • netstat 命令用於顯示各種網路相關信息,如網路連接,路由表,介面狀態 (Interface Statistics),masquerade 連接,多播成員 (Multicast Memberships) 等等 1 常用選項: -a :(all)顯示所有選項,預設不顯示LISTEN相關 -t :( ...
  • 測試工具:ab(apacheBench) 硬體環境: Intel xeon cpu E5-2682 v4 2.50GHz 4 core 8GBytes 記憶體 軟體環境: ubuntu16 nginx 靜態文件 3Kbytes 26000次響應/秒(平均值)吞吐量:111Mbytes/秒(平均值)併發 ...
  • Bonding 就是講到快網卡綁定到同一IP地址對外服務,可以實現高可用或者負載均衡。當然,直接給兩塊網卡設置同一IP地址是不可能的。通過bonding,虛擬一塊網卡對外提供連接,物理網卡被修改為同一MAC地址。 一 Bonding 的工作模式 Mode 0 (balance-rr) 輪轉(Roun ...
  • 1.PHP程式員玩轉Linux系列-怎麼安裝使用CentOS 2.PHP程式員玩轉Linux系列-lnmp環境的搭建 3.PHP程式員玩轉Linux系列-搭建FTP代碼開發環境 4.PHP程式員玩轉Linux系列-備份還原MySQL 5.PHP程式員玩轉Linux系列-自動備份與SVN 6.PHP程 ...
  • 目錄 "簡介" "常用命令" "功能" "代理" "反向代理" "集群" HTTP集群 TCP集群 "重定向" "靜態文件" "HTTPS配置" "常見問題" 簡介 Nginx ("engine x") 是一款輕量級,高性能的HTTP和反向代理伺服器,也是一個IMAP/POP3/SMTP伺服器。 傳 ...
  • 擴容兩步走: 第一步捲擴容 第二步文件系統擴容 需要註意的是不同Linux發行版本,文件格式有所不同 xfs用 ext3用 是時候給蒼老師安排換個新房子 :) ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...