徹底搞懂檔案描述符 檔案控制代碼 檔案指標的區別與聯絡

2021-10-02 15:40:16 字數 4218 閱讀 3053

處理了一起too many open files的報錯,中途忽然感覺這三個概念很容易混淆,網上其他部落格也是眾說紛紜。於是做了一點考證,專門寫一篇來盡量準確地記錄下。

本文的內容有不少來自linux領域的權威書籍,michael kerrisk所著《the linux programming inte***ce:a linux and unix system programming handbook》的第4、5兩章。

檔案描述符(file descriptor, fd)是linux系統中對已開啟檔案的乙個抽象標記,所有i/o系統呼叫對已開啟檔案的操作都要用到它。這裡的「檔案」仍然是廣義的,即除了普通檔案和目錄外,還包括管道、fifo(命名管道)、socket、終端、裝置等。

檔案描述符是乙個較小的非負整數,並且0、1、2三個描述符總是預設分配給標準輸入、標準輸出和標準錯誤。這就是常用的nohup ./my_script > my_script.log 2>&1 &命令裡2和1的由來。

linux系統中的每個程序會在其程序控制塊(pcb)內維護屬於自己的檔案描述符表(file descriptor table)。表中每個條目包含兩個域:一是控制該描述符的標記域(flags),二是指向系統級別的開啟檔案表中對應條目的指標。那麼開啟檔案表又是什麼呢?

檔案描述符表、開啟檔案表、inode表之間的關係可以用書中的下圖來表示。注意圖中的fd 0、1、2...只是示意下標,不代表三個標準描述符。

可見,乙個開啟的檔案可以對應多個檔案描述符(不管是同程序還是不同程序),乙個inode也可以對應多個開啟的檔案。開啟檔案表中的一行稱為一條檔案描述(file description),也經常稱為檔案控制代碼(file handle)。

多嘴一句,「控制代碼」這個詞在unix世界中並不很正式,但在windows裡遍地都是。windows nt核心會將記憶體中的所有物件(檔案、視窗、選單、圖示等一切東西)的位址列表維護成整數索引,這個整數就叫做控制代碼,邏輯上講類似於「指標的指標」,感覺上還是有一些相通的地方的。

說了這麼多,用最基礎的posix庫函式寫個示例程式吧。它將乙個檔案中的內容讀出來,並原封不動地寫入另外乙個檔案。

#include #include #define buf_size 1024

int main(int argc,char *ar**)

outputfd = open(

"data_copy.txt",

o_creat | o_wronly | o_trunc,

s_irusr | s_iwusr | s_irgrp | s_iwgrp | s_iroth | s_iwoth

);if (outputfd == -1)

while ((numread = read(inputfd, buf, buf_size)) > 0)

} close(inputfd);

close(outputfd);

exit(exit_success);

}

嚴格來講,posix提供的這些函式只是使用者與核心之前的橋梁,實際仍位於系統呼叫層之上。但是現實應用中,我們一般也把它們叫做系統呼叫了(儘管不太正確)。

要使用open()/read()/write()/close()這些系統呼叫,必須引入fcntl.h標頭檔案。open()返回的是檔案描述符,其引數中傳入的flags和mode值也會儲存在開啟檔案表中。在整個讀、寫並最終關閉檔案的過程中,操作的也都是檔案描述符。

那麼我們在大學c語言課程上學習的「檔案指標」(file pointer)又是什麼呢?這個就比較簡單,繼續看下面的栗子。

#include #include #define buf_size 1024

int main(int argc,char *ar**)

while (!feof(inputfp))

fclose(inputfp);

exit(exit_success);

}

可見,檔案指標就是file結構體的指標,與前兩個概念不屬於同一層。當通過檔案指標操作檔案時,需要呼叫c語言stdio.h中提供的檔案api(fopen()、fread()等),而c標準庫最終呼叫了posix的庫函式。並且「file pointer」這個詞裡的「file」指的是狹義的檔案,不包括管道、裝置等其他東西,所以單純用c api只能操作普通檔案。

file結構體中是包含了檔案描述符的,所以c語言也提供了互相轉換的方法:

int inputfd;

file *inputfp;

inputfd = fileno(inputfp);

inputfp = fdopen(inputfd, "r");

~ ulimit -a

core file size (blocks, -c) 0

data seg size (kbytes, -d) unlimited

scheduling priority (-e) 0

file size (blocks, -f) unlimited

pending signals (-i) 127961

max locked memory (kbytes, -l) 64

max memory size (kbytes, -m) unlimited

open files (-n) 65535

pipe size (512 bytes, -p) 8

posix message queues (bytes, -q) 819200

real-time priority (-r) 0

stack size (kbytes, -s) 8192

cpu time (seconds, -t) unlimited

max user processes (-u) 127961

virtual memory (kbytes, -v) unlimited

file locks (-x) unlimited

其中open files一行就表示當前使用者、當前終端、單個程序能擁有的檔案描述符的數量閾值(很多文章都描述錯了這一點),可以用ulimit -n [閾值]命令來臨時修改,退出登入即失效。如果想要永久修改,可以將ulimit -n [閾值]寫入使用者的.bash_profile檔案或/etc/profile中,也可以修改/etc/security/limits.conf:

~ vim /etc/security/limits.conf

# 使用者名稱 軟/硬限制 限制項 閾值

root soft nofile 65535

root hard nofile 65535

那麼如何列出各個程序的檔案描述符呢?可以利用lsof(list open files)命令。這個命令的用法很豐富,本文暫時不表。

既然有了程序級別的描述符數量限制,也就有系統級別的檔案控制代碼數量限制。可以這樣檢視其閾值,以及當前已分配的控制代碼數:

~ cat /proc/sys/fs/file-max

3247469 # 閾值

~ cat /proc/sys/fs/file-nr

# 已分配且使用中 / 已分配但未使用 / 閾值

2976 0 3247469

如果需要臨時修改,可以直接向file-max寫入新值。永久生效的方法是修改/etc/sysctl.conf:

~ vim /etc/sysctl.conf

fs.file-max = 5242880

# 立即生效

~ sysctl -p

最後總結一下吧。

明天公司年會,民那晚安晚安。

檔案控制代碼 檔案描述符

檔案控制代碼和檔案描述符 在我們跨平台開發的時候,經常會碰到這倆個概念 檔案描述符 本質上是乙個索引號 非負整數 系統使用者層可以根據它找到系統核心層的檔案資料。這是乙個posix標準下的概念,常見於linux系統。但windows也有檔案描述符這個概念,但不常用。檔案控制代碼 windows下的概...

檔案控制代碼 檔案描述符

由於程序級檔案描述符表的存在,不同的程序中會出現相同的檔案描述符,它們可能指向同乙個檔案,也可能指向不同的檔案。兩個不同的檔案描述符,若指向同乙個開啟檔案控制代碼 file 將共享同一檔案偏移量。因此,如果通過其中乙個檔案描述符來修改檔案偏移量,那麼從另乙個檔案描述符中也會觀察到變化,無論這兩個檔案...

Linux 檔案控制代碼 檔案描述符

這裡我們先區分好兩個概念 檔案描述符和檔案控制代碼 簡單來說,每個程序都有乙個開啟的檔案表 fdtable 表中的每一項是struct file型別,包含了開啟檔案的一些屬性比如偏移量,讀寫訪問模式等,這是真正意義上的檔案控制代碼。檔案描述符是乙個整數。代表fdtable中的索引位置 下標 指向具體...