目录

内核空间

       内核空间的扩展功能

硬件层面

       ARM64虚拟地址空间划分

      进程的用户虚拟地址空间

      内存布局

物理地址空间

     内存映射

内核空间

1)内核空间的基本功能
虚拟内存管理,负责从进程的虚拟地址空间分配虚拟页,
sys_brk用来扩大或收缩堆,
sys_mmap用来在内存映射区域分配虚拟页
sys_munmap用来释放虚拟页;

2)内核延迟分配物理内存的策略:进程第一次访问虚拟内存的时候,触发页错误异常
页错误异常处理程序从页分配器申请物理页,在进程的页表中把虚拟页映射到物理页。

3)页分配器负责分配物理页,当前使用的页分配器是伙伴分配器

4)内核空间把页划分成小块内存,提供分配内存的接口kmalloc和释放内存的接口kfree
支持slab slub slob

5)在内核初始化过程中,页分配器没有准备好,需要使用临时的引导内存分配器分配内存。

内核空间的扩展功能

1)不连续页分配器提供了分配内存的接口vmalloc和释放内存的接口vfree.在内存碎片化的时候,申请连续物理页的成功率很低,可以申请不连续的物理页,映射到连续的虚拟页。即虚拟地址连续而物理地址不连续。

2)每处理器内存分配用来为每处理器变量分配内存

3)连续内存分配器(Coutiguous Memory allocator ,CMA)用来给驱动程序预留一段连续的内存,当驱动程序不用的时候,可以给进程使用;当驱动程序使用的时候,
把进程占用的内存通过页回收或迁移的方式让出来,给驱动程序使用。

4)内存控制组 用来控制今年初占用的内存资源

5)内存碎片化时,找不到连续的物理页,内存碎片整理通过迁移的方式得到连续的物理页。

6)内存不足的时候,页回收负责回收物理页,对于没有后备存储设备支持的匿名页,把数据换出到交换器,然后释放物理页;
对于有后备存储支持的文件页,把数据写回存储设备,然后释放物理页。

7)如果页回收失败,内存耗尽杀手,选择进程杀掉。

硬件层面

MMU内存管理单元,负责把虚拟地址转化成物理地址
TLB页表缓存,保存最近使用的页表映射,避免每次把虚拟地址转换成物理地址都需要查询内存中的页表。
一级缓存 二级缓存,解决处理器与内存速度不匹配问题。
为了支持并行地取指令和取数据,一级缓存分为数据缓存和指令缓存。

ARM64虚拟地址空间划分

1、虚拟地址的最大宽度是48位
2、内核虚拟地址[0XFFFF 0000 0000 0000 ,OXFFFF FFFF FFFF FFFF]
3、用户虚拟地址[0x0000 0000 0000 0000 ,0X0000 FFFF FFFF FFFF]
4、内核虚拟地址和用户虚拟地址的宽度相同
5、所有进程共享内核虚拟地址空间,每个进程有独立的用户虚拟地址空间
6、同一个线程组的用户线程共享用户用户虚拟地址空间,内核线程没有用户虚拟地址空间。

进程的用户虚拟地址空间

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

为了使缓冲区溢出攻击更加困难,内核支持为内存映射区域、栈和堆随机的起始地址。进程是否使用虚拟地址空间随机化,由以下两个因素共同决定
1、进程描述符的成员personaliy是否设置ADDR_NO_RANDOMIZE
2、全局变量randomize_va_space:0表示关闭虚拟地址空间随机化;1表示使内存映射映射区域和栈的起始地址随机化;2表示内存映射区域、栈和堆起始地址随机化。

可以通过/proc/sys/kernel/randomize_va_space修改

内存布局

自顶向下增长,起始地址是STACK_TOP - 栈的最大长度 - 间隙。默认启用内存映射区域随机化,需要把起始地址减去一个随机值。

内核地址空间布局
1、线性映射区域的范围是[PAGE_OFFSET,2^64-1],起始位置是内核虚拟地址空间一半。

PAGE_OFFSET = 0xFFFFFFFFFFFFFFFF<<(VA_BIT-1)

虚拟地址=((物理地址 - PHYS_OFFSET)+ PAGE_OFFSET),其中PHYS_OFFSET是内存的物理地址

2、vmemmap区域范围[VMEMMAP_START,PAGE_OFFSET),长度是VMEMMAP_SIZE=(线性映射区域长度/页长度 * page结构体的长度上限)
3、PCI/IO区域范围是[PIC_IO_START,PCI_IO_END) 长度是16M,结束地址是PCI_IO_END=(VMEMMAP_START-2MB)
4、固定映射区域的范围[FIXADDR_START,FIXADDR_TOP),长度是FIXADDR_SIZE,结束地址是FIXADDR_TOP = (PCI_IO_START-2MB)
5、vmalloc区域范围是[VMALLOC_START,VMALLOC_END),内核镜像在此区域
6、内核模块区域范围[MODULES_VADDR,MODULES_END],长度128M
7、KASAN (动态的内存错误检查工具)影子区域的起始地址是内核虚拟地址空间的起始地址,长度是内核虚拟地址的1/8

物理地址空间

物理地址是处理器在系统总线上看到,使用精简指令集的处理器通常只实现一个物理地址空间

外围设备和物理内存使用统一的物理地址空间,有些处理器架构把分配给外围设备的物理地址区域称为设备内存。

处理器通过外围控制器的寄存器访问外围设备,分为控制寄存器、状态寄存器和数据寄存器三大类。
处理器对外围设备寄存器的编制方式有两种:
1、I/O映射方式
2、内存映射方式

程序只能通过虚拟地址访问外设寄存器,
ioremap 把外设寄存器的物理地址映射到内核虚拟地址空间
io_remap_pfn_range()把外设寄存器的物理地址映射到进程的用户虚拟地址空间

ARM64结构定义了两种内存类型
正常内存:物理内存和只读寄存器(ROM)
共享属性:
    不可共享:只能被处理器的一个核使用
    内部共享:一个处理器的所有共享或者多个处理器共享
    外部共享:处理器和其他观察者(如DMA)共享
缓存属性:用来定义访问时是否通过缓存处理器
设备内存:分配给外围设备寄存器的物理地址区域
    共享属性总是外部共享
    缓存属性总是不可缓存

内存映射

在进程的虚拟地址空间中创建一个映射
1)文件映射,把文件的一个区间映射到进程的虚拟地址空间,数据源是存储设备上的文件;通常把文件映射的物理页称为文件页。
2)匿名映射,没有文件支持,把物理内存映射到进程的虚拟地址空间,没有数据源。把匿名映射的物理页称为匿名页。

根据修改是否对其他进程可见和是否传递到底层文件,内存映射分为共享映射和私有映射
共享映射:修改数据时,映射相同区域的其他进程可以看见,如果是文件支持的映射,修改会传递到底层文件。
私有映射:第一次修改数据时会从数据源复制一个副本,然后修改副本,其他进程看不见,不影响数据源。

两个进程可以使用共享的文件映射实现共享内存,匿名映射通常是私有映射,共享的匿名映射只可能出现在父进程和子进程之间。
在进程的虚拟地址空间中,代码段数据段是私有的文件映射,未初始化数据段、堆和栈是私有的匿名映射

学习链接:

kernel学习链接

参考

https://course.0voice.com/v1/course/intro?courseId=2&agentId=0