os/vfs/fd_table.rs
1//! 文件描述符表
2//!
3//! 该模块实现了进程级的文件描述符管理,提供 POSIX 兼容的文件描述符操作。
4//!
5//! # 核心组件
6//!
7//! - [`FDTable`] - 文件描述符表,每个进程维护一个实例
8//! - [`FdFlags`] - 文件描述符标志(如 FD_CLOEXEC)
9//!
10//! # 设计理念
11//!
12//! ## 文件描述符的本质
13//!
14//! 文件描述符(FD)是进程级的整数索引,指向打开的文件对象:
15//!
16//! ```text
17//! FDTable
18//! ┌────┬──────────────────────┐
19//! │ 0 │ Arc<StdinFile> │ ← stdin
20//! │ 1 │ Arc<StdoutFile> │ ← stdout
21//! │ 2 │ Arc<StderrFile> │ ← stderr
22//! │ 3 │ Arc<RegFile> │
23//! │ 4 │ Arc<PipeFile> │
24//! │ 5 │ None │
25//! └────┴──────────────────────┘
26//! ```
27//!
28//! ## POSIX 语义
29//!
30//! ### 最小可用 FD
31//!
32//! `alloc()` 总是返回最小的可用文件描述符:
33//!
34//! ```rust
35//! let fd1 = fd_table.alloc(file1)?; // 返回 3
36//! let fd2 = fd_table.alloc(file2)?; // 返回 4
37//! fd_table.close(3)?;
38//! let fd3 = fd_table.alloc(file3)?; // 返回 3 (重用)
39//! ```
40//!
41//! ### dup 语义
42//!
43//! `dup` 系列操作共享 File 对象(包括 offset):
44//!
45//! ```text
46//! fd[3] ──┐
47//! ├──> Arc<RegFile> { offset: AtomicUsize }
48//! fd[4] ──┘
49//!
50//! // fd[3] 和 fd[4] 共享 offset,一个 read() 会影响另一个
51//! ```
52//!
53//! ### FD 标志 vs 文件状态标志
54//!
55//! - **FD 标志** (FdFlags): 文件描述符级别,dup 时不共享
56//! - `FD_CLOEXEC`: exec 时关闭
57//! - **文件状态标志** (OpenFlags): File 对象级别,dup 时共享
58//! - `O_RDONLY`/`O_WRONLY`/`O_RDWR`: 访问模式
59//! - `O_APPEND`: 追加模式
60//! - `O_NONBLOCK`: 非阻塞模式
61//!
62//! ## fork 和 exec
63//!
64//! ### fork 时
65//!
66//! 父子进程共享整个 FDTable:
67//!
68//! ```text
69//! 父进程 ──┐
70//! ├──> Arc<FDTable>
71//! 子进程 ──┘
72//! ```
73//!
74//! ### exec 时
75//!
76//! 自动关闭带 `FD_CLOEXEC` 标志的文件:
77//!
78//! ```rust
79//! fd_table.close_exec(); // exec 前调用
80//! ```
81//!
82//! # 并发安全
83//!
84//! FDTable 内部使用 `SpinLock` 保护:
85//! - `files`: `Vec<Option<Arc<dyn File>>>`
86//! - `fd_flags`: `Vec<FdFlags>`
87//!
88//! 多线程访问 FDTable 时会竞争锁,但 File 对象本身(如 RegFile 的 offset)
89//! 使用原子操作,无需额外锁定。
90//!
91//! # 使用示例
92//!
93//! ## 基本操作
94//!
95//! ```rust
96//! use vfs::{FDTable, RegFile};
97//!
98//! let fd_table = FDTable::new();
99//!
100//! // 分配文件描述符
101//! let file = Arc::new(RegFile::new(dentry, OpenFlags::O_RDONLY));
102//! let fd = fd_table.alloc(file)?;
103//!
104//! // 访问文件
105//! let file = fd_table.get(fd)?;
106//! file.read(&mut buf)?;
107//!
108//! // 关闭文件描述符
109//! fd_table.close(fd)?;
110//! ```
111//!
112//! ## dup 操作
113//!
114//! ```rust
115//! // dup: 复制到最小可用 FD
116//! let new_fd = fd_table.dup(old_fd)?;
117//!
118//! // dup2: 复制到指定 FD
119//! fd_table.dup2(old_fd, new_fd)?;
120//!
121//! // dup3: dup2 + 设置标志
122//! fd_table.dup3(old_fd, new_fd, OpenFlags::O_CLOEXEC)?;
123//! ```
124//!
125//! ## 重定向示例
126//!
127//! ```rust
128//! // 将 stdout 重定向到文件
129//! let file = open_file("/tmp/output.txt", O_WRONLY | O_CREAT)?;
130//! let fd = fd_table.alloc(file)?;
131//! fd_table.dup2(fd, 1)?; // 1 = stdout
132//! fd_table.close(fd)?;
133//! // 现在所有 println! 都会写到文件
134//! ```
135
136use crate::config::DEFAULT_MAX_FDS;
137use crate::sync::SpinLock;
138use crate::uapi::fcntl::{FdFlags, OpenFlags};
139use crate::vfs::{File, FsError};
140use alloc::sync::Arc;
141use alloc::vec::Vec;
142use core::fmt;
143
144/// 文件描述符表
145///
146/// # 并发安全
147///
148/// 内部使用 `SpinLock` 保护,支持多线程访问。
149pub struct FDTable {
150 /// 文件描述符数组
151 /// None 表示该 FD 未使用
152 files: SpinLock<Vec<Option<Arc<dyn File>>>>,
153
154 /// 文件描述符标志数组(与 files 索引对应)
155 /// 默认值为 FdFlags::empty()
156 fd_flags: SpinLock<Vec<FdFlags>>,
157
158 /// 最大文件描述符数量
159 max_fds: usize,
160}
161
162impl FdFlags {
163 /// 从 OpenFlags 中提取 FD 标志(用于兼容性)
164 ///
165 /// `O_CLOEXEC` 在 open() 时可以指定,但本质上是 FD 标志。
166 pub fn from_open_flags(flags: OpenFlags) -> Self {
167 let mut fd_flags = FdFlags::empty();
168 if flags.contains(OpenFlags::O_CLOEXEC) {
169 fd_flags |= FdFlags::CLOEXEC;
170 }
171 fd_flags
172 }
173}
174
175impl fmt::Debug for FDTable {
176 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
177 let files = self.files.lock();
178 let used = files.iter().filter(|slot| slot.is_some()).count();
179 f.debug_struct("FDTable")
180 .field("max_fds", &self.max_fds)
181 .field("slots", &files.len())
182 .field("used", &used)
183 .finish()
184 }
185}
186
187impl FDTable {
188 /// 创建新的文件描述符表
189 pub fn new() -> Self {
190 Self {
191 files: SpinLock::new(Vec::new()),
192 fd_flags: SpinLock::new(Vec::new()),
193 max_fds: DEFAULT_MAX_FDS,
194 }
195 }
196
197 /// 分配一个新的文件描述符(默认无 FD 标志)
198 pub fn alloc(&self, file: Arc<dyn File>) -> Result<usize, FsError> {
199 self.alloc_with_flags(file, FdFlags::empty())
200 }
201
202 /// 分配一个新的文件描述符并指定 FD 标志
203 pub fn alloc_with_flags(&self, file: Arc<dyn File>, flags: FdFlags) -> Result<usize, FsError> {
204 let mut files = self.files.lock();
205 let mut fd_flags = self.fd_flags.lock();
206
207 // 查找最小可用 FD
208 for (fd, slot) in files.iter_mut().enumerate() {
209 if slot.is_none() {
210 *slot = Some(file);
211 fd_flags[fd] = flags;
212 return Ok(fd);
213 }
214 }
215
216 // 如果没有空闲槽位,扩展数组
217 let fd = files.len();
218 if fd >= self.max_fds {
219 return Err(FsError::TooManyOpenFiles);
220 }
221
222 files.push(Some(file));
223 fd_flags.push(flags);
224 Ok(fd)
225 }
226
227 /// 在指定的 FD 位置安装文件(默认无 FD 标志)
228 pub fn install_at(&self, fd: usize, file: Arc<dyn File>) -> Result<(), FsError> {
229 self.install_at_with_flags(fd, file, FdFlags::empty())
230 }
231
232 /// 在指定的 FD 位置安装文件并指定 FD 标志
233 pub fn install_at_with_flags(
234 &self,
235 fd: usize,
236 file: Arc<dyn File>,
237 flags: FdFlags,
238 ) -> Result<(), FsError> {
239 let mut files = self.files.lock();
240 let mut fd_flags = self.fd_flags.lock();
241
242 if fd >= self.max_fds {
243 return Err(FsError::InvalidArgument);
244 }
245
246 // 扩展数组到指定大小
247 while files.len() <= fd {
248 files.push(None);
249 fd_flags.push(FdFlags::empty());
250 }
251
252 // 替换(旧文件会自动通过 Arc 释放)
253 files[fd] = Some(file);
254 fd_flags[fd] = flags;
255 Ok(())
256 }
257
258 /// 获取文件对象
259 pub fn get(&self, fd: usize) -> Result<Arc<dyn File>, FsError> {
260 let files = self.files.lock();
261 files
262 .get(fd)
263 .and_then(|f| f.clone())
264 .ok_or(FsError::BadFileDescriptor)
265 }
266
267 /// 关闭文件描述符
268 pub fn close(&self, fd: usize) -> Result<(), FsError> {
269 let mut files = self.files.lock();
270 let mut fd_flags = self.fd_flags.lock();
271
272 if fd >= files.len() || files[fd].is_none() {
273 return Err(FsError::BadFileDescriptor);
274 }
275
276 files[fd] = None;
277 fd_flags[fd] = FdFlags::empty();
278 Ok(())
279 }
280
281 /// 复制文件描述符
282 ///
283 /// 返回新的 fd,与 old_fd 指向同一个 `Arc<dyn File>` (共享 offset)。
284 pub fn dup(&self, old_fd: usize) -> Result<usize, FsError> {
285 let file = self.get(old_fd)?;
286 self.alloc(file)
287 }
288
289 /// 复制文件描述符,新 fd >= min_fd(F_DUPFD 语义)
290 ///
291 /// 返回新的 fd,与 old_fd 指向同一个 `Arc<dyn File>` (共享 offset)。
292 /// 新分配的 fd 是 >= min_fd 的最小未使用文件描述符。
293 pub fn dup_from(&self, old_fd: usize, min_fd: usize, flags: FdFlags) -> Result<usize, FsError> {
294 let file = self.get(old_fd)?;
295 let mut files = self.files.lock();
296 let mut fd_flags = self.fd_flags.lock();
297
298 // 1. 先确保数组至少有 min_fd 个元素
299 while files.len() < min_fd {
300 files.push(None);
301 fd_flags.push(FdFlags::empty());
302 }
303
304 // 2. 从 min_fd 开始查找最小可用 FD
305 for (fd, slot) in files.iter_mut().enumerate().skip(min_fd) {
306 if slot.is_none() {
307 *slot = Some(file);
308 fd_flags[fd] = flags;
309 return Ok(fd);
310 }
311 }
312
313 // 3. 如果没有空闲槽位,在数组末尾分配新的 fd
314 let fd = files.len();
315 if fd >= self.max_fds {
316 return Err(FsError::TooManyOpenFiles);
317 }
318
319 files.push(Some(file));
320 fd_flags.push(flags);
321 Ok(fd)
322 }
323
324 /// 复制文件描述符到指定位置
325 ///
326 /// 如果 new_fd 已打开,先关闭它。
327 pub fn dup2(&self, old_fd: usize, new_fd: usize) -> Result<usize, FsError> {
328 // 特殊情况:如果两个 FD 相同,直接返回
329 if old_fd == new_fd {
330 // 检查 old_fd 是否有效
331 self.get(old_fd)?;
332 return Ok(new_fd);
333 }
334
335 // 调用 dup3,不设置任何标志
336 self.dup3(old_fd, new_fd, OpenFlags::empty())
337 }
338
339 /// 复制文件描述符到指定位置(dup3 语义)
340 ///
341 /// 如果 new_fd 已打开,先关闭它。
342 /// 与 dup2 不同,dup3 不允许 old_fd == new_fd。
343 ///
344 /// # 参数
345 /// - `flags`: 可以包含 `O_CLOEXEC`,用于设置新 FD 的 CLOEXEC 标志
346 pub fn dup3(&self, old_fd: usize, new_fd: usize, flags: OpenFlags) -> Result<usize, FsError> {
347 // dup3 不允许 old_fd == new_fd
348 if old_fd == new_fd {
349 return Err(FsError::InvalidArgument);
350 }
351
352 let file = self.get(old_fd)?;
353
354 // 如果 new_fd 已打开,先关闭它(忽略错误)
355 let _ = self.close(new_fd);
356
357 // 提取 FD 标志
358 let fd_flags = FdFlags::from_open_flags(flags);
359
360 self.install_at_with_flags(new_fd, file, fd_flags)?;
361 Ok(new_fd)
362 }
363
364 /// 克隆整个文件描述符表(用于 fork)
365 ///
366 /// 所有 `Arc<dyn File>` 引用计数递增,父子进程共享文件对象。
367 /// FD 标志也会被复制。
368 pub fn clone_table(&self) -> Self {
369 let files = self.files.lock().clone();
370 let fd_flags = self.fd_flags.lock().clone();
371 Self {
372 files: SpinLock::new(files),
373 fd_flags: SpinLock::new(fd_flags),
374 max_fds: self.max_fds,
375 }
376 }
377
378 /// 关闭所有带有 CLOEXEC 标志的文件(用于 exec)
379 pub fn close_exec(&self) {
380 let mut files = self.files.lock();
381 let mut fd_flags = self.fd_flags.lock();
382
383 for (slot, flags) in files.iter_mut().zip(fd_flags.iter_mut()) {
384 if flags.contains(FdFlags::CLOEXEC) {
385 *slot = None;
386 *flags = FdFlags::empty();
387 }
388 }
389 }
390
391 /// 获取文件描述符标志 (F_GETFD)
392 pub fn get_fd_flags(&self, fd: usize) -> Result<FdFlags, FsError> {
393 let files = self.files.lock();
394 let fd_flags = self.fd_flags.lock();
395
396 if fd >= files.len() || files[fd].is_none() {
397 return Err(FsError::BadFileDescriptor);
398 }
399
400 Ok(fd_flags[fd])
401 }
402
403 /// 设置文件描述符标志 (F_SETFD)
404 pub fn set_fd_flags(&self, fd: usize, flags: FdFlags) -> Result<(), FsError> {
405 let files = self.files.lock();
406 let mut fd_flags = self.fd_flags.lock();
407
408 if fd >= files.len() || files[fd].is_none() {
409 return Err(FsError::BadFileDescriptor);
410 }
411
412 fd_flags[fd] = flags;
413 Ok(())
414 }
415}