os/bootloader/docs/INLINE_COMMENTS.md

21 KiB
Raw Permalink Blame History

NOS Bootloader 内联注释指南

本文档提供NOS引导加载程序关键算法和实现细节的内联注释说明帮助开发者理解复杂代码的工作原理。

目录

内存分配器

双级分配器设计

双级分配器结合了bump分配器和分离空闲列表的优势

// 双级分配器结构
pub struct DualLevelAllocator {
    heap: UnsafeCell<[u8; BOOTLOADER_HEAP_SIZE]>,  // 4MB堆空间
    offset: AtomicUsize,                          // 当前分配偏移
    free_list: UnsafeCell<[Option<*mut FreeNode>; NUM_BUCKETS]>, // 分离空闲列表
    free_list_size: AtomicUsize,                   // 空闲列表大小
}

设计原理:

  1. 大块分配(>256字节): 使用bump分配器快速简单
  2. 小块分配(≤256字节): 使用分离空闲列表,支持重用
  3. 内存对齐: 所有分配64字节对齐提高缓存效率

分配算法

unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
    let needed_size = layout.size();
    
    // 小块优先从空闲列表分配
    if needed_size <= SMALL_BLOCK_THRESHOLD {
        if let Some(ptr) = self.alloc_from_free_list(needed_size) {
            return ptr;
        }
    }
    
    // 回退到bump分配器
    let current = self.offset.load(Ordering::Relaxed);
    let heap_ptr = self.heap.get() as *mut u8;
    let aligned = Self::align_up(current, BOOTLOADER_HEAP_ALIGN);
    let new_offset = aligned + needed_size;

    // 检查是否超出堆边界
    if new_offset > BOOTLOADER_HEAP_SIZE {
        return core::ptr::null_mut();
    }

    // 原子更新偏移量
    match self.offset.compare_exchange(
        current,
        new_offset,
        Ordering::Release,
        Ordering::Relaxed,
    ) {
        Ok(_) => heap_ptr.add(aligned),
        Err(actual) => {
            // 重试一次
            let aligned = Self::align_up(actual, BOOTLOADER_HEAP_ALIGN);
            let new_offset = aligned + needed_size;

            if new_offset > BOOTLOADER_HEAP_SIZE {
                return core::ptr::null_mut();
            }

            if self.offset.compare_exchange(
                actual,
                new_offset,
                Ordering::Release,
                Ordering::Relaxed,
            ).is_ok() {
                heap_ptr.add(aligned)
            } else {
                core::ptr::null_mut()
            }
        }
    }
}

关键点:

  1. 原子操作: 使用compare_exchange确保线程安全
  2. 内存对齐: 64字节对齐提高缓存效率
  3. 快速路径: 小块优先从空闲列表分配
  4. 重试机制: 失败时重试一次,避免死循环

释放与合并算法

unsafe fn add_to_free_list(&self, ptr: *mut u8, size: usize) {
    // 检查空闲列表容量
    if self.free_list_size.load(Ordering::Relaxed) >= MAX_FREE_LIST_SIZE {
        return; // 空闲列表已满,直接丢弃
    }
    
    // 步骤1: 检查相邻空闲块并合并
    let mut merged_ptr = ptr;
    let mut merged_size = size;
    
    let heap_start = self.heap.get() as *mut u8;
    let heap_end = heap_start.add(BOOTLOADER_HEAP_SIZE);
    
    // 遍历所有桶查找相邻块
    for bucket_index in 0..NUM_BUCKETS {
        let bucket_ptr = &mut (*self.free_list.get())[bucket_index];
        
        let mut current = *bucket_ptr;
        let mut prev: Option<*mut FreeNode> = None;
        
        while let Some(node) = current {
            let node_ptr = node as *mut u8;
            let node_ref = node.as_ref().unwrap();
            let node_end = node_ptr.add(node_ref.size);
            
            // 检查当前块是否在合并块之前相邻
            if node_end == merged_ptr {
                // 与前一个块合并
                merged_size += node_ref.size;
                merged_ptr = node_ptr;
                
                // 从列表中取消链接
                if let Some(prev_node) = prev {
                    (*prev_node).next = node_ref.next;
                } else {
                    *bucket_ptr = node_ref.next;
                }
                
                self.free_list_size.fetch_sub(1, Ordering::Relaxed);
                break; // 重新开始合并检查
            } 
            // 检查当前块是否在合并块之后相邻
            else if merged_ptr.add(merged_size) == node_ptr {
                // 与后一个块合并
                merged_size += node_ref.size;
                
                // 从列表中取消链接
                if let Some(prev_node) = prev {
                    (*prev_node).next = node_ref.next;
                } else {
                    *bucket_ptr = node_ref.next;
                }
                
                self.free_list_size.fetch_sub(1, Ordering::Relaxed);
                
                // 继续检查下一个块
                current = node_ref.next;
            } 
            else {
                // 移动到列表中的下一个节点
                prev = Some(current);
                current = node_ref.next;
            }
        }
    }
    
    // 步骤2: 将合并后的块插入适当的桶中
    let new_node = merged_ptr as *mut FreeNode;
    let new_node_ref = new_node.as_mut().unwrap();
    new_node_ref.size = merged_size;
    
    let target_bucket = Self::get_bucket_index(merged_size);
    let bucket_ptr = &mut (*self.free_list.get())[target_bucket];
    
    if bucket_ptr.is_none() {
        // 空桶,直接插入
        (*new_node).next = None;
        *bucket_ptr = Some(new_node);
    } else {
        // 插入桶中,按大小升序排列
        let mut current = bucket_ptr.as_ref().copied().unwrap();
        let mut prev: Option<*mut FreeNode> = None;
        
        // 查找插入点
        while current.as_ref().unwrap().size < merged_size {
            prev = Some(current);
            match current.as_ref().unwrap().next {
                Some(next) => current = next,
                None => break,
            }
        }
        
        // 插入新节点
        (*new_node).next = Some(current);
        if let Some(prev_node) = prev {
            (*prev_node).next = Some(new_node);
        } else {
            // 在桶开头插入
            *bucket_ptr = Some(new_node);
        }
    }
    
    // 增加空闲列表大小
    self.free_list_size.fetch_add(1, Ordering::Relaxed);
}

关键点:

  1. 相邻块合并: 检查前后相邻块并合并,减少碎片
  2. 有序插入: 按大小有序插入,便于查找最佳匹配
  3. 容量限制: 限制空闲列表大小,避免内存浪费
  4. 原子更新: 使用原子操作更新列表大小

图形渲染系统

双缓冲渲染流程

pub fn swap_buffers(&mut self) -> Result<(), &'static str> {
    if self.rendering {
        return Err("Cannot swap buffers while rendering");
    }

    if self.dirty_tracking_enabled && !self.dirty_regions.is_empty() {
        // 只更新脏区域
        self.update_dirty_regions()?;
    } else {
        // 全屏更新
        self.copy_full_buffer()?;
    }

    Ok(())
}

设计原理:

  1. 无闪烁渲染: 后台缓冲区绘制,前台缓冲区显示
  2. 脏区域跟踪: 只更新变化区域,提高性能
  3. 全屏回退: 脏区域过多时回退到全屏更新

脏区域合并算法

impl DirtyRect {
    /// 合并两个脏区域,返回包含两者的最小矩形
    pub fn merge(&self, other: &DirtyRect) -> DirtyRect {
        let x1 = self.x.min(other.x);
        let y1 = self.y.min(other.y);
        let x2 = (self.x + self.width).max(other.x + other.width);
        let y2 = (self.y + self.height).max(other.y + other.height);
        
        DirtyRect {
            x: x1,
            y: y1,
            width: x2 - x1,
            height: y2 - y1,
        }
    }
}

关键点:

  1. 最小包围盒: 计算包含两个区域的最小矩形
  2. 简单快速: O(1)时间复杂度
  3. 可能扩大: 合并后的区域可能比原区域总和大

SIMD优化清屏

pub fn clear_screen(&mut self, color: Color) -> Result<(), &'static str> {
    let color_val = color.as_argb8888();
    let total_pixels = (self.fb.width * self.fb.height) as usize;
    let fb_ptr = self.get_draw_buffer();

    unsafe {
        // 尝试使用SIMD优化x86_64平台
        #[cfg(target_arch = "x86_64")]
        {
            use core::arch::x86_64::{_mm_set1_epi32, _mm_storeu_si128};
            
            // 创建4个颜色值的SIMD向量16字节
            let simd_color = _mm_set1_epi32(color_val as i32);
            
            // 计算可被4整除的像素数
            let simd_pixels = total_pixels & !3;
            let simd_end = fb_ptr.add(simd_pixels);
            
            // 使用SIMD批量填充
            let mut current_ptr = fb_ptr as *mut core::arch::x86_64::__m128i;
            while current_ptr < simd_end as *mut _ {
                _mm_storeu_si128(current_ptr, simd_color);
                current_ptr = current_ptr.add(1);
            }
            
            // 处理剩余像素
            let remaining = fb_ptr.add(simd_pixels);
            let end = fb_ptr.add(total_pixels);
            let mut ptr = remaining;
            while ptr < end {
                ptr::write(ptr, color_val);
                ptr = ptr.add(1);
            }
        }
        
        // 非x86_64平台使用常规块填充
        #[cfg(not(target_arch = "x86_64"))]
        {
            let fb_slice = core::slice::from_raw_parts_mut(fb_ptr, total_pixels);
            fb_slice.fill(color_val);
        }
    }

    // 添加脏区域
    if self.double_buffer.is_some() {
        self.add_dirty_region(0, 0, self.fb.width, self.fb.height);
    }

    Ok(())
}

优化原理:

  1. SIMD并行: 一次处理4个像素提高4倍速度
  2. 对齐访问: 128位对齐访问最大化内存带宽
  3. 平台适配: 非SIMD平台回退到常规填充
  4. 剩余处理: 处理不能被4整除的剩余像素

VBE实现

VBE模式缓存

pub struct VbeController {
    controller_info: Option<VbeControllerInfo>,
    supported_modes: Vec<u16>,
    mode_cache: hashbrown::HashMap<u16, CachedModeInfo>, // O(1)查找
    initialized: bool,
}

缓存优势:

  1. O(1)查找: 哈希表提供常数时间查找
  2. 减少BIOS调用: 避免重复的硬件查询
  3. 预加载: 预先加载常用模式
  4. 内存换时间: 用少量内存换取性能提升

VBE中断调用

unsafe fn vbe_interrupt(&self, interrupt: u8, regs: &mut VbeRegisters) -> VbeRegisters {
    let mut result = VbeRegisters {
        ax: regs.ax,
        bx: regs.bx,
        cx: regs.cx,
        dx: regs.dx,
        si: regs.si,
        di: regs.di,
        es: regs.es,
    };
    
    // 执行BIOS中断0x10进行VBE调用
    asm!(
        "int $0x10",
        // 输入输出寄存器
        inout("ax") result.ax,
        inout("bx") result.bx,
        inout("cx") result.cx,
        inout("dx") result.dx,
        inout("si") result.si,
        inout("di") result.di,
        inout("es") result.es,
        // 内存可能被修改
        clobber("memory"),
        options(att_syntax),
    );
    
    result
}

关键点:

  1. 内联汇编: 直接调用BIOS中断
  2. 寄存器约束: 正确设置输入输出寄存器
  3. 内存屏障: 告诉编译器内存可能被修改
  4. AT&T语法: 使用AT&T汇编语法

引导编排器

引导流程控制

pub fn boot_system(&mut self, cmdline: Option<&str>) -> BootResult<BootInfo> {
    // 阶段1: 加载配置
    let config = self.load_boot_configuration(cmdline)?;
    
    // 阶段2: 检测硬件
    let hw_info = self.hardware_detection.detect_hardware()
        .map_err(|e| BootError::HardwareError(e))?;
    
    // 阶段3: 验证先决条件
    BootValidator::validate_prerequisites(&config, &hw_info)?;
    
    // 阶段4: 初始化图形
    if config.graphics_mode.is_some() {
        self.initialize_graphics(&config)?;
    }
    
    // 阶段5: 创建引导信息
    let mut boot_info = BootInfo::new(self.di_container.protocol_type());
    
    // 阶段6: 加载内核
    boot_info.kernel_address = 0x100000;
    boot_info.kernel_size = 0x500000;
    boot_info.boot_timestamp = 0;
    
    // 阶段7: 验证引导信息
    boot_info.validate()?;
    
    Ok(boot_info)
}

设计原则:

  1. 阶段分离: 明确的引导阶段,便于调试
  2. 错误处理: 每个阶段都有适当的错误处理
  3. 可扩展性: 易于添加新的引导阶段
  4. 依赖管理: 正确的阶段依赖关系

硬件检测

CPU特性检测

pub fn detect_cpu_features(&self) -> CpuFeatures {
    let mut features = CpuFeatures::new();
    
    // 使用CPUID指令检测CPU特性
    unsafe {
        let mut eax: u32;
        let mut ebx: u32;
        let mut ecx: u32;
        let mut edx: u32;
        
        // 基本CPUID信息
        asm!(
            "cpuid",
            in("eax") 1,
            out("eax") eax,
            out("ebx") ebx,
            out("ecx") ecx,
            out("edx") edx,
        );
        
        // 解析特性标志
        if edx & (1 << 23) != 0 { features.set_mmx(); }
        if edx & (1 << 25) != 0 { features.set_sse(); }
        if edx & (1 << 26) != 0 { features.set_sse2(); }
        if ecx & (1 << 0) != 0 { features.set_sse3(); }
        if ecx & (1 << 9) != 0 { features.set_ssse3(); }
        if ecx & (1 << 19) != 0 { features.set_sse4_1(); }
        if ecx & (1 << 20) != 0 { features.set_sse4_2(); }
        
        // 扩展特性检测
        asm!(
            "cpuid",
            in("eax") 0x80000001,
            out("eax") eax,
            out("ebx") ebx,
            out("ecx") ecx,
            out("edx") edx,
        );
        
        if edx & (1 << 29) != 0 { features.set_long_mode(); }
    }
    
    features
}

检测原理:

  1. CPUID指令: 使用标准CPUID指令获取特性
  2. 位标志解析: 解析特性寄存器中的位标志
  3. 扩展检测: 检测扩展特性(如长模式)
  4. 安全汇编: 使用安全的内联汇编语法

双缓冲机制

脏区域更新算法

fn update_dirty_regions(&self) -> Result<(), &'static str> {
    for region in &self.dirty_regions {
        self.copy_region(region)?;
    }
    Ok(())
}

fn copy_region(&self, region: &DirtyRect) -> Result<(), &'static str> {
    let fb = &self.fb_info;
    
    // 边界检查
    if region.x >= fb.width || region.y >= fb.height {
        return Ok(()); // 超出边界的区域忽略
    }
    
    // 计算实际复制的区域(裁剪到屏幕边界)
    let x_end = (region.x + region.width).min(fb.width);
    let y_end = (region.y + region.height).min(fb.height);
    let actual_width = x_end - region.x;
    let actual_height = y_end - region.y;
    
    unsafe {
        for y in region.y..y_end {
            let src_offset = (y * fb.width + region.x) as usize;
            let dst_offset = (y * fb.width + region.x) as usize;
            
            let src_ptr = self.back_buffer.add(src_offset);
            let dst_ptr = self.front_buffer.add(dst_offset);
            
            ptr::copy_nonoverlapping(src_ptr, dst_ptr, actual_width as usize);
        }
    }
    
    Ok(())
}

优化原理:

  1. 区域裁剪: 只复制屏幕内的区域
  2. 逐行复制: 按行复制,提高缓存命中率
  3. 非重叠复制: 使用copy_nonoverlapping确保安全
  4. 边界检查: 防止越界访问

SIMD优化

批量像素绘制

pub fn draw_pixels_batch(&mut self, pixels: &[(u32, u32, Color)]) -> Result<(), &'static str> {
    if pixels.is_empty() {
        return Ok(());
    }

    let fb_ptr = self.get_draw_buffer();
    
    // 计算脏区域边界
    let mut min_x = u32::MAX;
    let mut min_y = u32::MAX;
    let mut max_x = 0u32;
    let mut max_y = 0u32;

    unsafe {
        for &(x, y, color) in pixels {
            if self.fb.in_bounds(x, y) {
                let offset = (y * self.fb.width + x) as usize;
                ptr::write(fb_ptr.add(offset), color.as_argb8888());
                
                // 更新脏区域边界
                min_x = min_x.min(x);
                min_y = min_y.min(y);
                max_x = max_x.max(x);
                max_y = max_y.max(y);
            }
        }
    }

    // 添加脏区域
    if self.double_buffer.is_some() && min_x != u32::MAX {
        let width = max_x - min_x + 1;
        let height = max_y - min_y + 1;
        self.add_dirty_region(min_x, min_y, width, height);
    }

    Ok(())
}

优化原理:

  1. 批量处理: 减少函数调用开销
  2. 边界计算: 一次计算所有像素的边界
  3. 条件检查: 只在需要时添加脏区域
  4. 内存局部性: 连续访问内存,提高缓存效率

内存对齐优化

64字节对齐策略

fn align_up(addr: usize, align: usize) -> usize {
    (addr + align - 1) & !(align - 1)
}

pub fn scanline_bytes(&self) -> usize {
    let bytes_per_pixel = (self.bits_per_pixel as usize + 7) / 8;
    let raw = self.width as usize * bytes_per_pixel;
    // 对齐到64字节边界以提高缓存效率
    ((raw + 63) / 64) * 64
}

对齐原理:

  1. 缓存行大小: 64字节是常见缓存行大小
  2. 对齐公式: (addr + align - 1) & !(align - 1)
  3. 性能提升: 对齐访问提高内存带宽利用率
  4. SIMD友好: 对齐内存更适合SIMD操作

错误处理策略

统一错误处理

#[derive(Debug)]
pub enum BootError {
    HardwareError(&'static str),
    ProtocolInitializationFailed(&'static str),
    NotInitialized,
    DeviceError(&'static str),
    InvalidParameter(&'static str),
}

impl fmt::Display for BootError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            BootError::HardwareError(msg) => write!(f, "硬件错误: {}", msg),
            BootError::ProtocolInitializationFailed(msg) => write!(f, "协议初始化失败: {}", msg),
            BootError::NotInitialized => write!(f, "组件未初始化"),
            BootError::DeviceError(msg) => write!(f, "设备错误: {}", msg),
            BootError::InvalidParameter(msg) => write!(f, "无效参数: {}", msg),
        }
    }
}

错误处理原则:

  1. 明确分类: 按错误类型分类
  2. 详细信息: 提供有意义的错误信息
  3. 可转换性: 实现标准错误特征
  4. 链式传播: 使用?操作符传播错误

性能分析

关键路径优化

  1. 内存分配: 双级分配器减少碎片
  2. 图形渲染: 双缓冲+脏区域跟踪
  3. SIMD优化: 利用向量指令加速
  4. 缓存策略: 模式缓存减少硬件调用

内存使用优化

  1. 64字节对齐: 提高缓存效率
  2. 批量操作: 减少函数调用开销
  3. 延迟初始化: 按需初始化组件
  4. 资源池: 重用频繁分配的对象

安全考虑

内存安全

  1. 边界检查: 所有数组访问都进行边界检查
  2. 生命周期: 正确管理对象生命周期
  3. 并发安全: 使用原子操作保护共享数据
  4. unsafe代码: 最小化unsafe代码块

输入验证

  1. 参数验证: 验证所有输入参数
  2. 范围检查: 检查数值范围
  3. 格式验证: 验证数据格式
  4. 长度检查: 防止缓冲区溢出

调试支持

日志记录

pub fn log_boot_phase(phase: BootPhase, details: &str) {
    match phase {
        BootPhase::Initialization => println!("[boot] 初始化: {}", details),
        BootPhase::HardwareDetection => println!("[boot] 硬件检测: {}", details),
        BootPhase::MemoryInitialization => println!("[boot] 内存初始化: {}", details),
        BootPhase::GraphicsInitialization => println!("[boot] 图形初始化: {}", details),
        BootPhase::KernelLoading => println!("[boot] 内核加载: {}", details),
        BootPhase::KernelLoadComplete => println!("[boot] 内核加载完成: {}", details),
        BootPhase::ReadyForKernel => println!("[boot] 准备跳转到内核: {}", details),
    }
}

性能测量

pub struct BootTimer {
    start_time: u64,
}

impl BootTimer {
    pub fn new() -> Self {
        Self {
            start_time: rdtsc(),
        }
    }
    
    pub fn elapsed(&self) -> u64 {
        rdtsc() - self.start_time
    }
    
    pub fn elapsed_ms(&self) -> f64 {
        let cycles = self.elapsed();
        let mhz = cpu_frequency() as f64 / 1_000_000.0;
        cycles as f64 / mhz / 1_000.0
    }
}

这些内联注释提供了NOS引导加载程序关键算法和实现细节的详细说明帮助开发者理解代码的工作原理和设计决策。