使用Rust开发操作系统(Canonical地址以及虚拟地址和物理地址操作)

本文介绍了使用Rust语言开发操作系统时涉及的地址空间概念,包括虚拟地址和物理地址的区分,以及IA-32e模式下的地址寻址。文章详细讲解了虚拟地址的不同类型,如逻辑地址、线性地址和物理地址,并讨论了IA-32e模式下的Canonical地址结构。此外,还展示了如何在Rust中定义和操作虚拟地址及物理地址的结构体,以确保地址的安全性和正确性。
摘要由CSDN通过智能技术生成


使用Rust编写操作系统(位运算)一章中我们实现了基本的位操作,在本节中我们使用之前写好的位操作开始实现地址的操作,我们先了解一下地址的理论知识

地址空间

地址空间在一般情况下分为两类:虚拟地址空间,物理地址空间,虚拟地址空间氛围逻辑地址,有效地址,线性地址.这些地址可以相互转换

虚拟地址空间

虚拟地址空间是一个抽象的地址,大多不能独立转换为物理地址,逻辑地址,有效地址,线性地址和平坦地址都属于虚拟地址的范畴

  • 逻辑地址:指应用程序角度看到的内存单元,存储单元,一个逻辑地址由两部份组成,段标识符和段内偏移量,段标识符是由一个16位长的字段组成
  • 线性地址: 线性地址是通过逻辑地址中的段基址和段偏移组合而成,使得程序无法字节访问线性地址
  • 平坦地址:是一个种特殊的线性地址,将段基址和段长度覆盖整个线性地址空间

物理地址

物理地址是真实存在硬件设备上的地址,通过处理的引脚直接或间接与外部设备,RAM,ROM相连接,在物理地址空间中除了物理内存还有硬件设备,在处理开启分页的情况下线性地址需要经过页表映射才能转为物理地址

  • I/O地址: I/O地址空间与内存地址空间相互隔离,必须IN/OUT指令才能访问,I/O地址空间由65536个可独立寻址的I/O端口组成,寻址范围为0-0xFFFF,0xF8和0xFF保留使用
  • 内存地址: 内存地址不仅只有物理内存,还有外部的硬件设备地址空间(例如之前的VGA地址)

IA-32e模式寻址

IA-32e模式的线性地址位宽64位,但是线性寻址只有48位,低48位用于线性地址寻址,高16位用于符号扩展(将第47位数值扩展到64位,全为0或全为1),这种地址称为Canonical地址,在IA-32e模式下,只有Canonical地址空间是可用地址空间,而No-Canonical空间属于无效地址空间

+---------------+<- 0xFFFFFFFF_FFFFFFFF
|               |                      
|   Canonical   |                      
|               |<-0xFFFF8000_00000000 
+---------------+<-0xFFFF7FFF_FFFFFFFF 
|               |                      
| Non-Canonical |                      
|               |<-0x00008000_00000000 
+---------------+<-0x00007FFF_FFFFFFFF 
|               |                      
|   Canonical   |                      
|               |                      
+---------------+<-0x00000000_00000000 

但采用64位Canonical地址后页管理机制也改成4级,低48位参与页表索引,高16位依旧不参与页表空间索引,页管理机制支持在4KB页面基础上增加2MB和1GB物理页

Canonical地址结构

              
| 63 - 48        |   47 - 0              |
+----------------+-----------------------+
| Sign Extension |    Liner Address      |
+----------------+-----------------------+

IA-32e段描述符

代码段描述符

结构如下

| 63-56     |55|54 |53|52 |51-48   |47|46-45|44|43 |42|41|40|39-16       |15 - 0    | 
+-----------+--+---+--+---+--------+--+-----+--+---+--+--+--+------------+----------+
|BaseAddr(H)|G |D/B|L |AVL|limit(H)|P |DPL  |S |C/D|C |R |A | BaseAddr(L)| limit(L) | 
+-----------+--+---+--+---+--------+--+-----+--+---+--+--+--+------------+----------+  
数据段描述符
|   63-56   |55|54 |53|52 | 51-48  |47|46-45 |44|43 |42|41|40| 39-16      |15-0      | 
+-----------+--+---+--+---+--------+--+------+--+---+--+--+--+------------+----------+
|BaseAddr(H)|G |D/B|L |AVL|limit(H)|P |DPL   |S |C/D|E |W |A | BaseAddr(L)| limit(L) | 
+-----------+--+---+--+---+--------+--+------+--+---+--+--+--+------------+----------+ 

有关实模式,保护模式,IA-32e模式寻址的详细的内容将在下一篇文章中讲述,在本章中我们只需要知道Canonical地址的结构即可

开始干活

好了,我们已经知道了关于在实模式,保护模式,IA-32e模式下的地址变化,我们现在开始着手编写IA-32e模式下的地址操作,我们可以构造虚拟地址和物理地址的结构VirtAddrPhyisAddr,VirtAddr用于Canonical地址,对u64类型进行包装,在使用时会检查一段地址是否属于Canonical地址,并且我们要提供一些指针算术的操作(加,减)等操作,PhyisAddrVirtualAddr操作基本一致

首先我们在system项目中创建新的模块称为ia_32e并创建子模块addr.rs,目录结构如下

system
|
|__ src
|		|
|		|__ lib.rs
|		|
|		|__ bits
|		|		|
|		|		|__ mod.rs
|		|
|		|__ ia_32e
|				|
|				|__mod.rs
|				|
|				|__addr.rs
|
|__ Cargo.toml
|
|__ .gitignore

然后在src/lib.rs文件中添加以下内容

pub mod ia_32e;

之后在src/ia_32e/mod.rs文件中添加以下内容

pub mod addr;

虚拟地址

紧接着我们在src/ia-32e/addr.rs文件中创建VirtAddrNoCanonicalAddr结构体,NoCanonicalAddr表示不属于Canonical地址,

#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
#[repr(transparent)]
pub struct VirtAddr(u64);

#[derive(Debug)]
pub struct NoCanonicalAddr(u64);

这几个宏的用法大家应该都知道了,就不再赘述

然后我们创建一个new_unchecked方法

use crate::bits::BitOpt;
impl VirtAddr{
  pub fn new_unchecked(mut addr: u64) -> VirtAddr {
    if addr.get_bit(47) {
      addr.set_bits(48..64, 0xFFFF);
    } else {
      addr.set_bits(48..64, 0);
    }
    VirtAddr(addr)
  }
}

根据之前的Canonical地址结构我们知道第47位为P表示已存在标示,如果47位被置1表示该地址在内存中存在,我们使用直线写的bits::BitOpttrait来完成位的填充操作,位填充完毕后,我们将它用VirtAddr封装以下,我们可以发现new_unchecked方法对传入的地址不加以判断便直接修改,我还要提供一个会判断的方法

 pub fn try_new(addr: u64) -> Result<VirtAddr, NoCanonicalAddr> {
   // 获取[47,64)
   match addr.get_bits(47..64) {
     // 这里47位标示内存已存在
     0 | 0x1FFFF => Ok(VirtAddr(addr)),
     1 => Ok(VirtAddr::new_unchecked(addr)),
     other => Err(NoCanonicalAddr(other)),
   }
}

在这里我们对[47,64)位进行的判断,如果传入的地址第47-63位均为0表示属于Canonical地址,相应的,第47位和第48-63位全是1也属于Canonical地址,如果传入的地址47位为1(48-63位均为0)我们需要对符号扩展做一些修改(把48-63位全部置1),其他地址均不属于Canonical地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值