linux内存管理概览
参考来源:
- 《内核设计与实现》
- 一步一图带你深入理解 Linux 物理内存管理 - bin的技术小屋 - 博客园 (cnblogs.com)
- Linux内存管理宏观篇(五)物理内存:页面分配和释放页面-阿里云开发者社区 (aliyun.com)
首先有个宏观的认识:
内存是cpu的内存,在多cpu中使用的NUMA架构,一个或多个cpu就是一个node,再将node中包含的一部份内存中区分出zone,在zone中又使用多种策略进行内存页分配。
注意和inode区分,文件系统的inode是存在固态上的,而这里的node是cpu的内存
zone
由于一些硬件的缺陷
- 有些硬件DMA必须对应特殊的地址
- 一些架构内存的物理寻址比虚拟寻址范围大得多,存在永久不能映射的地址
这样,linux将每个node又会划分出多个zone管理多个页,用来在逻辑上进行一些分组(没有任何物理意义).
- ZONE_DMA:其中的页可以使用DMA
- ZONE_DMA32:类似于ZONE_DMA,但是只有32位的设备可以访问
- ZONE_NORMAL:正常映射的页
- ZONE_HIGHEM:并不能永久的映射到内核地址的空间
- ZONE_MOVALBE:内部的页都是可以迁移的
- ZONE_DEVICE:支持热插拔设备的非易失性内存
图中的ZONE分布不是物理意义上的分布
其中每个zone有可能会有自己的伙伴管理系统等方式管理着页。
页
同样先给一个页的大致概念:
每个页都会有 struct page
结构描述。并且page与物理页有关,并不是虚拟页。通过这个结构来查看是否空闲,以及所有者是谁(进程、内核数据、静态代码、高速缓存等)。
一般来说页是4k,这是经过权衡和优化的结果,页太大会导致碎片化,页太小会导致页表寻址TLB的负担加重。但其实可以使用更大的内存页,只不过4k从上世纪默认延续下来了。
可能这时候会疑惑,为什么页的所有者还有可能是数据或者代码或者缓存,而不仅仅是某个进程。其实每个页在分配后,会由伙伴系统或者slab或者其他的方式统一管理,这时候这些页就存着不同的内容,而不仅仅是某个进程独有,有可能是每个进程的pcb在某一页中被slab统一的管理
为了后续更好的理解,这里先大致了解一下每个进程所占用的空间以及对应的内存管理的关系。
对于每个进程,其虚拟地址都是通过MMU向物理地址转换,如果发现进程所使用的物理地址不够了,那么就会触发 page fault ,从而向内核申请出物理页,然后做好映射后放回到MMU中,供进程使用。所申请的物理页可以被伙伴算法或者其他的算法管理,总之就是由内核做统一的管理。
内存分配管理方法
伙伴算法
处理页外碎片(大碎片), 每个页是4k,将页面数目凑成(4k 8k 16k...4M)大页分别用链表链接存放, 这样如果有页外碎片(连续n个页),就可以将这些碎片合成某一个大页然后放入对应链表等待分配
slab机制
处理内部碎片(小碎片), 基于高速缓存中字节缓存池(快 频繁小空间), 可以将小的空间优先塞到已经分配还没有填充的页中,其次是空的页中,最后如果没有空间了,就从物理页再取空间分配到缓存中,而且如果释放空间,也是会将分配了的页保留不返还回去
只有当内存紧缺或者显示的撤销高速缓存才会调用释放函数。
像pcb这种经常被使用的内存一般会用slab来做统一的管理。
分配方式
说了那么多概念上的东西,下面来具体看看内存分配的函数以及其作用。
分配页
获得页
下面这几个都是获得页
alloc_page(gfp_mask)
分配一页,返回页指针(page
)alloc_pages(gfp_mask, order)
分配2order个页,返回第一个页指针(连续分配的)__get_free_page(gfp_mask)
分配一页,返回逻辑地址指针(addr)__get_free_pages(gfp_mask, order)
分配2order个页,,返回第一个页逻辑地址指针(addr)
释放页
__free_pages
free_pages
free_page
内核完全信赖自己,如果传入错误的page或者地址,用了错误的order,那么系统就会崩溃。
以字节为单位分配
kmalloc
对于大页面会调用 alloc_page
这种函数,但是对于字节为单位的分配,就会使用 kmalloc
这种函数来分配。
函数原型kmalloc(size_t, gfp_t)
返回的是分配内存块的指针,并且分配的内存区在物理上是连续的。
kmalloc会向slab申请,如果内存太大会向伙伴系统申请。如果slab和伙伴系统都缺页了,会进行OOM(out of memory),释放一些内存。或者调用 alloc_page
来获得新的页,然后再去分配
对应的释放函数 kfree
vmalloc
虚拟地址连续,物理地址不一定连续。
缺点:每个页都要映射,并且有可能造成TLB抖动
对应的释放函数 vfree
slab分配
创建高速缓存,slab内部都是同一种类型
kmem_cache_create(name, size, align, flags, 构造函数)
创建高速缓存(高速缓存名字,大小,用来对齐的偏移,选项,当新页追加时触发的构造函数)kmem_cache_destory
撤销高速缓存
从创建的高速缓存中获得空间或者释放
kmem_cache_alloc(kmem_cache, gfp_t)
从高速缓存kmem_cache
中返回一个对象的指针
如果没有页,会调用
kmem_getpages
->alloc_pages
并且gfp_t会依次传递,用来获得新页
kmem_cache_free
释放
gfp_mask标志
从上面几个函数可以看到会使用到 gfp_mask
标识来只是在哪里分配。可以分为三类
- 行为标志:如何分配(是否睡眠、重新分配、启动IO等)
- 区标志:在哪个区(zone)分配
- 类型标志:(行为标志+区标志)的组合简化
对于malloc来说:常用的类型标志有
GFP_ATOMIC
不能睡眠GFP_KERNEL
睡眠+运行刷新到硬盘
几种分配的选择
gfp_mask的选择:
- 睡眠->GFP_KERNEL
- 不睡眠->GFP_ATOMIC
分配函数的选择
分配方式 | 时机 | 注意事项 |
---|---|---|
alloc_page | 需要多页 想通高端内存分配 |
返回的是页指针 高端内存大概率没有映射需要kmap |
kmalloc | 物理地址连续 比较小 |
返回的是内存体的指针 |
vmalloc | 虚拟地址连续 | 性能比kmalloc弱 TLB抖动 |
slab | 创建和撤销很多大的数据结构 高速缓存 |
并不是创建和释放而是从链表里拿|放 |
__EOF__

本文链接:https://www.cnblogs.com/alanli07/p/18173952.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程