1 CPU寻址内存,虚拟地址、物理地址
(1)寻址内存:
CPU访问外设,有两种类型,一个是内存空间,一个是IO空间;
IO空间,X86通过in/out指令访问外设,IO空间只存在X86架构,在RISC架构不存在;
内存空间,CPU通过指针访问所有内存空间,内存空间分为两类,普通内存和位于内存空间的寄存器。其他设备的寄存器,比如通过I2C总线访问触摸屏的寄存器,与CPU内存空间无关。
(2) MMU原理
对于一个支持MMU的CPU,只要开启MMU,CPU跟程序员视角一致,看到的永远是虚拟地址;
在访问内存空间时,CPU发一个虚拟地址(指针),MMU把虚拟地址映射为物理地址。
其他硬件设备,比如DMA/IVE(硬件模块不带mmu)访问也是物理地址;
(3)虚拟地址和物理地址:
比如在两个不同的进程QQ/WECHAT里,可以分别定义一个变量,虚拟地址相同(都为1G);真正访问的时候,经过MMU映射到实际不同的物理地址。
每个进程都维护自己的页表,相同的虚拟地址可以映射到不同的物理地址;
当CPU访问0x1234560时,先查询页地址0x1234对应的物理地址(比如1M),560为页内偏移
patch
int *p=1m; //对指针赋值物理地址,是错误的
(4) 物理地址的本质
在Linux 内核,物理地址定义为一个无符号64/32位整数;
32处理器虚拟地址最大4G,物理地址不一定32位,可以大于4G;
物理地址位数可以大于虚拟地址位数;
2.MMU以及RWX权限、kernel和user模式权限
32位处理器,页表有32位,但实际维护页表只用到高20位,低12位用来存放其他信息比如权限
CPU访问一个地址,不仅要做虚拟/物理地址页表地址查询,还要检查页表权限,出现非法操作时,MMU拦截CPU访问请求,并报page
fault,CPU收到MMUpage
fault中断时,报段错误,发信号11,进程默认信号处理方式是挂掉。
找不到物理地址,或者权限不对,发生段错误;
tips:
gdb layout src
meltdown漏洞,利用时间旁路攻击原理,从用户空间获取内核空间数据;
3.MMU作用总结:
(1). 内存隔离:防止应用程序访问不属于自己的空间,隔离应用对其它应用空间、内核空间的访问。因为它们只能访问自己的虚拟空间,每个进程的虚拟空间都是独立的。
(2). 权限管理:不同地址段拥有不同的访问权限,不能越权访问。
(3). 地址映射:将不连续的物理地址映射为连续的虚拟地址,并可以通过swap机制可以获得比实际内存大得多的使用空间。
前两者实现数据安全,第三条为程序提供空间便利。
该部分由网友Aero Learning补充, 感谢。
再补充两点:
1.直接使用物理内存,当即将运行的程序内存不足时,需要选择一个进程整体换出,这导致有大量数据的换出与换入,效率低下;即MMU可以解决内存使用效率问题;
2.有效解决碎片化问题;
3.内存的zone: DMA、Normal和HIGHMEM
32位Linux内核空间物理地址映射在3G~4G,分三个区域,
ZONE_DMA(0~16M):DMA内存分配区;
ZONE_NORMAL(16MB~896MB): 普通映射的内存区域;
ZONE_HIGHMEM(896MB~):高端内存区域,其中的页不能永久映射到内核地址空间;内核一般不使用,如果要使用,通过kmap做动态映射;
存在高端内存的原因是,当物理内存大于虚拟内存寻址范围(1G)时,需要做非线性映射,才能在访问所有物理内存空间范围。32系统一般都划分896M以上物理地址为高端内存。
在目前流行的64位CPU中,由于虚拟寻址位数足够,就不存在高端内存概念了。
896M以下,开机直接映射好(通过virt_to_phys/phys_to_virt简单线性映射),896MB以上动态分配,可以被内核和用户空间访问;
对于64位CPU有足够寻址能力,就不存在869MB问题了,64位处理器,一般没有HGIHMEM ZONE;;
对于一个嵌入式设备,内存太小,也不存在 HIGHMEM区域;
DMAzone存在的根本原因是DMA硬件有缺陷,比如一个典型32位系统ISA设备的DMA只有24位寻址能力,也就是只能访问内存的前16M内存,而整个系统的最大物理内存寻址甚至可以大于1G,超过内核的虚拟空间;
DMA没有MMU,所以只能访问连续物理内存;少数最新的DMA具有MMU,也可以访问物理不连续的内存,MMU页表映射即可。
当为一个DMA有缺陷设备申请内存时,传入标记GFP_DMA,在DMA
ZONE区域分配内存,如果DMA可以访问所有内存空间,则不用受限于DMA_ZONE。而其他普通设备,也可以申请DMA_ZONE.
申请DMA内存,会填入设备访问范围,根据范围确定申请的内存区域;
DMA内存不一定来自于DMA ZONE,DMA ZONE也不一定用于DMA
如果系统所有DMA都没有缺陷,则不存在dma_zone;
DMA直接访问内存,不会让外设访问速度更快,DMA最大好处是解放CPU资源,速度受限外设总线及访问频率;
4.Linux内存管理Buddy算法
Buddy 算法直面物理内存;
Buddy算法把内存中的所有页面按照2的幂次进行分块管理,分配的时候如果没有相应大小块,就把大的块二分成小块;释放的时候,回收的块跟相邻的空闲伙伴块又能合并成大块;
BUDDY页面的分配和使用情况可以通过proc接口/proc/buddyinfo来查看;
Buddy有个缺陷,会造成许多内存碎片,比如总和还剩余很大,但是没有足够连续的空余内存可用。
早期采用预留分配方法,给予显卡,摄像头等预留内存,即使设备不使用,也会预留。
5.连续内存分配器(CMA)
分配专门的CMA区域,用于DMA设备的内存申请,比如摄像头,
当摄像头设备不用时,该区域可以用于其他内存分配;
当使用摄像头时,将该区域内的所有已申请内存,拷贝到其他分散的页,并且修改对应页表。
这样CMA大块连续内存,就不会被浪费掉,也保持了大块连续内存的访问需求;
把不同的DMA设备,放在不同的CMA,就不会导致内存碎片化;