linux内核为驱动程序提供了一致的内存管理接口,因此不用考虑不同体系结构如何管理内存的。
在linux内核中分配内存用kmalloc和kfree。
kmalloc分配时可以被阻塞,且不对所获得的区域清零。它分配的区域在物理内存中也是连续的。
原型:
#include<linux/slab.h>
void *kmalloc(size_t size,int flags)//参数为分配大小及分配标志
flags参数:
GFP_KERNEL:内核内存通用分配方法,表示内存分配是由运行在内核空间的进程执行的。可休眠,所以使用GFP_KERNEL分配内存的函数必须是可重入的。
GFP_ATOMIC:用于在中断处理例程或者运行在进程上下文之外的代码中分配内存,不可休眠。内核通常会为原子性的分配预留一些空闲页面。
所有标志定义在 <linux/gfp.h>中。
size参数:
内核是基于页技术分配内存,以最佳的利用系统的RAM。
linux处理内存分配的方法是:创建一系列的内存对象池,每个池的内存大小事固定的,处理分配请求时,就直接在包含足够大的内存块中传递一个整款给请求者。内核只能分配一些预定义的固定大小的字节数组。kmalloc能处理的的最小内存块是32或者64,不大于128KB。
内存区段:
linux内核把内存分为3个区段:可用于DMA的内存,常规内存以及高端内存。kmalloc不能分配高端内存。内存区段在mm/page_alloc.c中实现。区段的初始化在对应的arch树下的mm/init.c中。
后备高速缓存 (lookaside cache)
内核中普通对象进行初始化所需的时间超过了对其进行分配和释放所需的时间,因此不应该将内存释放回一个全局的内存池,而是将内存保持为针对特定目而初始化的状态。例如,如果内存被分配给了一个互斥锁,那么只需在为互斥锁首次分配内存时执行一次互斥锁初始化函数(mutex_init)即可。后续的内存分配不需要执行这个初始化函数,因为从上次释放和调用析构之后,它已经处于所需的状态中了。
linux2.6中USB和SCSI驱动程序使用了这种高速缓存,是为一些反复使用的块增加某些特殊的内存池。后背高速缓存管理也叫slab分配器,相关函数和类型在<linux/slab.h>中申明。
slab分配器实现高速缓存具有kmem_cache_t类型。
kmem_cache_t * kmem_cache_create( const char *name, size_t size, size_t align,
unsigned long flags
void (*constructor)(void*,kmem_cache_t *, unsigned long),
void (*destructor)(void*, kmem_cache_t *, unsigned long))
用于创建一个新的高速缓存对象。
constructor用于初始化新分配的对象,destructor用于清除对象。
一旦某个对象的高速缓存被创建以后,就可以调用kmem_cache_alloc从中分配内存对象。
void * kmem_cache_alloc(kmem_cache_t *cache,int flags)
释放内存对象使用kmem_cache_free
void kmem_cache_free(kmem_cache_t *cache,const void *obj)
在内存空间都被释放后,模块被卸载前,驱动程序应当释放他的高速缓存。
int kmem_cache_destory(kmem_cache_t *cache)
要检查其返回状态,如果失败,表明莫块中发生了内存泄露。
基于slab的高速缓存scullc
kmem_cache_t *scullc_cache
scullc_cache=kmem_cache_creat("scullc",scullc_quantum,0,SLAB_HWCACHE_ALIGN,NULL,NULL)
if(!scullc_cache)
{
scullc_cleanup()
return -ENOMEM
}
if(!dpte->data[s_pos])
{
dptr->data[s_pos]=kmem_cache_alloc(scullc_cache,GFP_KERNEL)
if(!dptr->data[s_pos])
goto nomem
memset(dptr->data[s_pos],0,scullc_quantum)
}
for(i=0i<qseti++)
{
if(dptr->data[i])
kmem_cache_free(scullc_cache,dptr->data[i])
}
if(scullc_cache)
kmem_cache_destory(scullc_cache)
内存池:
内核中有些地方的内存分配是不允许失败的,为确保能分配成功,内核建立一种称为内存池的抽象,他试图始终保持空闲状态,以便紧急情况使用。
mempool_t * mempool_creat(int min_nr,
mempool_alloc_t *alloc_fn, //对象分分配 mempool_alloc_slab
mempool_free_t *free_fn, //释放 mempool_free_slab
void *pool_data)
可以用如下代码来构造内存池
cache=kmem_cache_creat(...)//创建一个高速缓存
pool=mempool_creat(MY_POOL_MINIMUM,mempool_alloc_slab,mempool_free_slab,cache)//建立内存池对象
void *mempool_alloc(mempool_t *poll,int gfp_mask)//分配对象
void *mempool_free(void *element,mempool_t *poll)//释放对象
void mempool_destroy(mempool_t *poll)//销毁内存池
注意:mempool会分配一些内存块,空闲且不会被用到,造成内存的大量浪费。所以一般情况不要用内存池。
1、内存是什么?
1) 内存又称主存,是 CPU 能直接寻址的存储空间,由半导体器件制成;
2) 内存的特点是存取速率快,断电一般不保存数据,非持久化设备;
2、内存的作用
1) 暂时存放 cpu 的运算数据
2) 硬盘等外部存储器交换的数据
3) 保障 cpu 计算机的稳定性和高性能
1、linux 内存地址空间 Linux 内存管理全貌
2、内存地址——用户态&内核态
3、内存地址——MMU 地址转换
4、内存地址——分段机制
1) 段选择符
更多Linux内核视频教程文档资料免费领取后台私信【 内核 】自行获取。
内核学习网站:
Linux内核源码/内存调优/文件系统/进程管理/设备驱动/网络协议栈-学习视频教程-腾讯课堂
2) 分段实现
5、内存地址——分页机制(32 位)
6、用户态地址空间
7、内核态地址空间
8、进程内存空间
内存管理算法 ——对讨厌自己管理内存的人来说是天赐的礼物
1、内存碎片
1) 基本原理
2) 如何避免内存碎片
2、伙伴系统算法——组织结构
1) 概念
2) 外部碎片
3、伙伴系统算法——申请和回收
1) 申请算法
2) 回收算法
3) 条件
4、如何分配 4M 以上内存?
1) 为何限制大块内存分配
2) 内核中获取 4M 以上大内存的方法
5、伙伴系统——反碎片机制
1) 不可移动页
2) 可回收页
6、slab 算法——基本原理
1) 基本概念
2) 内部碎片
7、slab 分配器的结构
详细参考:
经典|图解Linux内存性能优化核心思想
8、slab 高速缓存
1) 普通高速缓存
2) 专用高速缓存
9、内核态内存池
1) 基本原理
2) 内核 API
10、用户态内存池
1) C++ 实例
11、DMA 内存
1) 什么是 DMA
2) DMA 信号
out of memory 的时代过去了吗?no,内存再充足也不可任性使用。
1、内存的使用场景
2、用户态内存分配函数
a) 如果当前连续内存块足够 realloc 的话,只是将 p 所指向的空间扩大,并返回 p 的指针地址。这个时候 q 和 p 指向的地址是一样的
b) 如果当前连续内存块不够长度,再找一个足够长的地方,分配一块新的内存,q,并将 p 指向的内容 copy 到 q,返回 q。并将 p 所指向的内存空间删除
3、内核态内存分配函数
4、malloc 申请内存
5、缺页异常
6、用户进程访问内存分析
7、共享内存
1) 原理
2) shm 接口
1、C 内存泄露
2、C 野指针
3、C 资源访问冲突
4、STL 迭代器失效
错误示例:删除当前迭代器,迭代器会失效
正确示例:迭代器 erase 时,需保存下一个迭代器
5、C++ 11 智能指针
(1)原理分析:
(2)数据结构:
(3)使用方法:
6、C++ 11 更小更快更安全
六、 如何查看内存
可以通过 cat /proc/slabinfo 命令查看
可以通过 /proc/sys/vm/drop_caches来释放
Linux 进程通过 C 标准库中的内存分配函数 malloc 向系统申请内存,但是到真正与内核交互之间,其实还隔了一层,即内存分配管理器(memory allocator)。常见的内存分配器包括:ptmalloc(Glibc)、tcmalloc(Google)、jemalloc(FreeBSD)。MySQL 默认使用的是 glibc 的 ptmalloc 作为内存分配器。
内存分配器采用的是内存池的管理方式,处在用户程序层和内核层之间,它响应用户的分配请求,向 *** 作系统申请内存,然后将其返回给用户程序。
为了保持高效的分配,分配器通常会预先向 *** 作系统申请一块内存,当用户程序申请和释放内存的时候,分配器会将这些内存管理起来,并通过一些算法策略来判断是否将其返回给 *** 作系统。这样做的最大好处就是可以避免用户程序频繁的调用系统来进行内存分配,使用户程序在内存使用上更加高效快捷。
关于 ptmalloc 的内存分配原理,个人也不是非常了解,这里就不班门弄斧了,有兴趣的同学可以去看下华庭的《glibc 内存管理 ptmalloc 源代码分析》。
关于如何选择这三种内存分配器,网上资料大多都是推荐摒弃 glibc 原生的 ptmalloc,而改用 jemalloc 或者 tcmalloc 作为默认分配器。因为 ptmalloc 的主要问题其实是内存浪费、内存碎片、以及加锁导致的性能问题,而 jemalloc 与 tcmalloc 对于内存碎片、多线程处理优化的更好。
目前 jemalloc 应用于 Firefox、FaceBook 等,并且是 MariaDB、Redis、Tengine 默认推荐的内存分配器,而 tcmalloc 则应用于 WebKit、Chrome 等。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)