You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何忽略read_dir调用生成的文件描述符?——Rust遍历/proc目录获取进程FD的问题

解决Rust读取/proc/[pid]/fd时出现额外文件描述符的问题

你遇到的这个额外fd(比如测试里的103),本质是fs::read_dir在打开目标/proc/[pid]/fd目录时,会为这个目录本身创建一个文件描述符——而这个fd自然也会出现在你正在遍历的/proc/[pid]/fd列表里,所以被误收集了。直接用pop()完全不可靠,因为目录遍历的顺序不固定,这个额外fd不一定是最后一个元素。下面是两种可靠的最佳实践:

方案1:过滤掉read_dir自身的目录fd

ReadDir类型在Unix-like系统上实现了AsRawFd trait,你可以直接获取它打开目录时用的fd,然后在遍历的时候跳过这个数值。这种方法效率最高,不需要额外的系统调用。

修改后的代码:

use std::fs;
use std::path::Path;
use std::os::unix::io::AsRawFd; // 必须导入这个trait才能使用as_raw_fd

pub fn list_fds(&self) -> Option<Vec<usize>> {
    let mut fd_vec: Vec<usize> = Vec::new();
    let linux_process_fd_path_string = format!("/proc/{}/fd", self.pid);
    let linux_process_fd_path = Path::new(&linux_process_fd_path_string);
    
    // 获取目录的ReadDir实例,同时拿到它的原始fd
    let mut entries = fs::read_dir(linux_process_fd_path).ok()?;
    let dir_fd = entries.as_raw_fd() as usize;

    if linux_process_fd_path.is_dir() {
        for entry in entries {
            let entry = entry.ok()?;
            if let Ok(filename_usize) = entry.file_name().to_string_lossy().parse::<usize>() {
                // 跳过read_dir自身占用的fd
                if filename_usize != dir_fd {
                    fd_vec.push(filename_usize);
                }
            }
        }
    }
    Some(fd_vec)
}

方案2:过滤指向当前/proc/[pid]/fd目录的符号链接

/proc/[pid]/fd下的每个条目都是符号链接,指向对应的文件/资源。read_dir打开的目录对应的符号链接,目标就是/proc/[pid]/fd本身——所以我们可以读取每个条目的链接目标,跳过和当前目录路径一致的条目。这种方法更通用,即使有其他进程也打开了这个目录,也能正确过滤掉相关fd。

修改后的代码:

use std::fs;
use std::path::Path;

pub fn list_fds(&self) -> Option<Vec<usize>> {
    let mut fd_vec: Vec<usize> = Vec::new();
    let linux_process_fd_path_string = format!("/proc/{}/fd", self.pid);
    let linux_process_fd_path = Path::new(&linux_process_fd_path_string);
    let entries = fs::read_dir(linux_process_fd_path).ok()?;

    if linux_process_fd_path.is_dir() {
        for entry in entries {
            let entry = entry.ok()?;
            // 读取符号链接的目标路径
            if let Ok(link_target) = fs::read_link(entry.path()) {
                // 如果链接目标不是当前的fd目录,才收集这个fd
                if link_target != linux_process_fd_path {
                    if let Ok(filename_usize) = entry.file_name().to_string_lossy().parse::<usize>() {
                        fd_vec.push(filename_usize);
                    }
                }
            }
        }
    }
    Some(fd_vec)
}

为什么fd_vec.pop()不是好方案?

目录遍历的顺序由文件系统决定,没有固定的规则——read_dir创建的fd不一定是最后一个被遍历到的元素。在某些场景下,你可能会错误地移除一个真实存在的业务fd,或者根本没移除那个额外的fd,导致结果仍然不符合预期。这种完全依赖遍历顺序的操作是不可靠的,强烈不推荐。

内容的提问来源于stack exchange,提问作者jz steven

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 06:38:11