在 32 位的 linux/unix 系统下,每个程序打开一个文件都会有一段 4G 的虚拟地址空间,这部分空间中,有1G是内核地址空间,3G是用户地址空间,这个概念我们在之前的文章中有介绍过,可参考 “不同位置的变量在内存中的排布”,其中在内核地址空间中,维护着一个 PCB 进程控制块,其中包含很多进程相关的信息,比如进程ID、用户ID、组ID等等,但本文我们最关注的,是 PCB 进程控制块中维护的一份“文件描述符表”,表的格式抽象成下图的样子。

2015-06-24_230952 图中右侧表示的,就是我们今天讨论的主题“文件描述符表”,其内部有0~1023个id可以提供用户申请分配,每当用户使用一个 open 操作打开一个文件时,都会返回一个文件描述符,比如如下代码:

1
int nRet = open("/dev/zero", O_RDONLY);

这句代码会在当前进程内核区文件描述符表中申请一个新的文件描述符赋值给nRet值,系统默认情况下会帮我们打开三个文件描述符,0代表是标准输入、1代表是标准输出、2代表标准Error。我们open的文件将从第三个开始使用,所以nRet的返回值就是3。 而当我们将nRet使用close函数关闭时,3这个位置就被空闲出来了。再次open某个文件时,会再次从最小的值开始占用,也就还是占用3这个位置。 每一个系统对于文件描述符的数量限制是不同的,我们可以通过 ulimit -a 命令查看默认的系统限制是多少: 2015-06-24_232701 当然我们也可以修改这个值,使用命令 ulimit -n 2048,就可以将 open file 的值设定为2048了,这样我们每个程序就可以打开0~2047个文件描述符了(这种情况很少见)。 2015-06-24_233007 要注意的是,并不是你想设置多大就可以设置多大的,系统默认情况下是有最高上限的,可以通过查看 /proc/sys/fs/file-max 文件得到上限值: 2015-06-24_233118 从图中我们可以看到,上限值是204042,所以我们最大也只能设置这么大了。