Linux内核中创建slab主要由函数cache_grow()实现,从slab的创建中我们可以完整地看到slab与对象、页面的组织方式。

/*
* Grow (by 1) the number of slabs within a cache.  This is called by
* kmem_cache_alloc() when there are no active objs left in a cache.
*/
/*使用一个或多个页面创建一个空slab。
objp:页面虚拟地址,为空表示还未申请内存页,不为空
,说明已申请内存页,可直接用来创建slab*/
static int cache_grow(struct kmem_cache *cachep,
gfp_t flags, int nodeid, void *objp)
{
struct slab *slabp;
size_t offset;
gfp_t local_flags;
struct kmem_list3 *l3;
/*
* Be lazy and only check for valid flags here,  keeping it out of the
* critical path in kmem_cache_alloc().
*/
BUG_ON(flags & GFP_SLAB_BUG_MASK);
local_flags = flags & (GFP_CONSTRAINT_MASK|GFP_RECLAIM_MASK);
/* Take the l3 list lock to change the colour_next on this node */
check_irq_off();
/* 获得本内存节点的slab三链 */
l3 = cachep->nodelists[nodeid];
spin_lock(&l3->list_lock);
/* Get colour for the slab, and cal the next value. */
/* 获得本slab的着色区偏移 */
offset = l3->colour_next;
/* 更新着色区偏移,使不同slab的着色偏移不同 */
l3->colour_next++;
/* 不能超过着色区的总大小,如果超过了,重置为0。这就是前面分析过的着色循环问题
。事实上,如果slab中浪费的空间很少,那么很快就会循环一次。*/
if (l3->colour_next >= cachep->colour)
l3->colour_next = 0;
spin_unlock(&l3->list_lock);
/* 将着色单位区间的个数转换为着色区大小 */
offset *= cachep->colour_off;
if (local_flags & __GFP_WAIT)
local_irq_enable();
/*
* The test for missing atomic flag is performed here, rather than
* the more obvious place, simply to reduce the critical path length
* in kmem_cache_alloc(). If a caller is seriously mis-behaving they
* will eventually be caught here (where it matters).
*/
kmem_flagcheck(cachep, flags);
/*
* Get mem for the objs.  Attempt to allocate a physical page from
* 'nodeid'.
*/
if (!objp)/* 还未分配页面,从本内存节点分配1<<cachep->gfporder个页面
,objp为slab首页面的虚拟地址 */
objp = kmem_getpages(cachep, local_flags, nodeid);
if (!objp)
goto failed;
/* Get slab management. */
/* 分配slab管理对象 */
slabp = alloc_slabmgmt(cachep, objp, offset,
local_flags & ~GFP_CONSTRAINT_MASK, nodeid);
if (!slabp)
goto opps1;
/* 设置page到cache、slab的映射 */
slab_map_pages(cachep, slabp, objp);
/* 初始化slab中的对象 */
cache_init_objs(cachep, slabp);
if (local_flags & __GFP_WAIT)
local_irq_disable();
check_irq_off();
spin_lock(&l3->list_lock);
/* Make slab active. */
list_add_tail(&slabp->list, &(l3->slabs_free));
/* 更新本cache增长计数 */
STATS_INC_GROWN(cachep);
/* 更新slab链表中空闲对象计数 */
l3->free_objects += cachep->num;
spin_unlock(&l3->list_lock);
return 1;
opps1:
kmem_freepages(cachep, objp);
failed:
if (local_flags & __GFP_WAIT)
local_irq_disable();
return 0;
}

执行流程:

1,从cache结构中获得并计算着色区偏移量;

2,从伙伴系统中获得1<<cachep->gfporder个页面用于slab;

3,初始化slab中相关变量,如果是外置式slab需要从新申请slab管理区的空间,由函数alloc_slabmgmt()实现。

/*分配slab管理对象*/
static struct slab *alloc_slabmgmt(struct kmem_cache *cachep, void *objp,
int colour_off, gfp_t local_flags,
int nodeid)
{
struct slab *slabp;
if (OFF_SLAB(cachep)) {
/* Slab management obj is off-slab. */
/* 外置式slab。从general slab cache中分配一个管理对象,
slabp_cache指向保存有struct slab对象的general slab cache。
slab初始化阶段general slab cache可能还未创建,slabp_cache指针为空
,故初始化阶段创建的slab均为内置式slab。*/
slabp = kmem_cache_alloc_node(cachep->slabp_cache,
local_flags, nodeid);
/*
* If the first object in the slab is leaked (it's allocated
* but no one has a reference to it), we want to make sure
* kmemleak does not treat the ->s_mem pointer as a reference
* to the object. Otherwise we will not report the leak.
*//* 对第一个对象做检查 */
kmemleak_scan_area(slabp, offsetof(struct slab, list),
sizeof(struct list_head), local_flags);
if (!slabp)
return NULL;
} else {/* 内置式slab。objp为slab首页面的虚拟地址,加上着色偏移
,得到slab管理对象的虚拟地址 */
slabp = objp + colour_off;
/* 计算slab中第一个对象的页内偏移,slab_size保存slab管理对象的大小
,包含struct slab对象和kmem_bufctl_t数组 */
colour_off += cachep->slab_size;
} /* 在用(已分配)对象数为0 */
slabp->inuse = 0;
/* 第一个对象的页内偏移,可见对于内置式slab,colouroff成员不仅包括着色区
,还包括管理对象占用的空间
,外置式slab,colouroff成员只包括着色区。*/
slabp->colouroff = colour_off;
/* 第一个对象的虚拟地址 */
slabp->s_mem = objp + colour_off;
/* 内存节点ID */
slabp->nodeid = nodeid;
/* 第一个空闲对象索引为0,即kmem_bufctl_t数组的第一个元素 */
slabp->free = 0;
return slabp;
}

通过初始化,我们画出下面图像。

4,设置slab中页面(1<<cachep->gfporder个)到slab、cache的映射。这样,可以通过page的lru链表找到page所属的slab和cache。slab_map_pages()实现

/*设置page到cache、slab的指针,这样就能知道页面所在的cache、slab
addr:slab首页面虚拟地址*/
static void slab_map_pages(struct kmem_cache *cache, struct slab *slab,
void *addr)
{
int nr_pages;
struct page *page;
/* 获得slab首页面*/
page = virt_to_page(addr);
nr_pages = 1;
/* 如果不是大页面(关于大页面请参阅相关文档)
,计算页面的个数 */
if (likely(!PageCompound(page)))
nr_pages <<= cache->gfporder;
do {
/* struct page结构中的lru根据页面的用途有不同的含义
,当页面空闲或用于高速缓存时,
lru成员用于构造双向链表将page串联起来,而当page用于slab时,
next指向page所在的cache,prev指向page所在的slab */
page_set_cache(page, cache);
page_set_slab(page, slab);
page++;
} while (--nr_pages);
}

代码实现结果如下图

5,初始化slab中kmem_bufctl_t[]数组,其中kmem_bufctl_t[]数组为一个静态链表,指定了slab对象(obj)的访问顺序。即kmem_bufctl_t[]中存放的是下一个访问的obj。在后面分析中slab_get_obj()函数从slab中提取一个空闲对象,他通过index_to_obj()函数找到空闲对象在kmem_bufctl_t[]数组中的下标,然后通过slab_bufctl(slabp)[slabp->free]获得下一个空闲对象的索引并用它更新静态链表。

/*初始化slab中的对象,主要是通过kmem_bufctl_t数组将对象串联起来*/
static void cache_init_objs(struct kmem_cache *cachep,
struct slab *slabp)
{
int i;
/* 逐一初始化slab中的对象 */
for (i = 0; i < cachep->num; i++) {
/* 获得slab中第i个对象 */
void *objp = index_to_obj(cachep, slabp, i);
#if DEBUG
/* need to poison the objs? */
if (cachep->flags & SLAB_POISON)
poison_obj(cachep, objp, POISON_FREE);
if (cachep->flags & SLAB_STORE_USER)
*dbg_userword(cachep, objp) = NULL;
if (cachep->flags & SLAB_RED_ZONE) {
*dbg_redzone1(cachep, objp) = RED_INACTIVE;
*dbg_redzone2(cachep, objp) = RED_INACTIVE;
}
/*
* Constructors are not allowed to allocate memory from the same
* cache which they are a constructor for.  Otherwise, deadlock.
* They must also be threaded.
*/
if (cachep->ctor && !(cachep->flags & SLAB_POISON))
cachep->ctor(objp + obj_offset(cachep));
if (cachep->flags & SLAB_RED_ZONE) {
if (*dbg_redzone2(cachep, objp) != RED_INACTIVE)
slab_error(cachep, "constructor overwrote the"
" end of an object");
if (*dbg_redzone1(cachep, objp) != RED_INACTIVE)
slab_error(cachep, "constructor overwrote the"
" start of an object");
}
if ((cachep->buffer_size % PAGE_SIZE) == 0 &&
OFF_SLAB(cachep) && cachep->flags & SLAB_POISON)
kernel_map_pages(virt_to_page(objp),
cachep->buffer_size / PAGE_SIZE, 0);
#else
/* 调用此对象的构造函数 */
if (cachep->ctor)
cachep->ctor(objp);
#endif /* 初始时所有对象都是空闲的,只需按照数组顺序串起来即可 */
/*相当于静态索引指针*/
slab_bufctl(slabp)[i] = i + 1;
}
/* 最后一个指向BUFCTL_END */
slab_bufctl(slabp)[i - 1] = BUFCTL_END;
}

Linux内存管理之slab机制(创建slab)相关推荐

  1. Linux内存管理:分页机制

    <Linux内存管理:内存描述之内存节点node> <Linux内存管理:内存描述之内存区域zone> <Linux内存管理:内存描述之内存页面page> < ...

  2. Linux内存管理:反向映射机制(匿名页,文件页和ksm页)

    目录 1.反向映射的发展 2.反向映射应用场景 3.匿名页的反向映射 4.文件页的反向映射 5.ksm页的反向映射 6.总结 7.作者简介 8.推荐阅读 为了系统的安全性,Linux内核将各个用户进程 ...

  3. Linux内存管理:内存寻址之分段机制与分页机制

    目录 Linux 内存寻址之分段机制 前言 分段到底是怎么回事? 实模式的诞生(16位处理器及寻址) 保护模式的诞生(32位处理器及寻址) IA32的内存寻址机制 寻址硬件 IA32的三种地址 MMU ...

  4. linux 内存 段,Linux内存储器管理之分段机制

    Linux内存管理之分段机制 逻辑地址就是我们普通的段+偏移的表现方式,而线性地址就是段+偏移之后算出来的一个地址,前者可以认 为是二维的地址,而后者可以理解是一维的.线性地址和虚拟地址的概念相接近, ...

  5. Linux内存管理:memblock(引导期间管理内存区域)

    目录 介绍 内存块 内存块初始化 Memblock API 获取有关内存区域的信息 Memblock调试 链接 相关阅读 看原文:<Linux内存管理:memblock> 介绍 内存管理是 ...

  6. Linux内存管理:Fixmaps(固定映射地址)和ioremap

    目录 Fixmaps和ioremap 映射 ioremap工作原理 早期ioremap的使用 Links 相关阅读 Fix-Mapped地址是一组特殊的编译时地址,其对应的物理地址不必是线性地址减__ ...

  7. Linux内存管理:MMU那些事儿

    <ARM SMMU原理与IOMMU技术("VT-d" DMA.I/O虚拟化.内存虚拟化)> <Linux内存管理:分页机制> <Linux内存管理:内 ...

  8. Linux内存管理:TLB flush操作

    目录 一.前言 二.基本概念 1.什么是TLB? 2.为什么有TLB? 3.TLB工作原理 三.ARMv8的TLB 1.TLB的组成结构 2.如何确定TLB match 3.进程切换和ASID(Add ...

  9. Linux内存管理:一个故事看懂CPU内存管理技术

    目录 8086 32位时代 虚拟内存 分页交换 现在 往期热门回顾 推荐阅读 还记得我吗,我是阿Q,CPU一号车间的那个阿Q. 今天忙里偷闲,来到厂里地址翻译部门转转,负责这项工作的小黑正忙得满头大汗 ...

  10. Linux内存管理:内存分配:slab分配器

    <linux内核之slob.slab.slub> <Linux内核:kmalloc()和SLOB.SLAB.SLUB内存分配器> <Linux内存管理:内存分配:slab ...

最新文章

  1. 生产环境碰到系统CPU飙高和频繁GC,你要怎么排查?
  2. 用C#语言构造蜘蛛程序
  3. c++:vector用法
  4. 互换性与技术测量电子版_圆柱公差与配合,公差等级的选用,一文全面介绍互换性与测量技术...
  5. php thumbs.db,window_Win8系统删除thumbs.db文件的方法,  最近有Win8系统用户反映, - phpStudy...
  6. [转]Ubuntu terminator 无法打开解决方案
  7. python 数据类笔试题_数据分析岗Python笔试题
  8. 5分钟用C#实现串口助手
  9. 淘宝接口 http://ip.taobao.com/service/getIpInfo.php?ip=myip 获取不到手机ip地址
  10. 一篇文章带你了解!什么是贴近摄影测量
  11. 2.zookeeper
  12. “正被停用的激活上下文不是最近激活的”的错误的解决
  13. Hook其他程序中的StringGrid的内容(内牛满面,终于找到了。)
  14. 苹果三代耳机_华强北airpods2 华强北三代耳机 airpodspro可调通透 主动降噪 定位改名 苹果airpodspro...
  15. win10技巧(关闭自动更新、上帝模式、滑动关机)花里胡哨
  16. qq163音乐网归属问题释疑
  17. 多个目标优化的帕累托前沿面如何可视化
  18. 2020年美容师(中级)模拟考试及美容师(中级)考试软件
  19. 【云计算与大数据】知识点总结
  20. wix和wil文件_Wix的微服务和DevOps旅程

热门文章

  1. 生于忧患,死于安乐-时刻保持危机感
  2. 从Java程序猿到产品经理
  3. SQL注入攻击以及防护
  4. 【英语论文】英汉委婉语的文化价值和民族特质比较(节选)
  5. enc28j60 linux 驱动_enc28j60网卡驱动模块添加进Linux内核,Kconfig,Makefile配置过程...
  6. Ardunio开发实例-ENC28J60以太网模块实现Web服务器
  7. 移动端页面布局方式,简单记录一下
  8. centos7 nbd 挂在qcow2或qcow,raw,虚机镜像,virsh,virt,使用qemu-nbd挂载qcow2镜像文件
  9. 【Java实现链表操作】 万字肝爆 !链表的图文解析(包含链表OJ练习解析)
  10. 【springboot进阶】RestTemplate 集成 okhttp3 请求带p12证书