如何忽略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
相关产品推荐
相关产品推荐

