slab:
slab是啥 借用百度百科的话来说,slab是Linux操作系统的一种内存分配机制。其工作是针对一些经常分配并释放的对象,如进程描述符等,这些对象的大小一般比较小,如果直接采用伙伴系统来进行分配和释放,不仅会造成大量的内存碎片,而且处理速度也太慢。而slab分配器是基于对象进行管理的,相同类型的对象归为一类(如进程描述符就是一类),每当要申请这样一个对象,slab分配器就从一个slab列表中分配一个这样大小的单元出去,而当要释放时,将其重新保存在该列表中,而不是直接返回给伙伴系统,从而避免这些内碎片。slab分配器并不丢弃已分配的对象,而是释放并把它们保存在内存中。当以后又要请求新的对象时,就可以从内存直接获取而不用重复初始化。
数据结构 和初始化
struct ngx_slab_page_s {
uintptr_t slab;
ngx_slab_page_t *next;
uintptr_t prev;
};
typedef struct {
ngx_shmtx_sh_t lock;
size_t min_size;//最小的大小
size_t min_shift;//最小的偏移
ngx_slab_page_t *pages;//页数组
ngx_slab_page_t free; //空闲页面的数组
u_char *start; //可分配空间的开始节点
u_char *end; //结束地址
ngx_shmtx_t mutex;
u_char *log_ctx;
u_char zero;
unsigned log_nomem:1;
void *data;
void *addr;
} ngx_slab_pool_t;
void
ngx_slab_init(ngx_slab_pool_t *pool)
{
u_char *p;
size_t size;
ngx_int_t m;
ngx_uint_t i, n, pages;
ngx_slab_page_t *slots;
/* STUB */
if (ngx_slab_max_size == 0) {
ngx_slab_max_size = ngx_pagesize / 2;//最大为pagesize的一半
ngx_slab_exact_size = ngx_pagesize / (8 * sizeof(uintptr_t));//计算exact size
for (n = ngx_slab_exact_size; n >>= 1; ngx_slab_exact_shift++) {
/* void */
}
}
/**/
pool->min_size = 1 << pool->min_shift;//最小大小
p = (u_char *) pool + sizeof(ngx_slab_pool_t);//空闲内存的位置
size = pool->end - p;//总内存的大小
ngx_slab_junk(p, size);
slots = (ngx_slab_page_t *) p;
n = ngx_pagesize_shift - pool->min_shift;// 小内存的数量
for (i = 0; i < n; i++) {
//初始化
slots[i].slab = 0;
slots[i].next = &slots[i];
slots[i].prev = 0;
}
p += n * sizeof(ngx_slab_page_t);//继续偏移到页的位置
pages = (ngx_uint_t) (size / (ngx_pagesize + sizeof(ngx_slab_page_t)));//计算页数
ngx_memzero(p, pages * sizeof(ngx_slab_page_t));
pool->pages = (ngx_slab_page_t *) p;//页数组指针
pool->free.prev = 0;
pool->free.next = (ngx_slab_page_t *) p;//刚开始空闲就是开始
pool->pages->slab = pages;
pool->pages->next = &pool->free;
pool->pages->prev = (uintptr_t) &pool->free;
pool->start = (u_char *)
ngx_align_ptr((uintptr_t) p + pages * sizeof(ngx_slab_page_t),
ngx_pagesize);//设置开始位置
m = pages - (pool->end - pool->start) / ngx_pagesize;//地址偏移后 看下有木有被浪费了空间
if (m > 0) {
//更新
pages -= m;
pool->pages->slab = pages;
}
pool->log_nomem = 1;
pool->log_ctx = &pool->zero;
pool->zero = '\0';
}
总结下:
首先根据min_shift 和ngx_pagesize的大小算出分配小内存slots数量。然后再根据start 和end 算出pages(多少页)。
所以说他们的内存结构是 ngx_salb_pool 后面跟着一个ngx_slab_page_t[slots]大小的数组,保存的是小内存信息,然后后面跟着一个ngx_slab_page_t[pages]大小的数组,保存的页信息,然后后面跟上的就是剩余的用来分配的内存了。
插入
插入还是先直接上注释的代码看来代码来看总结,应该就能理解了
static ngx_slab_page_t *
ngx_slab_alloc_pages(ngx_slab_pool_t *pool, ngx_uint_t pages)
{
ngx_slab_page_t *page, *p;
for (page = pool->free.next; page != &pool->free; page = page->next) {
if (page->slab >= pages) {
//足够的内存
if (page->slab > pages) {
//还有剩的
page[pages].slab = page->slab - pages;//还剩的数量
page[pages].next = page->next;//下一个节点
page[pages].prev = page->prev;//上一个节点
p = (ngx_slab_page_t *) page->prev;//维护上下节点
p->next = &page[pages];
page->next->prev = (uintptr_t) &page[pages];
} else {
// 刚好用完
p = (ngx_slab_page_t *) page->prev;
p->next = page->next;
page->next->prev = page->prev;//维护上下信息
}
page->slab = pages | NGX_SLAB_PAGE_START;
page->next = NULL;
page->prev = NGX_SLAB_PAGE;
if (--pages == 0) {
//只有一页