虚拟地址空间布局架构


虚拟地址空间布局架构

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

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

Linux内核只是操作系统当中的一部分,对下管理系统所有硬件设备;对上通过系统调用向Library Routine 或(或者其他应用程序提供的API接口) 用户空间 malloc/free new/delete ptmalloc(glibc) jemalloc(freebsd) tcmalloc(google)

 内核空间
    sys_brk/sys_mmap/sys_munmap 进行内存管理
    虚拟内存管理
        页面错误异常处理
        页表管理
    引导内存分配器包括(页分配器)(块分配器,不连续页分配器,每处理器内存分配器,连续内存分配器)
    页回收
    内存控制组
    内存耗尽
    内存碎片整理

硬件
    物理内存
    中央处理器
        缓存
        内存管理单元
            页表缓存
  1. 用户空间 相当于应用程序使用malloc()申请内存,通过free()释放内存,通过free()释放内存,malloc()/free()是glibc库的内存分配器ptmalloc提供的接口,ptmalloc 使用的系统调用brk或mmap向内核以页为单位申请内存,然后进行分成很小内存块分配给对应应用程序。

  2. 内核空间

  3. 虚拟内存管理 负责从进程的虚拟地址空间分配虚拟页,sys_brk 来扩大或收缩堆,sys_mmap用来在内存映射区域分配虚拟页,sys_munmap用来释放虚拟页。页分配器负责分配物理页,使用分配器是伙伴分配器。

  4. 内核空间扩展功能,不连续页分配器提供分配内存的接口vmalloc 和释放内存接口vfree ,在内存碎片化的时候,申请连续物理页的成功率比较低,可以申请不连续的物理页,映射到连续的虚拟页,即虚拟地址连续而物理地址不连续。
  5. 内存控制组用来控制进程占用的内存资源,当内存碎片化的时候,找不到连续的物理页,内存碎片整理通过迁移方式得到连续的物理页,在内存不足的时候,页回收负责回收物理页

  6. 硬件 内存管理单元 MMU包含一个页表缓存,保存最近使用过的页表映射,避免每次把虚拟地址转换为物理地址都需要查询内存中的页表。解决处理器执行速度和内存速度不匹配,中间增加一个缓存,一级缓存分为数据缓存和指令缓存。二级作用协调一级缓存和内存之间的工作效率。

系统调用(system call)

用户应用程序 用户调用malloc函数进行内存分配 调用 内核与用户层接口 sbrk()/brk()或mmap()/munmap() 调用 kernel layer 中kmalloc/vmalloc

C++ 语言当中,new/delete他们底层的实现也是malloc/free 实现的,我们在编程过程中使用malloc()/free() 与内核之间的接口(桥梁)就是sbrk()等系统函数...

内存管理详解流程

  1. 用户层内存管理 STL(内存自动分配自动回收) C语言(malloc()/free()) C++语言(new/delete)
  2. 系统函数sbrk()/brk()此函数既可以分配也可以收回。
  3. mmap()分配/munmap()收回释放

内核层内存管理 kmalloc()/vmalloc() get_free_page()

虚拟地址空间布局架构

  1. 目前应用程序没有那么大的内存需求,所以ARM64处理器不支持完全的64位虚拟地址。
  2. 在ARM64架构的Linux内核中,内核虚拟地址和用户虚拟地址的宽度相同
  3. 所有进程共享内核虚拟地址空间,每个进程有独立的用户虚拟空间,同一个线程组的用户线程共享用户虚拟地址空间,内核线程没有用户虚拟地址空间 内核地址空间:0xffff ffff ffff ffff - 0xffff 0000 0000 0000 不规范地址空间(不允许使用) 用户地址空间:0x0000 ffff ffff ffff - 0x0000 0000 0000 0000 ARM 64 内核/用户虚拟地址空间划分

用户虚拟地址空间划分

  1. 进程的用户虚拟空间的起始地址为0,长度是TASK_SIZE,由每种处理器架构定义自己的宏TASK_SIZE.ARM64 架构定义的宏TASK_SIZE如下: 32位用户空间程序:TASK_SIZE的值是TASK_SIZE_32,即 0x1000000000 等4GB。 64位用户空间程序:TASK_SIZE的值是TASK_SIZE_64,即(UL(1)<<VA_BITS)字节。
//用户虚拟地址空间Linux内核源码
#ifdef CONFIG_COMPAT
#define TASK_SIZE_32 UL(0x100000000)
  1. VB_BITS 是编译内核的时候选择的虚拟地址位数。 进程的用户虚拟地址空间包含区域: 代码段,数据段,未初始化数据段, 动态库的代码段,数据段和未初始化数据段。 存放动态生成的数据的堆 存放局部变量和实现函数调用的栈。 把文件区间映射到虚拟地址空间的内存映射区域; 存放在栈底部的环境变量和参数字符串。

内核使用内存描述符mm_struct ,描述进程的用户虚拟地址空间

struct mm_struct {
    struct vm_area_struct *mmap;//虚拟内存区域链表
    struct rb_root mm_rb;//虚拟内存区域红黑树
    u32 vmacache_seqnum;
#ifdef CONFIG_MMU //在内存映射区域找到一个没有映射的区域
    unsigned long(*get_unmapped_area) (struct file *filp,
                    unsigned long addr, unsigned long len,
                    unsigned long pgoff,unsigned long flags);
#endif
    unsigned long mmap_base; //内存映射区域的起始地址
    unsigned long mmap_legacy_base;
#ifdef CONFIG_HAVE_ARCH_COMPAT_MMAP_BASES
    unsigned long mmap_compat_base;
    unsigned long mmap_compat_legacy_base;
#endif
    unsigned long task_size;// 用户虚拟地址空间的长度
    unsigned long highest vm_end; 
    pgd_t* pgd; //指向页全局目录,即第一级页表

    atomic_t mm_users;//共享一个用户虚拟地址空间的进程的数量,也就是线程组包含的进程的数量。
    atomic_long_t nr_ptes;
#if CONFIG_PGTABLE_LEVELS  >2 
    atomic_long_t nr_pmds;
#endif
    int map_count;
    spinlock_t page_table_lock;
    struct rw_semaphore mmap_sem;
    struct list_head mmlist;
    unsigned long hiwater_rss; // 进程所拥有的最大叶框数
    unsigned long hiwater_vm; //进程线性区中最大页数
    unsigned long total_vm; //进程地址空间的大小(页数)
    unsigned long locked_vm; //锁住而不能换出的页的个数
    unsigned long pinned_vm; 
    unsigned long data_vm;
    unsigned long exec_vm;
    unsigned long stack_vm;
    unsigned long def_flags;
    //代码段的起始地址和结束地址,数据段的起始地址和结束地址
    unsigned long start_code ,end_code,start_data,end_data;
    //堆的起始地址和结束地址,栈的起始地址
    unsigned long start_brk,brk,start_stack;
    //参数字符串的起始地址和结束地址,环境变量的起始地址和结束地址
    unsigned long arg_start,arg_end,env_start,env_end;
    unsigned long saved_auxv[AT_VECTOR_SIZE];
    struct mm_rss_stat rss_stat;
    struct linux_binfmt *binfmt;
    cpumask_var_t cpu_vm_mask_var;
    mm_context_t context;//处理器架构特定的内存管理上下文
}

一个进程的虚拟地址空间主要是由两个数据结构进行描述,一个是最高层次的mm_struct,较高层次vm_area_struct。 最高层次mm_struct结构描述一个进程整个虚拟地址空间。较高层次结构描述虚拟地址空间的一个区间(成为虚拟区)。 每个进程只有一个mm_struct结构,在每个进程的task_struct结构中,有一个专门用来指向该进程的结构,mm_struct 结构是对整个用户空间的描述。

task_struct :struct mm_struct *mm; struct mm_struct *active_mm;
mm_struct: struct mm_struct *vm_mm;
vm_area_struct: struct vm_area_struct *vm_next,*vm_prev; //分别vma链表的前后成员链接操作 struct rb_node vm_rb;//如果采用链表组织化,会影响到它搜索速度问题,解决问题采用红黑树。每个进程结构体mm_struct中都创建一个红黑树,将VMA作为一个节点加入到红黑树中,制样可以提升搜索速度。