linux 中 fd 申请和释放管理(两级 bitmap)

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 表示一个 fd
	unsigned long *open_fds;
    // bitmap,一个 bit 表示 BITS_PER_LONG 个 fd
	unsigned 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);
}
  • 9
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值