内存管理-slab[代码]
主要介绍kmalloc和kfree代码流程,侧重kmalloc和kfree流程中锁使用规则,会引用到cpuset,mempolicy(内存策略),numa相关知识。如果读起来比较困难可以参考另一篇随笔《内存管理-slab[原理]》
kmalloc
kmalloc原型如下:
// /include/linuxslab_def.h
static __always_inline void *kmalloc(size_t size, gfp_t flags)
函数功能:从内核态内存中申请size字节大小的内存段返回给系统,内核态内存指的是DMA区和NORMAL区,即kmalloc不能申请HIGHTMEM区的内存(其实再64位系统中不存在HIGHTMEM区)
结合代码流程将kmalloc流程分成两个阶段,下分会详细介绍这两个阶段的内容
kmalloc第一个阶段
第一个阶段函数调用路径如图(1)

图(1)
这个阶段主要完成的功能如下:
A.根据入参size和gfp_t选择合适的kmam_cache数据结构。具体如下:size的范围是1到2的(MAX_ORDER + PAGE_SHIFT - 1)次方,2的MAX_ORDER-1次方是buddy一次alloc_pages所能申请到的最大连续物理页数(大多数系统是2^10次方),2的PAGE_SHIFT次方是每个物理页框的大小。(宗上:得到一个结论,一个slab描述符最多经过一次alloc_pages获取内存,一次free_pages释放内存,并且slab下管理的最大内存段上限是2的(MAX_ORDER + PAGE_SHIFT - 1)次方.)根据size我们只能选定特定长度的kmem_cache,但《内存管理-slab[原理]》一文中提到每一个长度的对象对应的kmem_cache有两个,一个用来管理NORMAL区或者DMA区内存,两外一个专门管理DMA区内存,因此还需要第二个参数,才能选到唯一的kmem_cache
B.根据入参flags从1中通过size筛选出来的两个kmem_cache中选择一个。具体如下:如果flags中GFP_DMA位被值位,就选择专门用来管理DMA区内存的kmem_cache.否者则选择另一个用来管理NORMAL区或者DMA区内存的kmem_cache.(gfp具体参数可以通过如下命令过滤内核源码看到大概情况:grep -r '[^_]*kmalloc[[:space:]]('|sed -nr 's/.*,[[:space:]]*(GFP.*)$/\1/gp')
C.利用local_irq_save关闭cpu的本地中断.关中断原因:接下来会访问到kmem_cache以及其下的slab描述符等数据结构,而kmalloc是允许在中断上下文调用的,所以必须关中断来对中断上下文同步。这里特意用irq_save和irq_restore来关中断,而不是用irq_disable和irq_enable来关中断原因是kmalloc允许在关中断后调用。
上面代码经过1->4和2->3就功能来说完全相同,就是完成上面的A和B两个功能。但是为什么要分两个路径?原因:1->4这个路径在通过sizeof(XXX)的形式给出入参size的情况下会提高些性能;其实只要size入参是编译器能够计算出来的常量时,都走1->4这个路径,否者走2->3这个路径(注意kmalloc原型时inline)。路径5这里完成功能C,即关中断,调用__do_cache_alloc函数,__do_cache_alloc返回后,开中断,然后根据GFP_ZERO|flags结果来判断是否对返回的对象做memset(0,...)的操作。
到此介绍完了第一阶段的函数流程,宗上:第一阶段主要动作按时序如下:选择唯一一个kmem_cache->关中断->调用__do_cache_alloc来分配对象->开中断->根据GFP_ZERO|flags结果判断是否要对分配到的对象做memset(0,...)的操作。
kmalloc第二个阶段
通过第一个阶段得出结论:实际分配对象的操作是在__do_cache_alloc中来完成的。(注意__cache_alloc和__do_cache_alloc这两个函数的名字不是偶然的,在内核编码中经常出现XXXX和__do_XXXX这种组合,其中XXXX是__do_XXXX的包裹函数,XXXX中一般用来处理同步:比如关中断,关抢占,获取自旋锁,或者检查入参合法性等等,而实际的工作时在__do_XXXX中完成)
slab分配要实现的目标:
1.如果kmalloc入参flags显示有GFP_THISNODE:只能在当前cpu的本地节点分配内存
2.如果kmalloc入参flags没有GFP_THISNODE且在中断上下文:首先在当前cpu的本地节点分配对象,如果本地节点分配失败,则依次在系统中所有的节点上分配
3.如果kmalloc入参flag没有GFP_THISNODE且不在中断上下文且进程设置了cpuset或者mempolicy,限制只能在某个或某些node节点上分配内存:1.首先根据cpuset和mempolicy来选择一个节点,在这个节点上分配对象。2.如果所选择的这个节点分配不到对象则依次fallback到cpuset和mempolicy所允许的节点集合中分配对象,如果上面集合中的所有节点都分配不到对象,则尝试第二次(这里不是完全的重新尝试,类时重新尝试一次),如果第二次分配失败,则返回失败。
4.如果kmalloc参flag没有GFP_THISNODE且不在中断上下文且没有设置cpuset或mempolicy,
如图2是第二个阶段的关键点函数以及调用路径,总的来说需要注意两点:
α:其中1,2,3是并行关系,即__do_cache_alloc先调用1,1成功分配到对象则直接返回对象(成功),否者调用,如果成功分配到对象则直接返回(成功),否者调用,如果成功分配到对象则直接返回(成功),否者返回NULL(失败)
β:路径1和2最后都汇聚到___cache_alloc_node函数,而调用路径8->9出现了递归调用,找到这个递归调用结束的条件,是理解这部分代码的关键。

图(2)
alternate_node_alloc,___cache_alloc_node, ___cache_alloc的原型如下,结合图(2)1->4和2最后都汇聚到___cache_alloc_node函数,因此这个函数是kmalloc的核心。下面我们分别介绍1,2,3三个路径上函数完成的功能。
//:/mm/slab.c 三个函数的flags字段都是由kmalloc透传过来,没有做过任何改变
static void *alternate_node_alloc(struct kmem_cache *cachep, gfp_t flags);
static void *____cache_alloc_node(struct kmem_cache *cachep, gfp_t flags,int nodeid);
static inline void *____cache_alloc(struct kmem_cache *cachep, gfp_t flags);
路径1
由于路径1经过alternate_node_alloc后和路径2汇聚,路径1讨论主要集中在函数__do_cache_alloc调用alternate_node_alloc的时机,以及alternate_node_alloc完成的工作。
路径1主要是为了处理cpuset和mempolicy。首先,cpuset和mempolicy针对内核slab内存分配都是用来限制进程上下文分配的内存,不会限制中断(包括软中断和硬中断)上下文的内存分配。
内存管理-slab[代码]的更多相关文章
- linux内存管理--slab及其代码解析
Linux内核使用了源自于 Solaris 的一种方法,但是这种方法在嵌入式系统中已经使用了很长时间了,它是将内存作为对象按照大小进行分配,被称为slab高速缓存. 内存管理的目标是提供一种方法,为实 ...
- 内存管理-slab[原理]
前言 主要讲解原理,基于2.6.32版本内核源码.本文整体思路:先由简单内存模型逐渐演进到当下通用服务器面对的内存模型,讨论每一个内存模型下slab设计需要解决的问题. 历史简介 linux内核运行需 ...
- Linux内存管理 - slab分配器和kmalloc
本文目的在于分析Linux内存管理机制的slab分配器.内核版本为2.6.31.1. SLAB分配器 内核需要经常分配内存,我们在内核中最常用的分配内存的方式就是kmalloc了.前面讲过的伙伴系统只 ...
- OC MRC之set方法内存管理(代码分析)
// // main.m // 03-set方法的内存管理 // // Created by apple on 13-8-9. // Copyright (c) 2013年 itcast. All r ...
- 内存管理-buddy[代码]
基于2.6.32内核源码分析 首选内存区和gfp描述符关系运算 64位系统默认没有开启CONFIG_HIGHMEM选项,因此只有4个内存区DMA(0),DMA32(1),NORMAL(2),MOVAB ...
- iOS性能优化之内存管理:Analyze、Leaks、Allocations的使用和案例代码
最近接了个小任务,和公司的iOS小伙伴们分享下instruments的具体使用,于是有了这篇博客...性能优化是一个很大的话题,这里讨论的主要是内存泄露部分. 一. 一些相关概念 很多人应该比较了解这 ...
- 【深入理解Linux内核架构】第3章:内存管理
3.1 概述 内存管理涵盖了许多领域: 内存中物理内存页的管理: 分配大块内存的伙伴系统: 分配小块内存的slab.slub.slob分配器: 分配非连续内存块的vmalloc机制: 进程的地址空间. ...
- iOS 内存管理
一 . 内存管理 包括内存分配 和 内存清除 1.内存管理的范围 :人和继承于NSObject类的对象都需要进行内存管理,任何非对象类型的对象(基本数据类型 如 int char float doub ...
- objective-c 语法快速过(6)内存管理原理
内存管理基本原理(最重要) 移动设备的内存极其有限(iphone 4内存512M),每个app所能占用的内存是有限制的(几十兆而已). 当app所占用的内存较多时,系统会发出内存警告,这时得回收一些不 ...
随机推荐
- cdnbest独立主控配置自定义错误页面
注:自定义错误的域名最好配置ssl,不配碰到有使用ssl的域名,错误码显示就会有问题 (1)登陆管理后台点区域列表-->配置-->错误url 解析一个域名到你的cdn主控,然后输入这个ur ...
- DOM 扩展
1.选择符API,selectors API 可以使用CSS选择符匹配查找节点 1) querySelector(selector),接受一个CSS选择符,返回调用该函数的节点后代中第一个匹配的元素 ...
- sed (未完,待续)
sed [options] 'command' file(s) options: -e<script>或--expression=<script>:以选项中的指定的script ...
- 396. Rotate Function 移动加权求和,取最大值
[抄题]: Given an array of integers A and let n to be its length. Assume Bk to be an array obtained by ...
- (转载)Ubuntu 安装GNU Scientific library(GSL)
背景: Blei的hlda的C语言实现需要使用C语言的科学计算包GSL,因此决定安装.由于在windows下安装极其繁琐,先在Linux上安装之. 系统环境: Linux version 2.6.35 ...
- git 添加分支并与远程连接
今天由于项目需要,要改版,为了不影响当前网站,所以用分支来管理 首先,在本地添加分支dev git checkout -b dev 提交远程,让同事拉取这个分支,我是直接push了,推到远程. 同事在 ...
- Eigen矩阵基本运算
1 矩阵基本运算简介 Eigen重载了+,-,*运算符.同时提供了一些方法如dot(),cross()等.对于矩阵类的运算符重载只支持线性运算,比如matrix1*matrix2是矩阵相乘,当然必须要 ...
- win10下使用wget
一.下载 官网:http://gnuwin32.sourceforge.net/packages/wget.htm 下载地址:http://downloads.sourceforge.net/gnuw ...
- day 5,格式化输出,for,while, break,continue,列表
本节内容: 1,格式化输出 2,数据类型 3,for 循环 4,while 循环 5,列表 pycharm的简单使用,设置pycharm自动生成日期和计算机用户名 ctrl+d复制一行 1,格式化输出 ...
- python+selenium—webdriver入门(二)
本文中主要介绍webdriver常见的对象定位方法: 一.对象定位的目的 二.常见的对象定位方法 一.对象定位的目的: 1.操作对象 2.获得对象的属性,如:对象的class属性.name属性等 3. ...