内存管理 —— 地址转译

本文介绍了x86架构下的内存管理方式,包括段式和页式管理,并详细解析了从逻辑地址到虚拟地址再到物理地址的转换过程。通过具体实例展示了如何使用调试工具Windbg进行地址转换。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 内存管理方式

    (1)段式内存管理

    (2)页式内存管理

    



2. 逻辑地址到虚拟地址的转换

    (1)段寄存器(cs/ds/ss/es/fs/gs)的值为段选择器(Segment Selector),x86/x64段寄存器大小均为 16 位0:

     


    (2)根据选择器(Segment Selector)的 Table Indicator 位,在 GDT 或者 LDT 中查找段描述符(Segment Descriptor):

    


    (3)根据段描述符Segment Descriptor的具体结构,得出段基址:

        【x86 段描述符结构】:(注意 x64 的段描述符结构不一样

                


    (4)【例子】x86 下获取 fs:0 代表的虚拟地址:

2: kd> r fs  
fs=00000030  
  
2: kd> .formats 30  
Evaluate expression:  
  Binary:  00000000 00000000 00000000 00110000  
  分布 index->6 TI->0 RPL->0   
  
2: kd> r gdtr
gdtr=8c708c20

2: kd> dq 8c708c20 + (0x6 * 0x8) L1
8c708c50  8c409370`00003748

fs:0 -> 0x8c700000

2: kd> dt _kpcr 0x8c700000
nt!_KPCR
   ...
   +0x01c SelfPcr          : 0x8c700000 _KPCR
   +0x020 Prcb             : 0x8c700120 _KPRCB
   ...



3. 虚拟地址到物理地址的转换

    (1)用 windbg 的 !process 查看进程信息的时候会有一个 DirBase 的项目,其实它就是 进程(KProcess)结构中的一个域 DirectoryTableBase,指向进程顶层 PDE(Page Directory Entry)。且与当前进程的 cr3 寄存器的值相同。


        一、首先看看 MiInitMachineDependent 这个函数,这个函数的一部分功能就是建立虚拟内存机制。里面对 KProcess.DirectoryTableBase 的操作是:

CurrentProcess->Pcb.DirectoryTableBase[0] = DirBase;


        二、其次再看看 SwapContext 这个函数,这个函数是系统进行线程上下文切换的函数。其中就把 Pcb.DirectoryTableBase 赋值给了 cr3。

;
; Load the new CR3 and as a side effect flush non-global TB entries.
;

        mov     eax, [ebp]+PrDirectoryTableBase ; get new directory base
        mov     cr3, eax                ; and flush TB


    (2)PDE/PTE、物理地址、PFN 之间的关系 

            物理地址 = PFN + 物理页面内偏移(12bit)

            PDE/PTE = 其他部分 + PFN + PDE/PTE 标记(12bit)


            


    (3)虚拟地址的分布

            

     

    (4)【例子】(x86 PAE):修改虚拟机中 calc.exe 虚拟地址为 0x8F9768 对应的物理地址内存单元

     

!process 0 0
PROCESS 88a64788  SessionId: 1  Cid: 0908    Peb: 7ffd9000  ParentCid: 08f8
    DirBase: 11e15460  ObjectTable: 9478afc0  HandleCount:  38.
    Image: calc.exe

1: kd> .formats 0x8F9768
Evaluate expression:
  Binary:  00000000 10001111 10010111 01101000 
  分布 2->0x0, 9->0x4, 9->0xF9, 12->0x768 

1: kd> !dq 11e15460  
#11e15460 00000000`06876801 00000000`068b8801

1: kd> !dq 06876000 + 0x8 * 0x4 
# 6876020 00000000`06e6c867 00000000`00000000

1: kd> !dq 06e6c000 + 0x8 * 0xF9
# 6e6c7c8 00000000`10b0a005 00000000`0bb82005

1: kd> !db 10b0a000 + 0x768
#10b0a768 e8 4b fd ff ff 6a 58 68-e0 98 8f 00 e8 ab ea

1: kd> !eb 10b0a768 cc


<think>嗯,用户这次转向了一个相当专业的系统级问题——Linux中SMMU预留内存的使用模块。从对话历史看,用户之前深入探讨过PerCPU页帧缓存机制,说明ta具备相当程度的内核知识背景,可能是嵌入式开发或驱动工程师。有趣的是,用户特意引用了四份参考资料,但其中三份([2][3][4])和SMMU关联度不高,只有[1]直接相关。这说明用户可能想确认SMMU的通用定义。需要特别注意用户截取的[1]中关键描述:“为除CPU外的DMA设备提供地址转译和保护”——这直接点出了SMMU的核心价值。用户提问的精准度值得注意:“预留内存的使用模块”而非“配置方法”,说明ta更关注具体驱动实现而非理论。结合ta上次对PerCPU缓存的兴趣,或许正在调试某个涉及DMA和内存隔离的性能问题?比如GPU或AI加速卡的场景?技术层面需要分三块回答:第一要明确SMMU自身作为硬件模块如何管理预留内存(I/O页表映射机制很关键);第二要梳理驱动模型(iommu和dma-mapping子系统如何协作);第三必须举出具体子系统案例(GPU/iommu/remoteproc),这正是用户引用的[1]中提到的设备类型。考虑到用户引用资料里出现了内存地址划分([2])和mmap([3]),虽然和SMMU不直接相关,但回答时应该隐含这些概念的关联性——比如SMMU转译的物理地址范围就受限于[2]描述的架构设计。需要特别注意术语准确性:“预留内存”在Linux中有多种实现方式(reserved-memory节点、CMA、IOMMU预留区域),得区分场景。用户没明确是否指设备树配置,所以回答要覆盖设备树和内核API两种路径。最后生成的问题要延续用户之前的风格:具体、可操作、直指实现细节。比如监控工具就是ta上次对PerCPU缓存追问的方向。</think> 关于 Linux 中 SMMU 预留内存的使用模块,核心涉及 **IOMMU 框架**和 **DMA 映射子系统**,主要模块包括 `iommu` 核心模块、设备驱动(如 GPU/NPU 驱动)及预留内存管理机制(如设备树预留)。以下是详细解析: --- ### 一、SMMU 预留内存的作用 SMMU(System Memory Management Unit)为 DMA 设备(如 GPU、NPU、网络加速卡)提供地址转译与内存保护[^1]。其预留内存的主要目的是: 1. **硬件隔离**:为特定设备预留物理连续的物理内存,避免系统内存碎片影响性能 2. **安全性**:设备只能访问预留内存范围内的物理地址 3. **性能优化**:减少转译开销,满足 DMA 设备的低延迟需求 --- ### 二、关键使用模块及实现机制 #### 1. **IOMMU 核心框架 (`drivers/iommu/`)** - **功能**: - 管理 SMMU 硬件寄存器和地址转译表(IO页表) - 实现 `iommu_map`/`iommu_unmap` API 将物理内存映射到设备 IOVA(I/O Virtual Address) - **预留内存关联**: 通过 `iommu_dma_alloc_reserved` 从预留区域分配物理内存 ```c /* 示例:为设备分配预留内存 */ void *addr = iommu_dma_alloc_reserved(dev, size, dma_handle, flags, attrs); ``` #### 2. **DMA 映射子系统 (`drivers/base/dma-mapping.c`)** - **功能**: - 提供 `dma_alloc_coherent()` 等标准接口 - 自动识别设备是否连接 SMMU,选择是否启用地址转译[^1] - **预留内存路径**: ```mermaid graph TD A[dma_alloc_coherent] --> B{设备启用SMMU?} B -->|是| C[通过IOMMU分配预留内存] B -->|否| D[从普通ZONE_DMA分配] ``` #### 3. **设备驱动模块(如 GPU/NPU 驱动)** - **实现要点**: 1. 在驱动初始化时声明需要预留的内存范围: ```c /* 示例:GPU驱动预留512MB */ static struct resource gpu_reserved_mem = { .start = 0x80000000, .end = 0x80000000 + 512*1024*1024, .flags = IORESOURCE_MEM, }; ``` 2. 通过 `of_reserved_mem_device_init()` 绑定预留内存与设备 3. 使用 `dma_map_single()` 建立设备可访问的 DMA 地址 #### 4. **设备树配置** 预留内存通常通过设备树声明: ```dts reserved-memory { #address-cells = <2>; #size-cells = <2>; gpu_reserved: region@80000000 { compatible = "shared-dma-pool"; reg = <0x0 0x80000000 0x0 0x20000000>; // 512MB起始于0x80000000 no-map; }; }; gpu: gpu@3d000000 { memory-region = <&gpu_reserved>; }; ``` --- ### 三、典型应用场景 1. **图形渲染(GPU)** 预留显存空间,避免用户态进程篡改渲染数据 2. **AI 推理(NPU)** 为权重数据预留大块连续内存,满足高速DMA传输 3. **硬件编解码器** 隔离视频帧缓冲区,防止其他设备误访问 --- ### 四、监控与调试 1. **查看预留内存分配**: ```bash cat /proc/iomem | grep reserved # 输出示例:80000000-9fffffff : gpu_reserved ``` 2. **检查 SMMU 转译状态**: ```bash dmesg | grep iommu # 输出示例:iommu: Adding device ff9a0000.gpu to group 0 ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值