ᕕ( ᐛ )ᕗ Jimyag's Blog

Linux 文件描述符 fd

在使用 Linux 的过程中,我们平时经常看到下面这样的用法:

echo log > /dev/null 2>&1

> :表示将输出结果重定向到哪里,例如:echo“123” > /home/123.txt /dev/null :表示空设备文件

所以 echo log > /dev/null 表示把日志输出到空文件设备,也就是将打印信息丢弃掉,屏幕上什么也不显示。

1 :表示 stdout 标准输出 2 :表示 stderr 标准错误 & :表示等同于的意思

所以 2>&1 表示 2 的输出重定向等同于 1,也就是标准错误输出重定向到标准输出。因为前面标准输出已经重定向到了空设备文件,所以标准错误输出也重定向到空设备文件。

这个用法平时很常见,重点是为什么这里是用 2 和 1 ,不是 3456 什么的呢?这要从 Linux 中的文件描述符说起。

文件描述符(file descriptor

我们知道在 Linux 系统中一切皆可以看成是文件,文件又可分为:普通文件目录文件链接文件设备文件。在操作这些所谓的文件的时候,我们每操作一次就找一次名字,这会耗费大量的时间和效率。所以 Linux 中规定每一个文件对应一个索引,这样要操作文件的时候,我们直接找到索引就可以对其进行操作了。

文件描述符(file descriptor)就是内核为了高效管理这些已经被打开的文件所创建的索引,其是一个非负整数(通常是小整数),用于指代被打开的文件,所有执行 I/O 操作的系统调用都通过文件描述符来实现。同时还规定系统刚刚启动的时候,0 是标准输入,1 是标准输出,2 是标准错误。这意味着如果此时去打开一个新的文件,它的文件描述符会是 3,再打开一个文件文件描述符就是 4……

Linux 内核对所有打开的文件有一个文件描述符表格,里面存储了每个文件描述符作为索引与一个打开文件相对应的关系,简单理解就是下图这样一个数组,文件描述符(索引)就是文件描述符表这个数组的下标,数组的内容就是指向一个个打开的文件的指针。

1

上面只是简单理解,实际上关于文件描述符,Linux 内核维护了 3 个数据结构

一个 Linux 进程启动后,会在内核空间中创建一个 PCB 控制块,PCB 内部有一个文件描述符表(File descriptor table),记录着当前进程所有可用的文件描述符,也即当前进程所有打开的文件。进程级的描述符表的每一条记录了单个进程所使用的文件描述符的相关信息,进程之间相互独立,一个进程使用了文件描述符3,另一个进程也可以用3

除了进程级的文件描述符表,系统还需要维护另外两张表:打开文件表、i-node 表。这两张表存储了每个打开文件的打开文件句柄(open file handle)。一个打开文件句柄存储了与一个打开文件相关的全部信息。

系统级的打开文件描述符表:

文件系统的 i-node 表:

文件描述符、打开的文件句柄以及 i-node 之间的关系如下图: 1

这就说明:

  1. 同一个进程的不同文件描述符可以指向同一个文件;
  2. 不同进程可以拥有相同的文件描述符;不同进程的同一个文件描述符可以指向不同的文件(一般也是这样,除了 0、1、2 这三个特殊的文件);
  3. 不同进程的不同文件描述符也可以指向同一个文件。

Linux 上打开文件举例

在 Linux 上用 tail -f test.py 打开一个文件,保持打开状态,再新打开一个新的 shell,输入命令pidof tail 获取 tail 进程的 pid 号,然后 ll /proc/$pid/fd 查看 tail 进程所使用的文件描述符列表,可以看到文件描述符确实是从3开始使用的。tail 不是编辑器不存在修改文件的情况,所以直接文件描述符直接打开的源文件。实际上可以使用 ll /proc/$pid/fd命令获取当前运行的任意进程的文件描述符使用情况。

1

Linux 配置系统最大打开文件描述符个数

系统级限制

理论上系统内存有多少就可以打开多少的文件描述符,但是在实际中内核是会做相应的处理,一般最大打开文件数会是系统内存的 10%(以 KB 来计算),称之为系统级限制。这个数字可以通过 cat /proc/sys/fs/file-max或者 sysctl -a | grep fs.file-max 命令查看。

➜  ~ sudo sysctl -a | grep fs.file-max
fs.file-max = 9223372036854775807
➜  ~ cat /proc/sys/fs/file-max
9223372036854775807

更改系统级限制有临时更改和永久更改两种方式:

用户级限制

同时为了控制每个进程消耗的文件资源,内核也会对单个进程最大打开文件数做默认限制,即用户级限制。32 位系统默认值一般是 1024,64 位系统默认值一般是 65535,可以使用 ulimit -n 命令查看。

#linux