← 项目列表
课程已完成2026年6月

文件系统仿真 — Rust+egui

用 Rust 模拟类 FAT 文件系统,结合 egui 构建跨平台 GUI,位图可视化一目了然。

Rust操作系统文件系统GUIRusteguibit-vec
📋 项目概览
技术栈
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)disk.rs · 58 行

使用位图管理 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)fs.rs · 57 行

文件系统核心逻辑,包括目录项管理、主文件目录(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)app.rs · 59 行

基于 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.rsegui 图形界面应用180
src/shell.rs命令解析与执行150
src/visual.rs磁盘状态可视化渲染85
src/main.rs程序入口40
tests/integration_test.rs集成测试95
网站智能助手
💬 和我聊聊
🤖
你好呀 👋 有什么想聊的?