Linux 内存管理之 SLUB分配器(5):slub初始化过程

本小节整理记录下kmem_cache结构的初始化过程,其实呢,在前边4个小节整理完成后,本部分也就呼之欲出了

本部分主要解决如下问题:

  1. 在slub分配逻辑中可以看到,object的alloc实际是在kmem_cache和kmem_cache_node 结构中分配的,那么必然存在一个蛋与鸡的故事,如何处理的?
  2. 在初始化的过程中,有哪些关键接口,以及其作用?

首先将本部分的整体调用结构图示出来:

1. 构造静态kmem_cache & kmem_cache_node结构

前边第3小节已经介绍了object的分配方法,object分配的时候会在kmem_cache结构中逐级搜索,即需要kmem_cache和kmem_cache_node作为前置条件,内核的做法是先填充一个kmem_cache的数据结构,然后再调用alloc接口重新分配一次,将其内容填充进来;

  1. 创建静态的kmem_cache和kmem_cache_node结构
  2. 填充kmem_cache的结构体
    1. 填充name、size等变量
    2. 计算size、order、order_object的数据
    3. 申请node的object,注意这个时候还没有slub结构,所以调用的是early接口,直接向buddy申请page
    4. 填充node中变量
    5. 申请并填充kmem_cache_cpu结构
  3. 向slub系统申请object,并赋值添加到系统中
    1. 向slub申请object
    2. 将boot struct填充到刚才申请的空间中
    3. 添加到slab_caches的链表上

这里边几个重要的接口:

接口 说明
create_boot_cache 填充kmem_cache结构体
kmem_cache_open 实际填充kmem_cache的操作
calculate_sizes 计算object size并构造object结构
init_kmem_cache_nodes(s) 初始化node结构
early_kmem_cache_node_alloc(node) 向buddy申请page作为node结构,并赋值
alloc_kmem_cache_cpus(s) 申请并构造cpu结构
bootstrap 申请object将kmem_cache填充进来并添加到系统
kmem_cache_zalloc 通过slub系统申请object结构

经过这部分,我们可以知道的是后续构造空间的接口顺序是:

  1. kmem_cache_zalloc 申请空间
  2. create_boot_cache 填充数据
  3. list_add增加到链表管理

此部分比较重要的接口:

static void early_kmem_cache_node_alloc(int node)
{struct page *page;struct kmem_cache_node *n;page = new_slab(kmem_cache_node, GFP_NOWAIT, node);//直接向slub申请object,此接口实际调用allocate_page,之前分析过n = page->freelist;//接下来几行都是赋值操作page->freelist = get_freepointer(kmem_cache_node, n);page->inuse = 1;page->frozen = 0;kmem_cache_node->node[node] = n;init_object(kmem_cache_node, n, SLUB_RED_ACTIVE);//填充object数据,这里实际填充的是inactive值init_tracking(kmem_cache_node, n);kasan_kmalloc(kmem_cache_node, n, sizeof(struct kmem_cache_node), GFP_KERNEL);init_kmem_cache_node(n);//填充nodeinc_slabs_node(kmem_cache_node, node, page->objects);//node结构中几个统计数值++__add_partial(n, page, DEACTIVATE_TO_HEAD);//添加到partial中
}static struct kmem_cache * __init bootstrap(struct kmem_cache *static_cache)
{int node;struct kmem_cache *s = kmem_cache_zalloc(kmem_cache, GFP_NOWAIT);//cache和node已经ready可以alloc了struct kmem_cache_node *n;memcpy(s, static_cache, kmem_cache->object_size);//拷贝过来__flush_cpu_slab(s, smp_processor_id());for_each_kmem_cache_node(s, node, n) {struct page *p;list_for_each_entry(p, &n->partial, lru)//partial链表赋值p->slab_cache = s;list_for_each_entry(p, &n->full, lru)//full链表赋值p->slab_cache = s;}slab_init_memcg_params(s);list_add(&s->list, &slab_caches);//slab_caches ++return s;
}

2. kmalloc_caches中的几个计算

setup_kmalloc_cache_index_table() size_index的填充

size(bytes) Index size_index[index]
8 0 3
16 1 3
32 3 3
64 7 7
96 11 7
128 15 7
192 23 8

对应到kmalloc_cache上

i kmalloc_cache[i]
3 8
4 16
5 32
6 64
7 128
8 256
9 512
10 1024
11 2048

对应上上述的size_index:

size(bytes) Index size_index[index] kmalloc_slab(size) kmalloc_cache[i]
8 0 3 kmalloc_cache[3] 8
16 1 3 kmalloc_cache[3] 8
32 3 3 kmalloc_cache[3] 8
64 7 7 kmalloc_cache[7] 128
96 11 7 kmalloc_cache[7] 128
128 15 7 kmalloc_cache[7] 128
192 23 8 kmalloc_cache[8] 256

这部分计算比较奇怪,size_index数组填充为上述值后,反倒不比原始数据更容易理解:

 */
static s8 size_index[24] = {3, /* 8 */4,   /* 16 */5,  /* 24 */5,  /* 32 */6,  /* 40 */6,  /* 48 */6,  /* 56 */6,  /* 64 */1,  /* 72 */1,  /* 80 */1,  /* 88 */1,  /* 96 */7,  /* 104 */7, /* 112 */7, /* 120 */7, /* 128 */2, /* 136 */2, /* 144 */2, /* 152 */2, /* 160 */2, /* 168 */2, /* 176 */2, /* 184 */2  /* 192 */
};

这里存疑

3. 关键调用逻辑整理记录

 mm_init-- kmem_cache_init() //注意此部分是在mem_init之后就进行了,说明在此之前页表转换系统和buddy系统已经完成了-- create_boot_cache();// kmem_cache_node,创建静态的kmem_cache-- s->xxx = xxx //赋值操作-- __kmem_cache_create(s, flags)-- kmem_cache_open(s, flags)-- calculate_sizes(s, -1) //计算object的size,然后构造oo-- set_min_partial(s, ilog2(s->size) / 2) //从这里可以看到partial的数量与size相关,8Byte的话1个,8K的话6个-- s->cpu_partial = 0 /2 /6/ 13/ 30同样与size相关-- init_kmem_cache_nodes(s) -- for_each_node_state-- slab_state == down ==> early_kmem_cache_node_alloc-- page = new_slab //向buddy申请page,注意再次之前已经计算出order、count、size了;//inuse 此时为objects数量,frozen此时为1-- page->xxx赋值-- init_object //在申请page的时候allocate中已经操作过了一遍,差别在于//那时候传入为inactive,此时为active,即此时为分配出去的;-- init_tracking-- init_kmem_cache_node(n)//创建两个链表,将partial、nr_slabs、total_objects设置为0//标准初始化处理-- inc_slabs_node(kmem_cache_node)        //nr_slab数量+1,objects总数添加-- __add_partial(n, page, DRACTIVATE_TO_TAIL) //添加到partial列表中-- alloc_kmem_cache_cpus-- s->cpu_slab = __alloc_percpu()-- init_kmem_cache_cpus(s)-- for_each_possible_cpu(cpu)//遍历每个cpu,假设4核咯-- per_cpu_ptr(s->cpu_slab, cpu)->tid = init_tid(cpu));-- sysfs_slab_add(s) //添加到sys文件系统中-- bootstrap()//申请个object 将上述静态kmem_cache丢进来-- kmem_cache *s = kmem_cahce_zalloc(kmem_cache, GFP_NOWAIT)//申请空间,注意此时已经创建完成两个static的结构:kmem_cache & kmem_cache_node-- kmem_cache_alloc(kmem_cache, GFP_NOWAIT | __GFP_ZERO)-- void *ret = slab_alloc(s, gfpflags, _RET_IP_) //分配kmem_cache_node这么大的object出来,这部分在第三小节有详细逻辑描述,这里简单过去了-- slab_alloc_node(s, gfpflags, NUMA_NO_NODE, addr) //把node加进来,注意这个位置传入的是NUMA_NO_NODE-- slab_pre_aloc_hook(s, gfpflags)-- object = c->freelist,page = c->page-- if(unlikely(!object || !node_match(page, node)))//判断cpu指向的freelist是否有空闲的object-- case1 可以找到:-- void* next_object= get_freepointer_safe(s, object)-- unlikely(!this_cpu_cmpchg_double(s->cpu_slab->freelist, s->cpu_slab->tid, object, tid, next_object, next_tid(tid))) //比较并赋值-- prefetch_freepointer(s, next_object);//把next_object 预取进来-- case2 无法找到:object = __slab_alloc(s, gfpflags, node, addr, c)-- 加个锁-- ___slab_alloc(s, gfpflags, node, addr, c)-- 1. page内分配,针对于刚好有释放的情况;-- 2. cpu->partial内分配-- 3. node 内分配-- 4. 直接申请page-- 去掉锁-- slab_post_alloc_hook(s, gfpflags, 1 , &object)-- trace_kmem_cache_alloc()//添加trace操作,-- memcpy(s, static_cache, kmem_cache->object_size )-- __flush_cpu_slab(s, smp_processor_id)())-- p->slab_cache = s //对分配到的每个page赋值-- list_add(&s->list, &slab_caches) //将这个kmem_cache加入链表中-- setup_kmalloc_cache_index_table() //填充size_index数组,这个数组在kmalloc_caches初始化的时候会使用-- create_kmalloc_caches(0)-- 遍历kmalloc_caches数组-- new_kmalloc_cache(i, flags)-- kmalloc_caches[idx] = create_kmalloc_cache(kmalloc_info[idx].name, kmalloc_info[idx].size, flags)-- kmem_cache *s = kmem_cache_zalloc(kmem_cache, GFP_NOWAIT) //前文也已经分析过了,分配object操作;-- create_boot_cache(s, name, size, flags)//前文已经分析过了,是赋值操作-- list_add(&s->list, &slab_caches)

4. 附录

  1. 相关目录

    目录 说明
    /init/main.c kernel初始化
    /mm/slub.c slub 结构和操作核心文件
    /include/linux/slub_def.h 相关结构体定义
    /mm/slab_common.c kmalloc 相关结构体
  2. 最近这几部分的整理总觉得串不起来,到现在也基本都捋顺了,后续再重新串一遍搞个综述出来

Linux 内存管理之 SLUB分配器(5):slub初始化过程相关推荐

  1. Linux内存管理之SLAB分配器

    注:本文讲述的SLAB相关代码是基于Linux内核v4.7,代码网址. 1.SLAB分配器的由来 在讲SLAB分配器之前先说两个概念: 内部碎片和外部碎片. 外部碎片指的是还没有被分配出去(不属于任何 ...

  2. Linux内存管理(二):页面查询过程简述

    目录 0. 前言 1. MMU 2. VPN/PFN/PT/PTE 3. MMU中的TLB和TTW 4. 一级页表映射过程

  3. linux内存管理子系统采用基于内存区域,Linux 内存管理之highmem简介

    一.Linux内核地址空间 一般来说Linux 内核按照 3:1 的比率来划分虚拟内存(X86等):3 GB 的虚拟内存用于用户空间,1GB 的内存用于内核空间.当然有些体系结构如MIPS使用2:2 ...

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

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

  5. linux内存管理(六)-伙伴分配器

    linux内存三大分配器:引导内存分配器,伙伴分配器,slab分配器 伙伴分配器 当系统内核初始化完毕后,使用页分配器管理物理页,当使用的页分配器是伙伴分配器,伙伴分配器的特点是算法简单且高效,支持内 ...

  6. 【Linux 内核 内存管理】memblock 分配器编程接口 ⑤ ( memblock_free 函数 | memblock_remove_range 函数 )

    文章目录 一.memblock_free 函数分析 二.memblock_remove_range 函数分析 memblock 分配器提供了如下编程接口 : ① 添加内存 : memblock_add ...

  7. Windows内存管理和linux内存管理

    windows内存管理 windows 内存管理方式主要分为:页式管理,段式管理,段页式管理. 页式管理的基本原理是将各进程的虚拟空间划分为若干个长度相等的页:页式管理把内存空间按照页的大小划分成片或 ...

  8. Linux内存管理原理【转】

    转自:http://www.cnblogs.com/zhaoyl/p/3695517.html 本文以32位机器为准,串讲一些内存管理的知识点. 1. 虚拟地址.物理地址.逻辑地址.线性地址 虚拟地址 ...

  9. Linux内存管理原理

    本文以32位机器为准,串讲一些内存管理的知识点. 1. 虚拟地址.物理地址.逻辑地址.线性地址 虚拟地址又叫线性地址.linux没有采用分段机制,所以逻辑地址和虚拟地址(线性地址)(在用户态,内核态逻 ...

  10. 一文掌握 Linux 内存管理

    作者:dengxuanshi,腾讯 IEG 后台开发工程师 以下源代码来自 linux-5.10.3 内核代码,主要以 x86-32 为例. Linux 内存管理是一个很复杂的"工程&quo ...

最新文章

  1. matlab 弹出提示,谁能告诉我为什么一打开matlab2014b就弹出一个框就自动退出
  2. Java list三种遍历方法性能比较
  3. Go 性能优化技巧 8/10
  4. jquery如何调用后台的方法
  5. python 代码片段6
  6. CSS中!important的使用
  7. 我喜欢构建器模式的三个原因
  8. nt6启动菜单自动修复工具_轻量级windows系统修复,清理工具——Dism++
  9. mysql双重分组没有值也要显示_mysql 统计数据,按照日期分组,把没有数据的日期也展示出来...
  10. ShardingSphere(五) 公共表配置,实现读写改操作
  11. ExtJs六(ExtJs Mvc首页展示)
  12. MySQL—FTS实现原理介绍PPT
  13. Windows创建新账户进行登录
  14. Java在线反编译网站
  15. 编写一个班级管理程序java_java课程设计(班级管理系统)
  16. 苏宁易购实现逆势增长,但它的非电业务更超出意料
  17. 影视剪辑,零基础如何自学入门剪辑,视频剪辑入门规划
  18. 华为Ruby语言需要去理解的东东
  19. 自己怎么压缩js文件?
  20. 1480_人月神话阅读笔记_开篇

热门文章

  1. 数据库中,什么是事务,事务的特性
  2. python之绘制图形库turtle
  3. 如何使用SVG生成超酷的页面预加载素描动画效果
  4. 电影推荐之《白鹿原》 隐私策略(Privacy policy)
  5. jdbc连接数据库代码
  6. Android Contacts(二)—— SMS 短信 与 Contacts 联系人关联
  7. Linux驱动的platform机制
  8. 机器学习中的特征工程总结
  9. Hadoop 开源调度系统zeus
  10. 由input type=file /获取的file.type为空字符串引申浏览器是如何获取文件的MIME类型...