当前位置: 首页 > news >正文

虚拟地址空间布局架构

一、内存管理架构

1.Linux内核整体架构以及子系统

内存管理子系统架构分为用户空间、内核空间及硬件部分 3 个层面:

  1. 用户空间:应用程序使用malloc()申请内存资源,通过free()释放内存资源。
  2. 内核空间:内核是操作系统的一部分,始终驻留内存。内核空间为内核保留,不允许应用程序读写该区域内容或直接调用内核代码定义的函数。
  3. 硬件:处理器包含内存管理单元(Memory Management Unit, MMU)部件,负责将虚拟地址转换为物理地址。

Linux内核只是操作系统当中的一部分,对下管理所有硬件设备;对上通过系统调用向Library Routine或者其他应用程序提供API接口。

  1. 用户空间
    相当于应用程序使用 malloc() 申请内存,通过 free() 释放内存。malloc()/free() 是 glibc 库的内存分配器 ptmalloc 提供的接口,ptmalloc 使用系统调用 brk 或 mmap 向内核以页为单位申请内存,再分成小内存块分配给对应应用程序。

  2. 内核空间

  • 虚拟内存管理:负责从进程虚拟地址空间分配虚拟页,sys_brk 扩大或收缩堆,sys_mmap 在内存映射区域分配虚拟页,sys_munmap 释放虚拟页。页分配器(伙伴分配器)负责分配物理页。
  • 扩展功能:不连续页分配器提供 vmalloc(分配内存)和 vfree(释放内存)接口。内存碎片化时,可申请不连续物理页并映射到连续虚拟页(虚拟地址连续,物理地址不连续)。
  • 内存控制:内存控制组控制进程内存资源;内存碎片化时,通过迁移整理碎片以获取连续物理页;内存不足时,页回收负责回收物理页。

        3.硬件MMU 包含页表缓存,保存最近使用的页表映射,避免虚拟地址转物理地址时频繁查询内存页表。为解决处理器与内存速度不匹配问题,增加缓存:一级缓存分数据缓存和指令缓存,二级缓存协调一级缓存与内存的工作效率。

2.系统调用视图

 【代码案例】

sbrk.c

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>

#define MAX 1024

int main(int argc,char *argv[]){
    int *p = sbrk(0);
    int *old = p;
    printf("old: %p\n",old);

    p = sbrk(MAX * MAX);
    if(p == (void *)(-1)){
        perror("sbrk error.\n");
    }

    printf("old: %p\tp = %p\n",p,old);

    int *new = sbrk(0);
    printf("new: %p\n",new);

    printf("\npid = %d\n\n",getpid());

    while(1){
        sleep(1);
    }

    sbrk(-MAX * MAX);

    return 0;
}

运行结果:

二、虚拟地址空间布局架构

        因为目前应用程序没有那么大的内存需求,所以 ARM64 处理器不支持完全的 64 位虚拟地址。

        在 ARM64 架构的 Linux 内核中,内核虚拟地址和用户虚拟地址的宽度相同。

        所有进程共享内核虚拟地址空间,每个进程有独立的用户虚拟地址空间,同一个线程组的用户线程共享用户虚拟地址空间,内核线程没有用户虚拟地址空间。

1.用户虚拟地址空间划分

        进程的用户虚拟空间起始地址是 0,长度由处理器架构定义的宏TASK_SIZE决定。以 ARM64 架构定义的TASK_SIZE为例:

  • 32 位用户空间程序TASK_SIZE取值为TASK_SIZE_32,即0x100000000(等同于 4GB)。
  • 64 位用户空间程序TASK_SIZE取值为TASK_SIZE_64,即2^VA_BITS字节。

VA_BITS 是编译内核的时候选择的虚拟地址位数。

进程的用户虚拟地址空间包含区域:

  • 代码段、数据段、未初始化数据段;
  • 动态库的代码段、数据段和未初始化数据段;
  • 存放动态生成的数据的堆;
  • 存放局部变量和实现函数调用的栈;
  • 把文件区间映射到虚拟地址空间的内存映射区域;
  • 存放在栈底部的环境变量和参数字符串。

         Linux内核使用内存描述符mm_struct,描述进程的用户虚拟地址空间,内核源码分析如下:

        一个进程的虚拟地址空间主要由两个数据结构描述:

  • mm_struct:最高层次结构,描述一个进程整个虚拟地址空间,每个进程只有一个,在进程的task_struct结构中有专门指针指向它,是对整个用户空间的描述。
  • vm_area_struct:较高层次结构,描述虚拟地址空间的一个区间(虚拟区)。

  1. 进程基本组成部分对应的 vm_area_struct 节点数量
    一个典型进程至少有几个 vm_area_struct 节点描述不同内存区域:
    1. 代码段,进程可执行代码部分通常由一个 vm_area_struct 描述,包含程序机器指令,起始地址是代码在虚拟内存的加载位置,结束地址是代码段末尾下一字节地址,且区域通常只读可执行。
    2. 数据段,已初始化全局变量和静态变量所在数据段由一个 vm_area_struct 描述,用于存储程序运行需使用的固定数据,属性可读写。
    3. BSS 段(未初始化数据段),未初始化全局变量和静态变量所在 BSS 段对应一个 vm_area_struct,程序启动时初始化为零,可读写。
    4. 堆,进程堆空间由一个或多个 vm_area_struct 描述,用于动态内存分配,随 malloc 和 free 操作,堆大小和位置变化,对应 vm_area_struct 也更新。
    5. 栈,栈区域由一个 vm_area_struct 描述,存储函数调用相关信息(如局部变量、函数参数、返回地址等),大小和位置随函数调用返回动态变化。
  2. 共享库和内存映射引入的额外 vm_area_struct 节点
    若进程使用共享库,每个共享库在虚拟内存的映射区域有一个 vm_area_struct,共享库代码和数据部分分别对应 vm_area_struct,描述其在虚拟内存的位置、大小、权限等信息。进程通过 mmap 系统调用将文件或设备内存映射到虚拟地址空间时,每个映射区域有一个 vm_area_struct。例如,映射配置文件到进程虚拟内存,该区域由 vm_area_struct 管理。
  3. 具体数量因程序而异
    简单程序(如仅含基本代码段、数据段和栈的小程序)可能只有 3 - 5 个 vm_area_struct 节点。复杂大型程序(使用大量共享库、复杂内存映射操作,如数据库程序、大型图形处理软件等)可能有几十个甚至上百个 vm_area_struct 节点,用于精细管理程序在虚拟内存空间的各种不同区域,满足复杂内存需求和功能实

2.内核地址空间布局

        ARM64处理器架构内核地址空间布局如下:

1. 线性映射区域

  • 功能:直接映射物理内存,虚拟地址与物理地址存在一一对应关系(虚拟地址连续,物理地址也连续)。
  • 作用:内核可通过该区域快速访问物理内存,适用于需要直接操作内存的场景,如内存分配与管理。

2. vmemmap 区域

  • 功能:存储内存页的元数据(如页的使用状态、引用计数等)。
  • 作用:为内核内存管理系统(如页分配器)提供数据支持,用于跟踪和管理物理内存页。

3. 2MB 间隙

  • 功能:作为隔离带,分隔不同功能区域。
  • 作用:避免相邻区域地址重叠,增强内核地址空间布局的稳定性,防止不同模块间的地址冲突。

4. PCI I/O 区域(16MB)

  • 功能:映射 PCI 设备的寄存器和内存空间。
  • 作用:内核通过该区域与 PCI 设备进行交互,实现对硬件设备的配置和数据读写。

5. 固定映射区域

  • 功能:使用固定虚拟地址映射特定物理资源(如特定硬件寄存器、高端内存等)。
  • 作用:确保内核以固定地址访问关键资源,提升访问效率,适用于对地址敏感的场景。

6. vmalloc 区域

  • 组成:包含间隙、内核镜像、内核模块区域。
    • 间隙:隔离不同子区域,保证地址空间布局的规范性。
    • 内核镜像:存放内核代码和初始化数据,是内核运行的基础。
    • 内核模块区域(128MB):动态加载内核模块(如驱动程序),扩展内核功能。
  • 作用vmalloc用于分配物理地址不连续但虚拟地址连续的内存,满足内核非连续内存的映射需求。

7. KASAN 影子区域

  • 功能:存储内存访问的 “影子数据”,记录内存区域的访问权限、状态等信息。
  • 作用:配合内核地址消毒剂(KASAN)检测内存越界、使用已释放内存等错误,辅助调试内存相关问题。

 

 https://github.com/0voice

相关文章:

  • 八、重学C++—动态多态(运行期)
  • 痉挛性斜颈康复路,饮食要点来相助
  • PyTorch 深度学习实战(34):神经架构搜索(NAS)实战
  • Linux开发工具——vim
  • AIGC8——大模型生态与开源协作:技术竞逐与普惠化浪潮
  • 【UVM学习笔记】UVM验证平台的运行—Phase以及objection
  • Redis-11.在Java中操作Redis-Spring Data Redis使用方式-环境搭建
  • spark 集群
  • Gson修仙指南:谷歌大法的佛系JSON渡劫手册
  • C++语言新特性2
  • GraalVM 24 正式发布阿里巴巴贡献重要特性 —— 支持 Java Agent 插桩
  • CExercise_05_1函数_2海伦公式求三角形面积
  • YOLO学习笔记 | 基于YOLO与光流融合的车牌识别方法研究(附Matlab代码)
  • FastAPI依赖注入:从基础概念到应用
  • python+form+opengl显示动态图形数据
  • Nacos 的AP和CP底层是怎么实现的?
  • 多layout 布局适配
  • DHCP之报文格式
  • 记录 | Android getWindow().getDecorView().setSystemUiVisibility(...)设置状态栏属性
  • ubuntu22.04LTS设置中文输入法
  • 万网 网站备案信息真实性核验单/百度一下首页登录入口
  • 山东做网站公司/市场调研的基本流程
  • wordpress登录及注册/志鸿优化设计
  • 行业网站系统/网店推广软文范例
  • 北京做网站设计/太原百度快速排名提升
  • 做高档衣服的网站/上海优化价格