再上一节了解了SLUB是如何申请一个object的,其中涉及了从当前的freelist申请,以及kmem_cache_cpu->partital链表申请,以及到最后的kmem_cache_cpu→node中申请,如果上述三个步骤都没有申请到的话,就会重新创建一个新的slab,然后设置好freelist的指针,返回object使用。

本节我们重点分析下Kmalloc的实现,其实在驱动中大家使用最多的就是用kmalloc申请内存,kmalloc申请的内存大小都普遍比较小,比较快,而且物理地址和虚拟地址是线性映射的,因为kmalloc拿到的内存是从noraml zone获取的。关于zone的知识我们后面有机会说。

直接上代码,去看下Kmalloc的实现。

* The @flags argument may be one of:** %GFP_USER - Allocate memory on behalf of user.  May sleep.** %GFP_KERNEL - Allocate normal kernel ram.  May sleep.** %GFP_ATOMIC - Allocation will not sleep.  May use emergency pools.*   For example, use this inside interrupt handlers.** %GFP_HIGHUSER - Allocate pages from high memory.** %GFP_NOIO - Do not do any I/O at all while trying to get memory.** %GFP_NOFS - Do not make any fs calls while trying to get memory.** %GFP_NOWAIT - Allocation will not sleep.** %__GFP_THISNODE - Allocate node-local memory only.
static __always_inline void *kmalloc(size_t size, gfp_t flags)
{if (__builtin_constant_p(size)) {if (size > KMALLOC_MAX_CACHE_SIZE)return kmalloc_large(size, flags);
#ifndef CONFIG_SLOBif (!(flags & GFP_DMA)) {unsigned int index = kmalloc_index(size);if (!index)return ZERO_SIZE_PTR;return kmem_cache_alloc_trace(kmalloc_caches[index],flags, size);}
#endif}return __kmalloc(size, flags);
}
  • 申请的时候会传递2个参数,第一个参数就是要申请的大小,第二个参数就是申请内存的一些flag,比如常见的GFP_KERNEL
  • 大家也看下注释都有哪些flag,这些flag都代表啥意思,是否可以睡眠,是否是原子操作等
  • __builtin_constant_p 是gcc的一个属性,我们不用理会,直接看_kmalloc的实现。
#define GFP_ATOMIC  (__GFP_HIGH|__GFP_ATOMIC|__GFP_KSWAPD_RECLAIM)
#define GFP_KERNEL  (__GFP_RECLAIM | __GFP_IO | __GFP_FS)
#define GFP_KERNEL_ACCOUNT (GFP_KERNEL | __GFP_ACCOUNT)
#define GFP_NOWAIT  (__GFP_KSWAPD_RECLAIM)
#define GFP_NOIO    (__GFP_RECLAIM)
#define GFP_NOFS    (__GFP_RECLAIM | __GFP_IO)
#define GFP_USER    (__GFP_RECLAIM | __GFP_IO | __GFP_FS | __GFP_HARDWALL)
#define GFP_DMA     __GFP_DMA
#define GFP_DMA32   __GFP_DMA32
  • 大家常用的应该有GFP_KERNEL,它是由三个flag组成的。__GFP_RECLAIM=可回收,__GFP_IO=有IO的操作,有IO的操作就可能会导致sleep
  • GFP_ATOMIC: 它也是有三个组成,__GFP_HIGH=高优先级等等
  • 大家有兴趣可以看看Gfp.h文件的注释就清楚了。
void *__kmalloc(size_t size, gfp_t flags)
{struct kmem_cache *s;void *ret;if (unlikely(size > KMALLOC_MAX_CACHE_SIZE))return kmalloc_large(size, flags);s = kmalloc_slab(size, flags);if (unlikely(ZERO_OR_NULL_PTR(s)))return s;ret = slab_alloc(s, flags, _RET_IP_);trace_kmalloc(_RET_IP_, ret, size, s->size, flags);kasan_kmalloc(s, ret, size, flags);return ret;
}
  • 如果size大于Kmalloc申请的最大size(4K),则调用kmalloc_large去申请。也就是申请太大的内存,就不用直接找我slab了,直接去找buddy拿吧
  • 通过kmalloc_slab去获取对应大小的kmem_cache缓冲池
  • 调用slab_alloc从对应的kmem_cache中去申请一个object,返回去即可。
  • 所以说来说去,还是从slab去申请内存,里面的内存就和上一节的内存一样了。

如果size大于4K,每个平台,每个版本,每个slab的实现大小不一样。

static __always_inline void *kmalloc_large(size_t size, gfp_t flags)
{unsigned int order = get_order(size);return kmalloc_order_trace(size, flags, order);
}void *kmalloc_order(size_t size, gfp_t flags, unsigned int order)
{void *ret;struct page *page;flags |= __GFP_COMP;page = alloc_pages(flags, order);ret = page ? page_address(page) : NULL;kmemleak_alloc(ret, size, 1, flags);kasan_kmalloc_large(ret, size, flags);return ret;
}

很直接,如果大小太大,不用找我slab了,我是小内存申请分配器,反正我的内存也是从buddy拿的,你直接去找buddy拿对应order的页。

struct kmem_cache *kmalloc_slab(size_t size, gfp_t flags)
{unsigned int index;if (size <= 192) {if (!size)return ZERO_SIZE_PTR;index = size_index[size_index_elem(size)];} else {if (unlikely(size > KMALLOC_MAX_CACHE_SIZE)) {WARN_ON(1);return NULL;}index = fls(size - 1);}#ifdef CONFIG_ZONE_DMAif (unlikely((flags & GFP_DMA)))return kmalloc_dma_caches[index];#endifreturn kmalloc_caches[index];
}
  • 根据size计算出一个index,此index就是kmalloc_caches数组下标,此数据中有对应很多个大小的kmalloc_cache缓冲池
  • 如果申请的内存是从DMA ZONE去申请,就从kmalloc_dma_caches中去找对应的kmalloc_cache缓冲池
static void __init new_kmalloc_cache(int idx, slab_flags_t flags)
{kmalloc_caches[idx] = create_kmalloc_cache(kmalloc_info[idx].name,kmalloc_info[idx].size, flags, 0,kmalloc_info[idx].size);
}

kmalloc_caches数组是通过kmalloc_info数组里面的信息决定的。

const struct kmalloc_info_struct kmalloc_info[] __initconst = {{NULL,                      0},        {"kmalloc-96",             96},{"kmalloc-192",           192},        {"kmalloc-8",               8},{"kmalloc-16",             16},        {"kmalloc-32",             32},{"kmalloc-64",             64},        {"kmalloc-128",           128},{"kmalloc-256",           256},        {"kmalloc-512",           512},{"kmalloc-1024",         1024},        {"kmalloc-2048",         2048},{"kmalloc-4096",         4096},        {"kmalloc-8192",         8192},{"kmalloc-16384",       16384},        {"kmalloc-32768",       32768},{"kmalloc-65536",       65536},        {"kmalloc-131072",     131072},{"kmalloc-262144",     262144},        {"kmalloc-524288",     524288},{"kmalloc-1048576",   1048576},        {"kmalloc-2097152",   2097152},{"kmalloc-4194304",   4194304},        {"kmalloc-8388608",   8388608},{"kmalloc-16777216", 16777216},        {"kmalloc-33554432", 33554432},{"kmalloc-67108864", 67108864}
};

可以看到,系统中专门为kmalloc申请了一系列大小的kmem_cache缓冲区,平常用到的大小都会有涉及的。

大家也可以去/proc/slabinfo下去查看对应的kmalloc信息

root:/ # cat /proc/slabinfo | grep "kmalloc"
kmalloc-8192         644    663   8704    3    8 : tunables    0    0    0 : slabdata    221    221      0
kmalloc-4096        2250   2254   4608    7    8 : tunables    0    0    0 : slabdata    322    322      0
kmalloc-2048        6767   6780   2560   12    8 : tunables    0    0    0 : slabdata    565    565      0
kmalloc-1024        2374   2436   1536   21    8 : tunables    0    0    0 : slabdata    116    116      0
kmalloc-512         7034   7296   1024   32    8 : tunables    0    0    0 : slabdata    228    228      0
kmalloc-256        17285  17640    768   21    4 : tunables    0    0    0 : slabdata    840    840      0
kmalloc-128       301624 303775    640   25    4 : tunables    0    0    0 : slabdata  12151  12151      0

Kmalloc申请内存源码分析相关推荐

  1. 一文给你解决linux内存源码分析- SLUB分配器概述(超详细)

    SLUB和SLAB的区别 首先为什么要说slub分配器,内核里小内存分配一共有三种,SLAB/SLUB/SLOB,slub分配器是slab分配器的进化版,而slob是一种精简的小内存分配算法,主要用于 ...

  2. linux内存源码分析 - 内存压缩(同步关系)

    本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 概述 最近在看内存回收,内存回收在进行同步的一些情况非常复杂,然后就想,不会内存压缩的页面迁移过程中的同步关系也 ...

  3. linux内核源码剖析 博客,【Linux内存源码分析】页面迁移

    页面迁移其实是伙伴管理算法中的一部分,鉴于其特殊性,特地另行分析.它是2007年的时候,2.6.24内核版本开发时,新增碎片减少策略(the fragmentation reduction strat ...

  4. Linux brk(),mmap()系统调用源码分析3:brk()的内存申请流程

    Linux brk(),mmap()系统调用源码分析 brk()的内存申请流程 荣涛 2021年4月30日 内核版本:linux-5.10.13 注释版代码:https://github.com/Rt ...

  5. ceph bluestore 源码分析:ceph-osd内存查看方式及控制源码分析

    文章目录 内存查看 内存控制 内存控制源码分析 通过gperftools接口获取osd进程实际内存 动态设置cache大小 动态调整cache比例 trim释放内存 本文通过对ceph-osd内存查看 ...

  6. Nginx源码分析:核心数据结构ngx_cycle_t与内存池概述

    nginx源码分析 nginx-1.11.1 参考书籍<深入理解nginx模块开发与架构解析> 核心数据结构与内存池概述 在Nginx中的核心数据结构就是ngx_cycle_t结构,在初始 ...

  7. Python3.5源码分析-内存管理

    Python3源码分析 本文环境python3.5.2. 参考书籍<<Python源码剖析>> python官网 Python3的内存管理概述 python提供了对内存的垃圾收 ...

  8. java直接内存为什么快_直接内存与 JVM 源码分析

    直接内存(堆外内存) 直接内存有一种叫法,堆外内存. 直接内存(堆外内存)指的是 Java 应用程序通过直接方式从操作系统中申请的内存.这个差别与之前的堆.栈.方法区,那些内存都是经过了虚拟化.所以严 ...

  9. Spark源码分析之九:内存管理模型

    Spark是现在很流行的一个基于内存的分布式计算框架,既然是基于内存,那么自然而然的,内存的管理就是Spark存储管理的重中之重了.那么,Spark究竟采用什么样的内存管理模型呢?本文就为大家揭开Sp ...

  10. nginx源码分析—内存池结构ngx_pool_t及内存管理

    本博客( http://blog.csdn.net/livelylittlefish)贴出作者(阿波)相关研究.学习内容所做的笔记,欢迎广大朋友指正! Content 0.序 1.内存池结构 1.1 ...

最新文章

  1. Driver for device rausb0 has been compiled with version 22
  2. java-number
  3. 厉害了,Servlet3的异步处理机制
  4. 【Android】实现页面跳转
  5. 任务寄存器TR:GDT、LDT、IDT、TR、TSS之间的关系
  6. Android开发笔记(一百六十九)利用BottomNavigationView实现底部标签栏
  7. 关闭IE窗口时执行事件
  8. web前端js上传文件
  9. SVNAdmin - 好用的开源SVN管理系统
  10. 【脑图制作】万彩脑图大师教程 | 修改主题样式
  11. 最新列表!国内外核心期刊数据库收录范围汇总介绍
  12. maya中英文对照_Maya 2018 英汉速查手册
  13. 非常有用的 windows CMD 命令大全
  14. 联想拯救者笔记本(R720、y7000、y7000p)安装ubuntu无法使用无线网卡
  15. matlab视频工具箱下载,MATLAB robotics tools工具箱下载安装
  16. html5开发一个音乐播放器,HTML5开发学习(1):使用aduio标签打造音乐播放器
  17. JAVA 基因牛的繁殖
  18. LCD编程显示像素点
  19. Linux内核中的软中断、tasklet和工作队列详解
  20. 版本测试准入准出的一些标准

热门文章

  1. mysql Packet for query is too large (1185 1024)异常
  2. apache配置Options详解
  3. CocoaPods 2017最新、最快安装和使用说明
  4. c# wince 小技巧
  5. 网络转载 ! 不保证网站安全 谨慎!
  6. 收集Tomcat异常日志并发送邮件
  7. Android版-支付宝APP支付
  8. [转载] 七龙珠第一部——第077话 皮拉夫大作战
  9. oracle忘记sys密码处理
  10. 记tcp网络编程中遇到的readline()方法