清华训练营悟道篇之操作系统的内存管理

news2024/12/28 4:58:04

文章目录

  • SV39多级页表的硬件机制
  • 物理页帧的管理
  • 多级页表管理
  • 内核与应用的地址空间

SV39多级页表的硬件机制

在这里插入图片描述

  • 三级页表
    • 共3*9 = 27位虚拟页号
      • 第 26-18 位为一级页索引 VPN0
      • 第 17-9 位为二级页索引 VPN1
      • 第 8-0 位为三级页索引 VPN2
    • 每个页表都用 9 位索引
      • 2^9 =512 个页表项
      • 每个页表项 64位 = 8 字节
      • 每个页表大小都为 512×8=4KiB
      • 每个页表刚好被放到一个物理页框中
    • 虚拟地址 (VPN0,VPN1,VPN2,offset) :
      • 首先会记录装载「当前所用的一级页表的物理页」的页号到 satp 寄存器中;
      • 把 VPN0 作为偏移在一级页表的物理页中找到二级页表的物理页号;
      • 把 VPN1 作为偏移在二级页表的物理页中找到三级页表的物理页号;
      • 把 VPN2 作为偏移在三级页表的物理页中找到要访问位置的物理页号;
      • 物理页号对应的物理页基址(即物理页号左移12位)加上 offset 就是虚拟地址对应的物理地址

物理页帧的管理

  • [ekernel, MEMORY_END)
    • 总共 8M - rCore内核所占内存的大小
/// Allocate a physical page frame in FrameTracker style
pub fn frame_alloc() -> Option<FrameTracker> {
    FRAME_ALLOCATOR
        .exclusive_access()
        .alloc()
        .map(FrameTracker::new)
}

/// Deallocate a physical page frame with a given ppn
pub fn frame_dealloc(ppn: PhysPageNum) {
    FRAME_ALLOCATOR.exclusive_access().dealloc(ppn);
}
  • RAII
impl Drop for FrameTracker {
    fn drop(&mut self) {
        frame_dealloc(self.ppn);
    }
}

多级页表管理

/// page table structure
pub struct PageTable {
    root_ppn: PhysPageNum,
    frames: Vec<FrameTracker>,
}

/// Assume that it won't oom when creating/mapping.
impl PageTable {
    /// Create a new page table
    pub fn new() -> Self {
        let frame = frame_alloc().unwrap();
        PageTable {
            root_ppn: frame.ppn,
            frames: vec![frame],
        }
    }
}
  • PageTable

    • PageTable 要保存它根节点的物理页号 root_ppn 作为页表唯一的区分标志
    • frames: Vec: 以 FrameTracker 的形式保存了页表所有的节点(包括根节点)所在的物理页帧
  • 内核中直接访问物理地址(恒等映射)

    • get_pte_array 得到某级页表的所有页表项
impl PhysPageNum {
    /// Get the reference of page table(array of ptes)
    pub fn get_pte_array(&self) -> &'static mut [PageTableEntry] {
        let pa: PhysAddr = (*self).into();
        unsafe { core::slice::from_raw_parts_mut(pa.0 as *mut PageTableEntry, 512) }
    }
    /// Get the reference of page(array of bytes)
    pub fn get_bytes_array(&self) -> &'static mut [u8] {
        let pa: PhysAddr = (*self).into();
        unsafe { core::slice::from_raw_parts_mut(pa.0 as *mut u8, 4096) }
    }
    /// Get the mutable reference of physical address
    pub fn get_mut<T>(&self) -> &'static mut T {
        let pa: PhysAddr = (*self).into();
        pa.get_mut()
    }
}
  • map and unmap
    • 从一个虚拟地址中得到一级、二级、三级页表索引
impl VirtPageNum {
    /// Get the indexes of the page table entry
    pub fn indexes(&self) -> [usize; 3] {
        let mut vpn = self.0;
        let mut idx = [0usize; 3];
        for i in (0..3).rev() {
            idx[i] = vpn & 511;
            vpn >>= 9;
        }
        idx
    }
}
    • 逐级遍历页表,如果发现对应的页表没有被建立,则申请一个物理页框并填上一级页表项
/// Find PageTableEntry by VirtPageNum, create a frame for a 4KB page table if not exist
fn find_pte_create(&mut self, vpn: VirtPageNum) -> Option<&mut PageTableEntry> {
    let idxs = vpn.indexes();
    let mut ppn = self.root_ppn;
    let mut result: Option<&mut PageTableEntry> = None;
    for (i, idx) in idxs.iter().enumerate() {
        let pte = &mut ppn.get_pte_array()[*idx];
        if i == 2 {
            result = Some(pte);
            break;
        }
        if !pte.is_valid() {
            let frame = frame_alloc().unwrap();
            *pte = PageTableEntry::new(frame.ppn, PTEFlags::V);
            self.frames.push(frame);
        }
        ppn = pte.ppn();
    }
    result
}
    • 逐级遍历页表,如果发现对应的页表没有被建立,则申请一个物理页框并填上一级页表项
/// Find PageTableEntry by VirtPageNum, create a frame for a 4KB page table if not exist
fn find_pte_create(&mut self, vpn: VirtPageNum) -> Option<&mut PageTableEntry> {
    let idxs = vpn.indexes();
    let mut ppn = self.root_ppn;
    let mut result: Option<&mut PageTableEntry> = None;
    for (i, idx) in idxs.iter().enumerate() {
        let pte = &mut ppn.get_pte_array()[*idx];
        if i == 2 {
            result = Some(pte);
            break;
        }
        if !pte.is_valid() {
            let frame = frame_alloc().unwrap();
            *pte = PageTableEntry::new(frame.ppn, PTEFlags::V);
            self.frames.push(frame);
        }
        ppn = pte.ppn();
    }
    result
}
    • map与unmap的具体实现
 /// set the map between virtual page number and physical page number
 #[allow(unused)]
 pub fn map(&mut self, vpn: VirtPageNum, ppn: PhysPageNum, flags: PTEFlags) {
     let pte = self.find_pte_create(vpn).unwrap();
     assert!(!pte.is_valid(), "vpn {:?} is mapped before mapping", vpn);
     *pte = PageTableEntry::new(ppn, flags | PTEFlags::V);
 }
 /// remove the map between virtual page number and physical page number
 #[allow(unused)]
 pub fn unmap(&mut self, vpn: VirtPageNum) {
     let pte = self.find_pte(vpn).unwrap();
     assert!(pte.is_valid(), "vpn {:?} is invalid before unmapping", vpn);
     *pte = PageTableEntry::empty();
 }

内核与应用的地址空间

  • 逻辑段 MapArea
    • 描述一段建立了映射的连续的虚拟地址区间
    • 管理虚拟页面到物理页框的映射关系
    • 管理页表映射方式(MapType)与映射的标志位(MapPermission)
  • 地址空间管理
    • 页表 + 一个 vec 维护的 MapArea
  • 内核地址空间
  • 应用地址空间
    • 在 os/src/build.rs 中,不再将丢弃了所有符号的应用二进制镜像链接进内核,
    • 直接使用 ELF 格式的可执行文件
    • 这个loader子模块在后续chapter中会被文件系统替代
    • 需要学习解析 ELF格式 文件的过程
    • 联想一下编译原理学到的相关知识
  • 基于地址空间修改分时多任务
    • trampoline 这个东西需要多读几遍,很有意思
  • 在 __alltraps 中需要借助寄存器 jr 而不能直接 call trap_handler 了。因为在 内存布局中,这条 .text.trampoline 段中的跳转指令和 trap_handler 都在代码段之内,汇编器(Assembler) 和链接器(Linker)会根据 linker.ld 的地址布局描述,设定电子指令的地址,并计算二者地址偏移量 并让跳转指令的实际效果为当前 pc 自增这个偏移量。但实际上我们知道由于我们设计的缘故,这条跳转指令在被执行的时候, 它的虚拟地址被操作系统内核设置在地址空间中的最高页面之内,加上这个偏移量并不能正确的得到 trap_handler 的入口地址。
    • 内核
      • 跳板页与内核栈

在这里插入图片描述

      • 内核代码的内存布局 (os/src/linker.ld里面定义的东西)在这里插入图片描述
    • 用户
      • 用户的 linker.ld 定义的内存布局
        在这里插入图片描述
/// Mention that trampoline is not collected by areas.
fn map_trampoline(&mut self) {
    self.page_table.map(
        VirtAddr::from(TRAMPOLINE).into(),
        PhysAddr::from(strampoline as usize).into(),
        PTEFlags::R | PTEFlags::X,
    );
}
 .text : {
     *(.text.entry)
     . = ALIGN(4K);
     strampoline = .;
     *(.text.trampoline);
     . = ALIGN(4K);
     *(.text .text.*)
 }
    .section .text.trampoline
    .globl __alltraps
    .globl __restore
    .align 2
__alltraps:
    # ...
    # load kernel_satp into t0
    ld t0, 34*8(sp)
    # load trap_handler into t1
    ld t1, 36*8(sp)
    # move to kernel_sp
    ld sp, 35*8(sp)
    # switch to kernel space
    csrw satp, t0
    sfence.vma
    # jump to trap_handler
    jr t1
__restore:
    # a0: *TrapContext in user space(Constant); a1: user space token
    # switch to user space
    csrw satp, a1
    sfence.vma
    csrw sscratch, a0
    • trap_return
/// finally, jump to new addr of __restore asm function
pub fn trap_return() -> ! {
    set_user_trap_entry();
    let trap_cx_ptr = TRAP_CONTEXT_BASE;
    let user_satp = current_user_token();
    extern "C" {
        fn __alltraps();
        fn __restore();
    }
    let restore_va = __restore as usize - __alltraps as usize + TRAMPOLINE;
    // trace!("[kernel] trap_return: ..before return");
    unsafe {
        asm!(
            "fence.i",
            "jr {restore_va}",         // jump to new addr of __restore asm function
            restore_va = in(reg) restore_va,
            in("a0") trap_cx_ptr,      // a0 = virt addr of Trap Context
            in("a1") user_satp,        // a1 = phy addr of usr page table
            options(noreturn)
        );
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1148657.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据结构-顺序表6

八.返回key的前驱下标&#xff0c;如果不存在&#xff08;key无前驱&#xff0c;在表头&#xff09;返回-1函数 思路&#xff1a;找到key的前驱&#xff0c;调用Search函数找key就可以 这里把i<0写成i<0&#xff0c;也是可以的&#xff0c;因为i0接着进入下面return i-1…

响应式相册写真摄影网站模板源码

模板信息&#xff1a; 模板编号&#xff1a;28526 模板编码&#xff1a;UTF8 模板颜色&#xff1a;黑白 模板分类&#xff1a;摄像、婚庆、家政、保洁 适合行业&#xff1a;婚纱摄影类企业 模板介绍&#xff1a; 本模板自带eyoucms内核&#xff0c;无需再下载eyou系统&#x…

springboot第44集:Kafka集群和Lua脚本

servers&#xff1a;Kafka服务器的地址。这是Kafka集群的地址&#xff0c;生产者将使用它来发送消息。retries&#xff1a;在消息发送失败时&#xff0c;生产者将尝试重新发送消息的次数。这个属性指定了重试次数。batchSize&#xff1a;指定了生产者在发送消息之前累积的消息大…

6.scala辅助构造器与为构造函数提供默认值(一)

概述 本文主要说明: 辅助构造器 与 为构造函数提供默认值 的使用 辅助构造器为构造函数提供默认值 相关链接 阅读之前&#xff0c;可以浏览一下 scala相关文章 辅助构造器 可以通过定义名为this的方法来定义辅助Scala类构造函数。只有几个规则需要了解&#xff1a; 每个辅助…

算法笔记【7】-直接插入排序算法

文章目录 一、简介二、基本原理和实现步骤三、优缺点分析 一、简介 在排序算法中&#xff0c;直接插入排序是一种基本而常用的排序方法。它通过不断将待排序数组中的元素插入到已排序部分的合适位置&#xff0c;逐步构建有序数组。本文将详细介绍直接插入排序算法的原理、实现…

2024王道考研计算机组成原理——总线

6.1 总线概述 每一个外设都通过IO接口和DB、CB、AB相连 三系统总线结构&#xff1a; 桥有总线仲裁的功能&#xff0c;就是把某一总线的使用权分给哪个设备&#xff1f; 6.1.2 总线的性能指标 总线复用&#xff1a;分时传输地址&数据 6.2 总线仲裁 通过控制总线来发送使…

《RT-DETR改进实战》专栏介绍 专栏目录

《RT-DETR改进实战专栏》介绍及目录 介绍&#xff1a;欢迎来到最新专栏《RT-DETR改进实战》&#xff01;这个专栏专注于基于 YOLOv8 项目的魔改版本&#xff0c;而不是百度飞桨框架中的 RT-DETR。 本专栏为想通过改进 RT-DETR 算法发表论文的同学设计。每篇文章均包含完整的改…

使用示例和应用程序全面了解高效数据管理的Golang MySQL数据库

Golang&#xff0c;也被称为Go&#xff0c;已经成为构建强大高性能应用程序的首选语言。在处理MySQL数据库时&#xff0c;Golang提供了一系列强大的库&#xff0c;简化了数据库交互并提高了效率。在本文中&#xff0c;我们将深入探讨一些最流行的Golang MySQL数据库库&#xff…

ImportError: DLL load failed while importing _pyopenvino: 找不到指定的程序

ImportError: DLL load failed while importing _pyopenvino: 找不到指定的程序 完全按照官方的pip安装方式&#xff0c;但是报错 解决方法&#xff1a; 下载下面压缩包 官网下载链接 解压到 运行程序之前 完成&#xff01;&#xff01;&#xff01; 测试 python -c &quo…

思维模型 纳什均衡

本系列文章 主要是 分享 思维模型&#xff0c;涉及各个领域&#xff0c;重在提升认知。纳什均衡解释了囚徒困境、智猪博弈、内卷、美苏的军备竞赛等博弈问题。 1 纳什均衡的应用 1.1 经典的 囚徒困境 1 背景 囚徒困境是一个经典的博弈论问题&#xff0c;主要描述了两个被捕的…

第十三章 枚举与泛型

13.1枚举类型 13.1.1 使用枚举类型设置常 设置常量时&#xff0c;通常将常量放置在接口中&#xff0c;这样在程序中直接使用。该常量不能被修改&#xff0c;因为在接口定义常量时&#xff0c;该常量的修饰符为final与static。常规定义常量的代码如下&#xff1a; public int…

Stable Diffusion系列(一):古早显卡上最新版 WebUI 安装及简单操作

文章目录 Stable Diffusion安装AnimateDiff插件适配sdxl模型适配 Stable Diffusion使用插件安装界面设置基础文生图加入lora的文生图 Stable Diffusion安装 我的情况比较特殊&#xff0c;显卡版本太老&#xff0c;最高也就支持cuda10.2&#xff0c;因此只能安装pytorch1.12.1&…

05、SpringCloud -- 秒杀按钮、秒杀请求流程(各种请求到后台的判断、减库存、下单数据和次数保存)

目录 秒杀按钮代码实现:vue的JS实现:秒杀请求需求:代码前端后端Seckill-apidomainSeckill-serverWebConfig1、秒杀请求判断controller2、重复下单判断MapperService 接口Impl 实现类controller3、库存判断4、秒杀涉及到的操作_01、减库存_02、创建订单对象并保存_03、用户下…

mycat2.X读写分离

一、数据库中间件介绍 二、下载安装包 2.1下载地址: 下载两个一个是mycat程序,一个是mycat的驱动 http://dl.mycat.org.cn/2.0/install-template/mycat2-install-template-1.20.zip http://dl.mycat.org.cn/2.0/1.21-release/mycat2-1.21-release-jar-with-dependencies-2…

电子邮件钓鱼攻击的防范:如何识别并避免网络诈骗

在数字化的今天&#xff0c;电子邮件成为我们日常工作和生活中不可或缺的通讯工具。然而&#xff0c;电子邮件钓鱼攻击也随之成为网络诈骗的常见手法。通过识别和避免电子邮件钓鱼攻击&#xff0c;我们可以在很大程度上保护自己的网络安全。本文将为您提供一些实用的识别和防范…

nacos在linux中的安装、集群的配置、mysql生产配置

1.下载和安装 官方下载地址&#xff1a;https://github.com/alibaba/nacos/releases&#xff0c;根据自己需要的本版去下载就行 下载的是 .tar.gz 后缀的文件是linux版本的 使用tar命令解压&#xff0c;完成之后是一个nacos的文件夹 和windows下的文件夹目录是一样的 要启…

小黑子—spring:第三章 AOP开发

spring入门3.0 三 小黑子的springAOP开发1. AOP简介1.1 AOP的概念1.2 AOP思想的实现方案1.3 模拟AOP思想实现的基础代码1.4 AOP的相关概念 2. 基于xml配置的AOP2.1 XML方式AOP快速入门2.2 XML方式AOP配置详解2.3 xml方式AOP的原理解析2.3.1 AOP底层两种生成Proxy的方式 3. 基于…

一个老旧优盘从2M变回8G的逆袭之路

前言 最近收拾资料&#xff0c;发现了一个比较老的优盘&#xff0c;上面标记8G内存&#xff0c;就好奇里边存了点啥。用电脑打开&#xff0c;啥内容都没有&#xff0c;结果大小还显示2M&#xff1f;&#xff1f;&#xff1f;看看今天能不能救活吧。 正文 步骤一、清空磁盘 …

并发安全问题之超卖问题

并发安全问题之超卖问题 乐观锁总结&#xff1a; 优点&#xff1a;不加锁性能好。 缺点&#xff1a;同时请求成功率低&#xff08;即只要发现数据变了就放弃了&#xff09;。 乐观锁思想的具体体现&#xff1a;一共两步&#xff0c;第一步&#xff0c;先查询状态。第二步&…

Java VMTranslator Part I

目录 堆栈运算命令 基本思路 核心代码 Parser Code Writer Main 实验结果&#xff0c;使用SimpleAdd、StackTest进行验证 内存访问命令 基本思路 核心代码 Parser Code Writer Main 实验结果&#xff0c;使用进行验证。对比生成的二进制代码文件。 用Java写一个翻…