Linux内存管理
Linux内存管理
物理内存
三个级别
- Page 大小为4k,内存的基本单位
- Zone 管理Page的队列
- ZONE_DMA 内核专用,存放DMA(直接存储器访问)读取IO设备的数据
- ZONE_NORMAL 内核专用,存放内核的相关数据
- ZONE_HIGHMEM 高端内存 用户进程存放数据
- Node节点 一个CPU对应一个Node
内存分配
-
大内存 利用伙伴系统分配
将ZONE中的page分组,组装为链表,链表存放页块的集合
-
**小内存 **slub分配
将几个页单独拎出来作为缓存,里面维护了链表,每次在链表中获取对应的内存,用完后不清空
内存映射
内存映射:将虚拟内存地址映射到物理内存地址,为了完成内存映射,内核为每个进程维护了一张页表(存储在CPU的内存管理单元MMU中),记录虚拟地址与物理地址的映射关系。
linux给每个进程提供一个独立的虚拟地址空间,并且是连续的,进程就可以方便的访问内存(虚拟内存)
虚拟地址空间=[内核空间+用户空间]
32位系统:[用户空间3G(0x0-0xc0000000)+内核空间1G(0xfffffff-0xc0000000)]3:1
64位系统:[用户空间128T(0x0-0x00007FFFFFFFF000)+未定义空间+内核空间128T(0xFFFF900000000000-0xFFFFFFFFFFFFFFFF)]
用户态:Text(代码)+Data(数据),BSS(全局变量)+堆+栈+mmap内存映射区的的内存虚拟地址范围
内核态:
-
直接映射区 逻辑地址-offset=物理地址,映射到ZONE_DMA和ZONE_NORMAL
-
动态映射区
所有的内存分配都由内核程序分配,用户无权限 ,所以内核空间需要映射到所有物理内存地址
采用直接映射,1G只能映射1G
动态映射:内核空间的逻辑值映射到物理内存中的ZONE_HIGHMEM的任何一个地址,使用完之后再映射其他物理内存
- 动态内存映射 使用完后映射其他物理内存
- 永久内存映射 一个地址只能只能映射一个物理地址
- 固定内存映射 被特定的函数调用引用地址
区别:
- 直接映射的规则是定义好的,一个逻辑读地址对应的物理地址是固定的
- 动态映射是动态绑定的,通过页表查询
只有实际使用的虚拟地址才会分配内存 128+128
内存调用,只有在首次访问才会分配,此时进程访问的虚拟地址在MMU存储的页表中查询不到,系统产生缺页异常,进入内核空间分配物理内存,更新进程页表,再返回用户空间,恢复进程的运行
页表
MMU规定的内存映射最小单位(4KB)
-
多级页表(64位)
内存分成区块管理,映射关系->区块索引和区块内的偏移
linux五级页表[全局页目录(PGD)+上层页目录(PUD)+中间页目录(PMD)+直接页表(PTE)+页(Offset)]
-
大页
比普通页更大的内存块,常用在大量内存的进程上
虚拟内存空间分布
[0x0-只读段+数据段+堆+文件映射+栈-0xC0000000](用户态)+内核空间[0xc0000000-0xFFFFFFFF](内核态)
堆和文件映射是动态分配的(malloc函数)
内存回收
-
回收缓存(LRU算法-回收最近最少使用的内存页面)
-
回收不常访问的内存,通过交换分区写到磁盘中
交换分区(swap分区,虚拟内存):把一块磁盘分区当成内存,暂时不用的数据放到磁盘,使用时从磁盘取到内存中
-
杀死进程(OOM-out of memory)
监控内存使用情况,通过oom_score为每个进程内存使用情况打分,可以手动设置oom_adj,调整oom_score
内存工具
free 查看内存使用情况
top 查看进程内存使用情况(排序)
Buffer Cache
- buffers (对磁盘数据的缓存)对原始磁盘块的临时存储,缓存磁盘数据,把分散的写入集中起来,统一优化磁盘的写入,将多次小的写入合并为大的写入
- Cached (对磁盘文件的缓存)读文件的页缓存,缓存从文件读取的数据,下次访问直接从内存中返回
普通进程只能看到内存提供的虚拟内存
进程通过malloc申请内存后,不会立即分配,在首次访问时才通过缺页异常分配
TLB
缓存虚拟地址和物理地址的对应关系,查询物理地址的时候先查TLB,找不到再查页表
虚拟空间和物理内存都分为内核空间和用户空间
虚拟地址需要通过页表转为物理地址才能使用
用户虚拟空间只能映射物理内存中的用户内存,无法映射内核内存,用户进程只能操作用户内存
内核空间只能被内核使用
用户进程调用系统调用的时候,对应的代码和数据保存在内核空间中
调用内核空间读取文件的时候,首先会拷贝到内存空间,再将数据从内核空间拷贝到用户空间,用户进程不能访问内核空间。