linux 中 fd 的幾點理解_linux fd-CSDN博客
通過上邊的文章,我們可以知道,在 linux 中,fd 有以下幾點需要了解:
(1)fd 表示進程打開的文件,是進程級別的資源,不是系統級別的資源
(2)struct task_struct 在內核中用于描述一個進程,其中打開的文件使用 fd table 來描述
(3)在用戶態看 linux,一些皆文件
(4)一個進程可以打開的文件個數是有限制的,使用 ulimit -a 可以查看
那么在 linux 中,當我們打開一個文件的時候,會返回一個 fd,fd 是一種資源,在內核中是怎么維護這些資源的呢 ?當關閉一個文件的時候,會釋放這個 fd,釋放的時候又是怎么釋放的呢 ?
可以想象,如果讓我們自己來實現的話,我們會選擇一個 bitmap 來維護 fd 的被使用情況。系統默認情況下,一個進程可以打開的文件個數是 1024,我們就需要維護一個長度為 1024 的 bitmap。如下圖所示,表示一個長度為 1024 的 bitmap,bitmap 的下標從 0 到 1023 表示 1024 個 fd,bitmap 中的內容 1 表示 fd 被使用,0 表示 fd 沒有被使用。下圖表示 fd 0、1、2、501 被使用,其它的 fd 沒有被使用。
那么當我們打開一個文件的時候,是怎么分配 fd 的呢,是每次都要遍歷 bitmap,從中選擇一個空閑的 fd 來返回嗎 ?這種方式是最基礎的方法,當然是可行的。缺點在于,每次都要遍歷 bitmap,如果 bitmap 0~1000 都已經被使用,1001 沒有沒使用,這個時候我們就需要做 1000 次無用的查詢,效率比較低。當我們關閉文件,釋放 fd 的時候,是比較好理解的,直接使用 fd 作為下標,找到對應的 bit,直接將該 bit 設置為 0 即可。
1 fd 上下邊界
fd 最小是 0,最大可以使用 ulimit -a 來查看。默認情況下,系統允許一個進程最多打開 1024 個文件,所以 fd 最大值為 1023。所以默認情況下,進程內的 fd 的取值范圍是 [0, 1023]。
2 申請 fd
2.1 數據結構 struct fdtable 和函數 find_next_fd
struct fdtable 中有以下幾個成員和 fd 的維護有關。
struct fdtable {// 進程能打開的文件個數的最大值unsigned int max_fds;...// bitmap,一個 bit 表示一個 fdunsigned long *open_fds;// bitmap,一個 bit 表示 BITS_PER_LONG 個 fdunsigned long *full_fds_bits;...
};
在函數 find_next_fd 中,空閑 fd 的查找分了兩步來完成:
(1)先在 full_fds_bits 中查找,如果文件個數最多是 1024 個,在 64 位機器上 long 類型長度市是 64 個 bit。那么 full_fds_bit 的長度是 16(1024/64),第 0 bit 就能代表 open_fds 中的第 0 到第 63bit,第 1bit 能代表 open_fds 中的第 64 到 127bit,以此類推。只要第 64 到 127bit 有空閑的 fd,哪怕只有 1 個,那么在 full_fds_bit 中的第 1 bit 也會標志為空閑。
(2)在第一步中已經在 full_fds_bits 找到了空閑的 bit,這個 bit 能把查找范圍縮小到 64 個 bit 范圍之內。然后第二步中從 full_fds_bits 中查找具體空閑的 bit。
static unsigned int find_next_fd(struct fdtable *fdt, unsigned int start)
{unsigned int maxfd = fdt->max_fds;unsigned int maxbit = maxfd / BITS_PER_LONG;unsigned int bitbit = start / BITS_PER_LONG;bitbit = find_next_zero_bit(fdt->full_fds_bits, maxbit, bitbit) * BITS_PER_LONG;if (bitbit > maxfd)return maxfd;if (bitbit > start)start = bitbit;return find_next_zero_bit(fdt->open_fds, maxfd, start);
}
使用兩級 bitmap 來查找空閑的 fd,對性能做了優化。
如果使用一級 bitmap,那么查找次數平均下來要 1024 次。
使用兩級 bitmap,查找次數平均下來是 16 + 64 = 80 次。16 是第一級 map 查找的次數,64 是第二級 bitmap 查找的次數。
3 釋放 fd
釋放 fd 相對來說好理解,直接使用 fd 做下標找到 bitmap 中對應的 bit,然后將 bit 清除即可。關閉 fd 的時候,會通過函數?__put_unused_fd() 最終調用 導函數 __clear_open_fd()。
static inline void __clear_open_fd(unsigned int fd, struct fdtable *fdt)
{__clear_bit(fd, fdt->open_fds);__clear_bit(fd / BITS_PER_LONG, fdt->full_fds_bits);
}