另外,slab 分配器所提供的小块连续内存的分配,也是通用高速缓存实现的 。通用高速缓存所提供的对象具有几何分布的大小,范围为32到131072字节 。内核中提供了 kmalloc() 和 kfree() 两个接口分别进行内存的申请和释放 。
专用高速缓存内核为专用高速缓存的申请和释放提供了一套完整的接口,根据所传入的参数为指定的对象分配slab缓存 。
专用高速缓存的申请和释放kmem_cache_create() 用于对一个指定的对象创建高速缓存 。它从 cache_cache 普通高速缓存中为新的专有缓存分配一个高速缓存描述符,并把这个描述符插入到高速缓存描述符形成的 cache_chain 链表中 。kmem_cache_destory() 用于撤消和从 cache_chain 链表上删除高速缓存 。
slab的申请和释放slab 数据结构在内核中的定义,如下:
文章插图
slab结构体内核代码
kmem_cache_alloc() 在其参数所指定的高速缓存中分配一个slab,对应的 kmem_cache_free() 在其参数所指定的高速缓存中释放一个slab 。
虚拟内存分配前面讨论的都是对物理内存的管理,Linux 通过虚拟内存管理,欺骗了用户程序假装每个程序都有 4G 的虚拟内存寻址空间(如果这里不懂我说啥,建议回头看下 别再说你不懂Linux内存管理了,10张图给你安排的明明白白!) 。
所以我们来研究下虚拟内存的分配,这里包括用户空间虚拟内存和内核空间虚拟内存 。
注意,分配的虚拟内存还没有映射到物理内存,只有当访问申请的虚拟内存时,才会发生缺页异常,再通过上面介绍的伙伴系统和 slab 分配器申请物理内存 。
用户空间内存分配mallocmalloc 用于申请用户空间的虚拟内存,当申请小于 128KB 小内存的时,malloc使用 sbrk或brk 分配内存;当申请大于 128KB 的内存时,使用 mmap 函数申请内存;
存在问题由于 brk/sbrk/mmap 属于系统调用,如果每次申请内存都要产生系统调用开销,cpu 在用户态和内核态之间频繁切换,非常影响性能 。
而且,堆是从低地址往高地址增长,如果低地址的内存没有被释放,高地址的内存就不能被回收,容易产生内存碎片 。
解决因此,malloc采用的是内存池的实现方式,先申请一大块内存,然后将内存分成不同大小的内存块,然后用户申请内存时,直接从内存池中选择一块相近的内存块分配出去 。
文章插图
内核空间内存分配在讲内核空间内存分配之前,先来回顾一下内核地址空间 。kmalloc 和 vmalloc 分别用于分配不同映射区的虚拟内存,看这张上次画的图:
文章插图
内核空间细分区域
kmallockmalloc() 分配的虚拟地址范围在内核空间的「直接内存映射区」 。
按字节为单位虚拟内存,一般用于分配小块内存,释放内存对应于 kfree ,可以分配连续的物理内存 。函数原型在 <linux/kmalloc.h> 中声明,一般情况下在驱动程序中都是调用 kmalloc() 来给数据结构分配内存。
还记得前面说的 slab 吗?kmalloc 是基于slab 分配器的 ,同样可以用cat /proc/slabinfo 命令,查看 kmalloc 相关 slab 对象信息,下面的 kmalloc-8、kmalloc-16 等等就是基于slab分配的 kmalloc 高速缓存 。
文章插图
slabinfo-kmalloc
vmallocvmalloc 分配的虚拟地址区间,位于 vmalloc_start 与vmalloc_end 之间的「动态内存映射区」 。
一般用分配大块内存,释放内存对应于 vfree,分配的虚拟内存地址连续,物理地址上不一定连续 。函数原型在 <linux/vmalloc.h> 中声明 。一般用在为活动的交换区分配数据结构,为某些 I/O 驱动程序分配缓冲区,或为内核模块分配空间 。
下面的图总结了上述两种内核空间虚拟内存分配方式 。
文章插图
总结一下Linux内存管理是一个非常复杂的系统,本文所述只是冰山一角,从宏观角度给你展现内存管理的全貌,但一般来说,这些知识在你和面试官聊天的时候还是够用的,当然也希望大家能够通过读书了解更深层次的原理 。
推荐阅读
- 使用 UEFI 双启动 Windows 和 Linux
- 洗牌 面试遇到shuffle算法时,用这三种就够了
- 招聘|如何面试管理人员
- 一道头条面试题:如何实现 LRU 原理?
- 搞懂Android应用启动过程,再也不怕面试官了
- 让Linux系统操作更顺畅的几个便捷小工具
- 为什么给猫咪取的名字她听不懂 猫咪听得懂主人叫它名字吗
- Linux 上部署 Java 应用绕不开的命令,撒花啦
- Linux与网络设备 GRE配置经验总结
- Linux,软件安装依赖该怎么解决?