跳到主要内容
约 3 分钟3 张卡

文件描述符

文件描述符是进程引用已打开 I/O 对象的非负整数。Linux 文件、socket 和管道都可通过 fd 操作。本文负责引用关系、关闭责任和容量限制。

1. 面试核心​

先记住这几条,再读正文
  • 描述符与打开文件描述。 fd 是进程描述符表的索引,打开文件描述保存偏移和状态标志。
  • 共享偏移。 dup 和 fork 继承的 fd 共享原打开文件描述。卡 · 面试核心
  • 关闭与编号复用。 close 释放一个描述符引用,之后其编号可被复用。
  • 资源耗尽。 EMFILE 表示进程描述符超限,ENFILE 表示系统打开文件数超限。卡 · fd 配额与继承

2. 文件引用层次​

进程 fd 3 ─┐
├→ 打开文件描述 → 文件对象
进程 fd 4 ─┘

open 成功后创建打开文件描述,并返回 fd。两次独立 open 同一路径,文件偏移分别保存。dup 则复制引用,两个 fd 共享偏移。

fork 后,子进程继承 fd 表中的引用。父子各自的 fd 指向同一打开文件描述。共享关系也包含 O_NONBLOCK 等文件状态标志。

FD_CLOEXEC 属于单个描述符的标志。它决定成功 exec 时是否关闭该 fd。dup 新建的描述符默认清除 FD_CLOEXEC。

3. 生命周期与泄漏​

每次成功打开都要明确关闭责任。异常、提前返回和失败重试也要释放 fd。持续泄漏会耗尽描述符配额。

unlink 删除目录项。最后一个硬链接删除后,仍打开的文件继续存在。最后一个打开引用释放后,相关存储才可回收。因此删除日志后仍占空间,要检查进程持有的引用。

Linux close 在 EIO 等错误下也已释放 fd 编号。盲目重试 close 会误关已复用的新对象。关闭前需协调异步任务和并发使用者。

4. fd 配额与继承​

RLIMIT_NOFILE 的软限制约束进程可分配的 fd 编号范围。open 或 accept 超限会返回 EMFILE。系统打开文件表达到上限时返回 ENFILE。

排查时先查看 /proc/PID/fd 和 /proc/PID/limits。再按文件、socket、管道分类统计。描述符持续增长时,应追查未关闭路径。确认是正常容量不足后,再调整服务限制。

设置 O_CLOEXEC 可在 open 时原子启用关闭继承标志。先 open 再 fcntl 存在多线程竞争窗口。另一线程在窗口中执行 fork 与 exec,会把 fd 传入新程序。

5. 使用边界​

异步任务只保存 fd 数字时,要防止编号复用。fd 3 关闭后可成为另一个连接。任务取消、引用生命周期和事件回调必须协同。

close 释放引用,文件内容的持久性由 fsync 等机制控制。标准输入、输出、错误分别使用 0、1、2。它们也能被重定向到其他 I/O 对象。

6 · 面试题卡片​

本页题目与参考回答

6.1 为什么两个文件描述符可能共享读取位置,关闭一个后另一个还能使用?​

考察重点:描述符与打开文件描述的区别、共享偏移和引用生命周期。

相关内容:面试核心、文件引用层次、使用边界。

参考回答

dup 后两个 fd 指向同一打开文件描述,因此共享偏移。

close 释放当前 fd 的引用。另一个引用仍有效时,仍能访问底层对象。两次独立 open 则分别保存文件偏移。

6.2 服务出现 EMFILE 时应该怎样排查?​

考察重点:描述符配额、泄漏与正常容量。

相关内容:fd 配额与继承。

参考回答

EMFILE 表示进程描述符超限。

先核对 RLIMIT_NOFILE 和 /proc/PID/fd。再区分泄漏与正常连接容量。泄漏需要修复关闭路径,正常容量不足则调整限制。

6.3 为什么多线程程序应优先使用 O_CLOEXEC?​

考察重点:原子设置与 exec 继承窗口。

相关内容:fd 配额与继承。

参考回答

O_CLOEXEC 在 open 时原子设置关闭继承标志。

先 open 再 fcntl 会留下竞争窗口。另一线程在窗口中 fork、exec,新程序就会继承该 fd。成功 exec 时,带标志的描述符自动关闭。

7. 参考资料​