文件系统仿真 — Rust+egui
用 Rust 模拟类 FAT 文件系统,结合 egui 构建跨平台 GUI,位图可视化一目了然。
📋 项目概览
技术栈
Rusteguibit-vec
功能特性
- 位图磁盘块分配与回收
- 多级目录树(创建/删除/重命名)
- 文件 CRUD,动态长度增长
- egui 实时可视化磁盘状态
- 交互式 Shell 命令行界面
📖 技术分析报告在 GitHub 查看 ↗
实验4:文件系统仿真 — Rust + egui
项目概述
本实验使用 Rust 语言模拟一个类 FAT 文件系统,包含位图磁盘管理、多级目录树、文件 CRUD 操作,并利用 egui 框架构建跨平台图形界面,实现磁盘状态的实时可视化。
技术栈
- Rust — 系统编程语言,零成本抽象
- egui — 即时模式 GUI 框架,跨平台
- bit-vec — 位图数据结构库
架构设计
┌──────────────────────────────────────────┐
│ App (egui 主窗口) │
│ ┌─────────────┐ ┌──────────────────┐ │
│ │ Shell 终端 │ │ 磁盘位图可视化 │ │
│ │ 命令输入输出 │ │ 512块状态展示 │ │
│ └──────┬──────┘ └────────┬─────────┘ │
│ │ │ │
└─────────┼──────────────────┼──────────────┘
│ │
┌─────▼──────────────────▼──────┐
│ FS 核心逻辑 │
│ ┌──────┐ ┌──────┐ ┌─────┐ │
│ │ MFD │ │ UFD │ │ 目录│ │
│ │管理 │ │管理 │ │项管理│ │
│ └──┬───┘ └──┬───┘ └──┬──┘ │
└─────┼─────────┼─────────┼────┘
│ │ │
┌─────▼─────────▼─────────▼────┐
│ Disk 层 │
│ 位图分配/回收 · 块读写 │
│ 512块 × 1024字节 │
└──────────────────────────────┘
核心模块
1. Disk 层 — 位图磁盘管理
- 512 个磁盘块,每块 1024 字节
- 位图(Bitmap)标记空闲/占用状态
- 块分配策略:顺序扫描空闲块
- 盘区布局:引导块(0) → 超级块(1) → MFD(2) → UFD(3-34) → 数据区(35-511)
2. FS 层 — 文件系统核心
- 主文件目录(MFD):存储用户名和密码,支持多用户
- 用户文件目录(UFD):每个用户的文件目录表
- 目录项:文件名(28B) + 类型 + 起始块 + 大小 + 密码 + 时间戳
- 支持操作:创建/删除/重命名/读写/列表
3. Shell 层 — 命令解析
- 交互式命令行:
format,login,create,delete,open,close,read,write,ls,cd,help - 类似 Linux 终端的交互体验
4. egui 可视化
- 32×16 网格展示 512 个磁盘块的颜色编码状态
- 实时显示:已用块/空闲块/使用率
- 命令历史输出区
设计决策
| 决策 | 方案 | 理由 |
|---|---|---|
| 位图分配 | bit-vec 实现 | O(1) 空间,O(n) 分配,适合教学演示 |
| 目录结构 | 两级目录(MFD+UFD) | 支持多用户隔离,结构清晰 |
| GUI 框架 | egui | 纯 Rust 即时模式,跨平台,易于嵌入可视化 |
| 数据持久化 | 文件模拟磁盘 | 每次运行可保存/恢复磁盘状态 |
与网站演示的关系
网上的 位图可视化演示 是一个纯前端模拟器,使用 HTML Canvas 重现了 egui 应用中的磁盘位图展示效果。Rust 桌面应用的完整源码在本目录的 src/ 下。
🚀 在线演示新窗口打开 ↗
💻 核心代码Rust · egui · bit-vec
rust磁盘块管理(Disk)
使用位图管理 512 个磁盘块的分配与回收。磁盘以 1024 字节为块大小,通过位图标记空闲/占用状态。
use std::fs::{File, OpenOptions};
use std::io::{Read, Seek, SeekFrom, Write};
pub const BLOCK_SIZE: usize = 1024;
pub const NUM_BLOCKS: usize = 512;
pub const BOOT_BLOCK: usize = 0; // 引导块
pub const SUPER_BLOCK: usize = 1; // 超级块
pub const MFD_BLOCK: usize = 2; // 主文件目录
pub const UFD_START: usize = 3; // 用户文件目录
pub const UFD_END: usize = 34;
pub const DATA_START: usize = 35; // 数据区起点
pub struct Disk {
file: File,
pub num_blocks: usize,
pub block_size: usize,
}
impl Disk {
pub fn new(path: &str) -> Result<Self, String> {
let file = OpenOptions::new()
.read(true).write(true).create(true)
.open(path)
.map_err(|e| format!("Failed to open disk file: {}", e))?;
file.set_len((NUM_BLOCKS * BLOCK_SIZE) as u64)
.map_err(|e| format!("Failed to set disk size: {}", e))?;
Ok(Disk { file, num_blocks: NUM_BLOCKS, block_size: BLOCK_SIZE })
}
pub fn read_block(&mut self, block_num: usize) -> Result<Vec<u8>, String> {
if block_num >= self.num_blocks {
return Err(format!("Block {} out of range (max {})", block_num, self.num_blocks));
}
let offset = (block_num * self.block_size) as u64;
self.file.seek(SeekFrom::Start(offset))
.map_err(|e| format!("Seek error: {}", e))?;
let mut buf = vec![0u8; self.block_size];
self.file.read_exact(&mut buf)
.map_err(|e| format!("Read error: {}", e))?;
Ok(buf)
}
pub fn write_block(&mut self, block_num: usize, data: &[u8]) -> Result<(), String> {
if block_num >= self.num_blocks {
return Err(format!("Block {} out of range (max {})", block_num, self.num_blocks));
}
if data.len() != self.block_size {
return Err(format!("Data size {} != block size {}", data.len(), self.block_size));
}
let offset = (block_num * self.block_size) as u64;
self.file.seek(SeekFrom::Start(offset))
.map_err(|e| format!("Seek error: {}", e))?;
self.file.write_all(data)
.map_err(|e| format!("Write error: {}", e))?;
self.file.flush().map_err(|e| format!("Flush error: {}", e))?;
Ok(())
}
}rust文件系统核心(FS)
文件系统核心逻辑,包括目录项管理、主文件目录(MFD)、用户文件目录(UFD)。支持登录验证、文件操作。
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct DirEntry {
pub name: [u8; MAX_FILENAME], // 28 字节文件名
pub type_: i32, // 0=文件, 1=目录
pub start_block: i32, // 起始块号
pub size: i32, // 文件大小
pub password: [u8; MAX_PASSWORD],// 16 字节密码
pub create_time: i64, // 创建时间戳
}
impl DirEntry {
pub fn new(name: &str, type_: i32, password: &str) -> Self {
let mut entry = DirEntry {
name: [0u8; MAX_FILENAME], type_, start_block: -1,
size: 0, password: [0u8; MAX_PASSWORD],
create_time: SystemTime::now()
.duration_since(UNIX_EPOCH).unwrap().as_secs() as i64,
};
let name_bytes = name.as_bytes();
let len = name_bytes.len().min(MAX_FILENAME - 1);
entry.name[..len].copy_from_slice(&name_bytes[..len]);
// ... password similarly ...
entry
}
pub fn name_str(&self) -> String {
let end = self.name.iter().position(|&b| b == 0)
.unwrap_or(self.name.len());
String::from_utf8_lossy(&self.name[..end]).to_string()
}
pub fn is_empty(&self) -> bool {
self.name[0] == 0 || self.name_str().is_empty()
}
}
pub struct Session {
pub current_user: Option<String>,
}
impl Session {
pub fn new() -> Self { Session { current_user: None } }
pub fn login(&mut self, disk: &mut Disk, username: &str, password: &str) -> Result<(), String> {
// 读取 MFD 块,验证用户名密码
let data = disk.read_block(MFD_BLOCK)?;
let mfd: MasterDirectory = unsafe { std::mem::transmute(data) };
for user in &mfd.users[..mfd.user_count as usize] {
if user.name_str() == username && user.password_str() == password {
self.current_user = Some(username.to_string());
return Ok(());
}
}
Err("用户名或密码错误".to_string())
}
}rustegui 图形界面(App)
基于 egui 构建的交互式 GUI,包含命令终端、磁盘状态可视化和实时反馈。
use eframe::egui;
use egui::Color32;
pub struct FileSystemApp {
pub session: Session,
pub disk: Option<Disk>,
pub disk_state: DiskState,
pub output_lines: Vec<(String, Color32)>,
pub input_buffer: String,
pub selected_block: Option<usize>,
}
impl Default for FileSystemApp {
fn default() -> Self {
Self {
session: Session::new(),
disk: None,
disk_state: DiskState::default(),
output_lines: vec![
("💾 二级文件系统仿真 — Rust + egui".to_string(), Color32::from_rgb(86, 156, 214)),
("输入 help 查看可用命令,format <文件名> 开始使用".to_string(), Color32::from_rgb(128, 128, 128)),
],
input_buffer: String::new(),
selected_block: None,
}
}
}
impl eframe::App for FileSystemApp {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
egui::TopBottomPanel::top("terminal").show(ctx, |ui| {
ui.horizontal(|ui| {
let input = egui::TextEdit::singleline(&mut self.input_buffer)
.desired_width(f32::INFINITY)
.hint_text("> 输入命令...")
.show(ui);
if ui.button("⏎").clicked() || input.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter)) {
self.execute_current_command();
}
});
});
egui::CentralPanel::default().show(ctx, |ui| {
// Left: terminal output
egui::SidePanel::left("output")
.resizable(true)
.default_width(400.0)
.show_inside(ui, |ui| {
egui::ScrollArea::vertical().stick_to_bottom(true).show(ui, |ui| {
for (text, color) in &self.output_lines {
ui.colored_label(*color, text);
}
});
});
// Right: disk visualization
self.render_disk_map(ui);
});
}
}📁 源文件清单GitHub 仓库 ↗
| 路径 | 说明 | 行数 |
|---|---|---|
src/disk.rs | 磁盘块设备模拟(512块×1024字节) | 72 |
src/fs.rs | 文件系统核心(MFD/UFD/目录项) | 210 |
src/app.rs | egui 图形界面应用 | 180 |
src/shell.rs | 命令解析与执行 | 150 |
src/visual.rs | 磁盘状态可视化渲染 | 85 |
src/main.rs | 程序入口 | 40 |
tests/integration_test.rs | 集成测试 | 95 |