Linux内存管理
Linux内存管理
1、页
内核将物理页作为内存管理的基本单位。从虚拟内存的角度看,页是最小的单位。在不同的计算机体系结构中,页的大小不尽相同。32位系统支持4KB的页,而64位系统则会支持8KB的页。
内核用struct page
结构来表示系统中的每个物理页。其中包括了物理页的引用计数、物理页的状态,如是否为脏页,是否被锁定等。其中virtual
表示页的虚拟地址。通常情况下它就是页在虚拟内存中的地址。page
结构与物理页相关,而并非与虚拟页相关。内核仅仅用这个结构来描述此刻在相关的物理页中存放的东西。这种结构的目的在于描述物理内存本身而非其中的数据。
2、区
由于硬件的限制,内核将页划分为不同的区。内核使用区对具有相似性的页进行分组。Linux必须处理如下两种由于硬件存在缺陷而引起的内存寻址问题。1)一些硬件只能用某些特定的内存地址来执行DMA
;2)一些体系结构的内存的物理寻址范围比虚拟寻址范围大得多,这样就有一些内存不能永久映射到内核空间上。Linux只要使用了如下四种区:1)ZONE_DMA
:这个区包含的页能用来执行DMA
操作。2)ZONE_DMA32
:和ZONE_DMA
不同之处在于,这些页面能够被32位设备访问。在某些体系机构中,该区将比ZONE_DMA
更大。3)ZONE_NORMAL
:这个区包含的都是能够正常映射的页。4)ZONE_HIGHMEM
:这个区包含“高端内存”,其中的页并不能永久映射到内核地址空间。
内核对于页不同区的划分没有任何物理意义,只是内核为了管理页而采用的一种逻辑分组。某些分配可能需要从特定的区中获得页,而另外一些分配则可以从多个区中获得页,但不能同时从两个区分配,因为分配是不能跨区界限的。不是所有的体系结构都定义了全部区,如x86-64
体系结构中没有ZONE_HIGHMEM
区,所有物理内存都处于ZONE_DMA
和ZONE_NORMAL
。
3、slab层
在系统中为了便于数据的频繁分配和回收多使用空闲链表,空闲链表包含可供使用的、已经分配好的数据结构块。当代码需要使用一个新的数据结构实例时,就可以从空闲链表中抓取一个,而不需要分配内存,再把数据放进去。当不再需要这个数据结构实例时,就将它放回到空闲链表,而不是释放。在内核中,空闲链表一个主要问题是不能全局控制。当可用内存变得紧缺时,内核无法通知每个空闲链表让其收缩缓存大小以便释放一些内存。实际上,内核根本不知道存在任何空闲链表。
slab层将不同的对象划分为所谓高速缓存组,让每个高速缓存组都存放不同类型的对象。每种对象类型对应一个高速缓存。之后,这些高速缓存又被划分为slab,slab由一个或者多个物理上连续的页组成。每个高速缓存可以由多个slab组成。每个slab都包含一些对象成员,处于三种状态之一:满、部分满或者空。当内核的某一部分需要一个新的对象时,先从部分满的slab进行分配,如果没有部分满的slab,就从空的slab中进行分配。如果没有空的slab,就要创建一个slab。
slab层的管理是在每个高速缓存的基础上,通过提供给整个内核一个简单的接口来完成的。通过接口就可以创建和撤销新的高速缓存,并在高速缓存内分配和释放对象。
4、分配函数的选择
如果需要连续的物理页,就可以使用某个低级页分配器或者kmalloc()
。这是内核中内存分配的常用方式。传递的常用标志是GFP_ATOMIC
和GFP_KERNEL
。分别表示进行不进行睡眠的高优先级分配和可以睡眠的分配。
如果需要从高端内存进行分配,就使用alloc_pages()
,函数返回一个指向struct page
结构的指针,而不是一个指向某个逻辑地址的指针。因为高端内存可能并没有被映射。为了获得真正的指针,应该调用kmap()
,把高端内存映射到内核的逻辑地址空间。
如果不需要物理上连续的页,仅需要虚拟地址上连续的页,就使用vmalloc()
。函数分配的内存虚拟地址连续,但本身并不保证物理上的连续。
如果要创建和撤销很多大的数据结构,考虑建立slab告诉缓存。会极大提高对象分配和回收的性能。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具