bigywater.tech
Rust · 134

文件描述符与资源释放

Rust 用所有权管理文件描述符:drop 就关闭。这篇用真实的 fd 计数验证每一种情形 —— 正常释放、泄漏、上限、克隆、移交给裸 fd、重复关闭,以及子进程继承。

第 61438 次
不 drop 地打开文件时,open 开始失败(Too many open files)的位置
4 → 1004
mem::forget 1000 个 File 后进程的 fd 数
-1(EBADF)
对同一个 fd 手动 close 第二次的返回值

drop 就关闭(含作用域结束的自动释放)

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
    打开 1 个文件:fd = 3,进程里打开的 fd 数 4 → 5
    再打开 1 个:fd = 4,数量 6
    drop(f1) 之后数量 5
    再开一个,拿到的 fd = 3(复用了刚释放的编号)
    块内再开一个,数量 7
    块结束(自动 drop)之后数量 6

Drop 的规则见Drop Trait 与资源释放;文件读写见std::fs 文件读写。

fd 的上限

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
    RLIMIT_NOFILE 软限制 = 1048576(getrlimit 报告的值)
    第 61438 次 open 失败:kind = TooManyOpenFiles,os_error = Some(24),Too many open files (os error 24)
    一共成功打开 61437 个(加上进程启动时已有的 stdin/out/err 等)
    释放 10 个之后再 open: 成功

上限在不同系统与配置下不同;这里 getrlimit 返回的值与实际失败点不一致,原因没有深究。

泄漏

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
    每轮函数结束时 File 被 drop:处理了 1000 / 1000 个,结束后 fd 数 4
    mem::forget 让 fd 泄漏:打开了 1000 个并忘记关闭,结束后 fd 数 1004(从 4 涨到 1004,一个都没有回收)

克隆与移交

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
    try_clone:原 fd = 3,克隆 fd = 4(不同编号,指向同一个打开的文件),数量 2
    原 File 已 drop,克隆仍然可读:"hello"
    into_raw_fd 得到 fd 3:File 已经被消耗,但 fd 仍然打开(数量 2),要自己关闭
    手动 close 两次:第一次返回 0,第二次返回 -1(-1 = EBADF:fd 已经关了)
    OwnedFd::from_raw_fd 重新交给 Rust 管理:数量 2
    drop(OwnedFd) 之后数量 1

裸指针的类似 into_raw / from_raw 见原始指针解引用。

子进程

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
    child has no fd 3 (not inherited)

子进程见子进程与 Command。

分步演示

文件描述符与资源释放
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / rustc 1.98.1(brew)/ cargo 1.98.1;单个文件用 rustc 直接编译,需要依赖时用 cargo(经代理从 crates.io 下载);计时用 std::time::Instant,数字每次运行会有小幅抖动。/dev/fd 统计方式在 macOS 上适用
    • 没有验证:Linux 上 /proc/self/fd 的统计、fd 上限的具体来源(getrlimit 与实际不一致)、把 fd 传给子进程(Stdio::from)、socket fd 的 SO_LINGER、重复 close 在多线程下误关别人 fd 的后果、O_CLOEXEC 的实现细节
    京ICP备2026067502号-1京公网安备11010502063487号