slab 内存管理在现代C++中的应用
来源:互联网 发布:端口号怎么查看 编辑:程序博客网 时间:2024/05/01 15:52
大四一段时间曾经学习过 Linux 内核的一小部分,自然读过大名鼎鼎的 slab 内存管理;后来又看了看<Modern C++ Design>中的 Small Object Allocator, 觉得它们在原理上大致相同。这里仅将其基本原理简单介绍一下,以飨读者。
1. 先说 slab
Linux 的 slab 内存管理由两个部分组成,一是所谓的 cache, 再就是 slab;每一种数据结构对应一个 cache,举例来说,struct task_struct (这个结构非常大,就是所谓的进程控制块)对应 taskstruct_cachep, 而这种数据结构在分配内存时就只能从对应的 cache 中分配,也即,要分配一个 struct task_struct, 就只能
kmem_cache_alloc(taskstruct_cachep);总体上,这个 cache 管理着一个对象(object)池,分配内存时,直接从池中取空闲的对象,而释放时则返还给这个对象池,只有在池为空时才向系统申请内存页;所以这个对象池是非常快的。
这个内存池由一个cache管理,由多个 slab 组成,每个 slab 可以缓存多个对象。注意,slab是固定大小的而且都是一个页的大小(4K), 这是为了计算的方便,后面就会看到。cache 和 slab 的结构如图所示:
_________ _________
cachep----->| slab |------>| slab |---->......
| | | |
| | | |
| object | | object |
| object | | object |
(4K) |___ _____| (4K)|________|
一个 slab 又由两部分组成,这个4K的内存块的前面的一小部分是 slab 控制块(Linux 里面稍有不同),紧接着就是一组 object。slab控制块维护着一个指针,指向当前空闲着的对象。当一个对象被释放时,就让这个指针指向这个刚释放的对象;而在申请内存时直接将这个指针指向的空闲对象返回即可。在释放时需要找到对象对应的slab,那么如何找到它呢?直接将其指针圆整到4K就是相应 slab 控制块的首地址!
你会猜,既然每一种数据结构都需要一个 cache,那么内核中肯定有不少 cache 吧?猜对了! Linux 内核中确实有不少 cache, 而且每一种重要的数据结构都有一个 cache;然而,仔细观察发现,cache其实是以数据结构的大小为分界线的,两个数据结构,只要大小相同,就可以共用一个 cache,于是又有了 kmalloc,它管理着几个 cache,内核中没有 cache 的数据结构都从 kmalloc 中分配;为了使内存管理器更加通用,我们将要实现的内存管理器也将以大小为中心。
实现通用性是有代价的,以数据结构为中心的 cache 在释放内存时只需传入数据结构的指针,而以大小为中心的 cache 还要传入内存块的大小;不过,在C++的内存分配中,我们可以很容易地解决这个问题。
2. 改造的 slab
我们不能为每一种数据结构都实现一个 cache,但我们可以为每一种大小的数据结构实现一个 cache。这里要解决的问题是从数据结构的大小找到对应的 cache。我们的解决办法如下:从1到512字节大小的对象的 cache,用数组,大于512字节的,用 std::map或hash_map;最终,内存分配的数据结构及接口如下:
struct kmem_slab
{
struct kmem_cache* cache;
struct kmem_slab* next; // next slab of the same cache
char *freeblock; // linked list of free blocks, NULL if none
char *chunk; // linked list of all blocks
size_t chunkSize; // sizeof the chunk
size_t blocks; // total number of blocks (chunkSize / blockSize)
size_t refCount; // How many blocks are allocated.
};
struct kmem_cache
{
struct kmem_slab* slabs; // slabs are linked into a list
struct kmem_slab* freeslab; // slab that may have free block
size_t blockSize; // block size of a block
// map from block to slab, only used in large object
KSlabMap *slabmap;
};
// for small objects, we store the caches in static array;
// for large objects, we store the caches in a map.
// Memory is aligned, so we don't need KMM_MAX_SMALLOBJECT_SIZE kmem_caches
//
static struct kmem_cache smallCache[KMM_MAX_SMALLOBJECT_SIZE / KMM_ALIGN];
/*
* Mapping from block size to corresponding kmem_cache(large objects).
*/
static KCacheMap *largeCache;
void* kmemcache_alloc(size_t nBytes);
void kmemcache_free(void* ptr, size_t nBytes);
3. 在C++中使用 slab
有两种方法,一,虚基类,二,宏
(1) 虚基类
class KObject {
public:
virtual ~KObject(){}
static void* operator new(size_t nBytes) {
return kmemcache_alloc(nBytes);
}
static void operator delete(void* ptr, size_t nBytes) {
kmemcache_free(ptr, nBytes);
}
}
凡是从 KObject 继承的类都具备这种基于 cache 的分配能力。注意,virtual ~KObject(){}是必须的,否则编译器无法知道对象的实际大小,<Modern C++ Design>里有对这一点的详细说明。
(2) 宏
#define OBJECT_ALLOCATOR() /
public:/
static void* operator new(size_t nBytes) {/
return kmemcache_alloc(nBytes);/
}/
static void operator delete(void* ptr, size_t nBytes) {/
kmemcache_free(ptr, nBytes);/
}
把它放在每一个类中就可以了。
参考:
1. Modern C++ Design
2. The Slab Allocator: An Object-Caching Kernel Memory Allocator
3. Understanding the Linux Kernel
4. Linux 内核源代码情景分析
1. 先说 slab
Linux 的 slab 内存管理由两个部分组成,一是所谓的 cache, 再就是 slab;每一种数据结构对应一个 cache,举例来说,struct task_struct (这个结构非常大,就是所谓的进程控制块)对应 taskstruct_cachep, 而这种数据结构在分配内存时就只能从对应的 cache 中分配,也即,要分配一个 struct task_struct, 就只能
kmem_cache_alloc(taskstruct_cachep);总体上,这个 cache 管理着一个对象(object)池,分配内存时,直接从池中取空闲的对象,而释放时则返还给这个对象池,只有在池为空时才向系统申请内存页;所以这个对象池是非常快的。
这个内存池由一个cache管理,由多个 slab 组成,每个 slab 可以缓存多个对象。注意,slab是固定大小的而且都是一个页的大小(4K), 这是为了计算的方便,后面就会看到。cache 和 slab 的结构如图所示:
_________ _________
cachep----->| slab |------>| slab |---->......
| | | |
| | | |
| object | | object |
| object | | object |
(4K) |___ _____| (4K)|________|
一个 slab 又由两部分组成,这个4K的内存块的前面的一小部分是 slab 控制块(Linux 里面稍有不同),紧接着就是一组 object。slab控制块维护着一个指针,指向当前空闲着的对象。当一个对象被释放时,就让这个指针指向这个刚释放的对象;而在申请内存时直接将这个指针指向的空闲对象返回即可。在释放时需要找到对象对应的slab,那么如何找到它呢?直接将其指针圆整到4K就是相应 slab 控制块的首地址!
你会猜,既然每一种数据结构都需要一个 cache,那么内核中肯定有不少 cache 吧?猜对了! Linux 内核中确实有不少 cache, 而且每一种重要的数据结构都有一个 cache;然而,仔细观察发现,cache其实是以数据结构的大小为分界线的,两个数据结构,只要大小相同,就可以共用一个 cache,于是又有了 kmalloc,它管理着几个 cache,内核中没有 cache 的数据结构都从 kmalloc 中分配;为了使内存管理器更加通用,我们将要实现的内存管理器也将以大小为中心。
实现通用性是有代价的,以数据结构为中心的 cache 在释放内存时只需传入数据结构的指针,而以大小为中心的 cache 还要传入内存块的大小;不过,在C++的内存分配中,我们可以很容易地解决这个问题。
2. 改造的 slab
我们不能为每一种数据结构都实现一个 cache,但我们可以为每一种大小的数据结构实现一个 cache。这里要解决的问题是从数据结构的大小找到对应的 cache。我们的解决办法如下:从1到512字节大小的对象的 cache,用数组,大于512字节的,用 std::map或hash_map;最终,内存分配的数据结构及接口如下:
struct kmem_slab
{
struct kmem_cache* cache;
struct kmem_slab* next; // next slab of the same cache
char *freeblock; // linked list of free blocks, NULL if none
char *chunk; // linked list of all blocks
size_t chunkSize; // sizeof the chunk
size_t blocks; // total number of blocks (chunkSize / blockSize)
size_t refCount; // How many blocks are allocated.
};
struct kmem_cache
{
struct kmem_slab* slabs; // slabs are linked into a list
struct kmem_slab* freeslab; // slab that may have free block
size_t blockSize; // block size of a block
// map from block to slab, only used in large object
KSlabMap *slabmap;
};
// for small objects, we store the caches in static array;
// for large objects, we store the caches in a map.
// Memory is aligned, so we don't need KMM_MAX_SMALLOBJECT_SIZE kmem_caches
//
static struct kmem_cache smallCache[KMM_MAX_SMALLOBJECT_SIZE / KMM_ALIGN];
/*
* Mapping from block size to corresponding kmem_cache(large objects).
*/
static KCacheMap *largeCache;
void* kmemcache_alloc(size_t nBytes);
void kmemcache_free(void* ptr, size_t nBytes);
3. 在C++中使用 slab
有两种方法,一,虚基类,二,宏
(1) 虚基类
class KObject {
public:
virtual ~KObject(){}
static void* operator new(size_t nBytes) {
return kmemcache_alloc(nBytes);
}
static void operator delete(void* ptr, size_t nBytes) {
kmemcache_free(ptr, nBytes);
}
}
凡是从 KObject 继承的类都具备这种基于 cache 的分配能力。注意,virtual ~KObject(){}是必须的,否则编译器无法知道对象的实际大小,<Modern C++ Design>里有对这一点的详细说明。
(2) 宏
#define OBJECT_ALLOCATOR() /
public:/
static void* operator new(size_t nBytes) {/
return kmemcache_alloc(nBytes);/
}/
static void operator delete(void* ptr, size_t nBytes) {/
kmemcache_free(ptr, nBytes);/
}
把它放在每一个类中就可以了。
参考:
1. Modern C++ Design
2. The Slab Allocator: An Object-Caching Kernel Memory Allocator
3. Understanding the Linux Kernel
4. Linux 内核源代码情景分析
- slab 内存管理在现代C++中的应用
- slab内存管理
- Nginx slab内存管理
- linux内存管理--slab
- 内存管理-SLAB(SLAB的基本数据结构)
- 内存管理-SLAB(创建SLAB高速缓存kmem_cache_create())
- 内存管理-SLAB(销毁SLAB高速缓存kmem_cache_destroy())
- 内存管理-SLAB(分配SLAB对象kmem_cache_alloc())
- 内存管理-SLAB(释放SLAB对象kmem_cache_free())
- 内存管理-SLAB(创建SLAB块cache_grow())
- 内存管理-SLAB(撤销SLAB块slab_destroy())
- 内存管理-SLAB(SLAB的基本数据结构)
- GPRS在现代远程测控中的应用
- 几何画板在现代教学中的应用
- Excel在现代办公中的应用
- slab内存管理源代码分析
- slab内存管理学习笔记
- slab slub和内存管理
- [NetHack003]You等用法
- 快人快语,言多语失——我伤心,我难过
- 居安思危
- Visual C++开发工具与调试技巧整理
- 百度优化经验总结(实用)
- slab 内存管理在现代C++中的应用
- seo,你是做加法,还是做减法?
- unix/linux 经典书籍推荐
- 在编程过程中需要注意的火狐和IE兼容性问题和解决方法
- 一个小发现:Internet临时文件夹设置过小会影响一些网页的正常运行
- VB编程的一些小技巧 - ado如何连接oracle
- JUnit best practices
- Flex启动事件的执行顺序
- C#日期函数