内存管理-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所占用的内存较多时,系统会发出内存警告,这时得回收一些不 ...
随机推荐
- springboot整合zookeeper
在springboot中所有的整合都是以bean的形式注入对象,从数据库coon.redis conn.再到整合的zookeeper,依然是依照bean注入连接对象,通过zookeeper api对z ...
- Pandas聚合
数据聚合 import pandas as pd from pandas import Series import numpy as np # 准备数据 df = pd.DataFrame([[-0. ...
- 用归并排序或树状数组求逆序对数量 poj2299
题目链接:https://vjudge.net/problem/POJ-2299 推荐讲解树状数组的博客:https://blog.csdn.net/int64ago/article/details/ ...
- oracle 表空间创建和删除
oracle数据库:数据库对象以及表数据都存储在表空间中,创建用户时可以指定对应的表空间.这样用户可以在各自的表空间中操作数据,互不干扰. 1. 表空间创建 若不清楚表空间对应文件的路径,可以登录系统 ...
- [leetcode]16. 3Sum Closest最接近的三数之和
Given an array nums of n integers and an integer target, find three integers in nums such that the s ...
- Linux驱动之输入子系统简析
输入子系统由驱动层.输入子系统核心.事件处理层三部分组成.一个输入事件,如鼠标移动.键盘按下等通过Driver->Inputcore->Event handler->userspac ...
- 转载:C# 将引用的DLL文件放到指定的目录下
当软件引用的DLL比较多的时候,全部的DLL都放在exe同目录下,显得比较乱,如果能把dll放到响应的文件夹下面,就方便很多 下面是解决该问题的一种方法: 右键点击项目:属性->设置,项目会生成 ...
- Maven依赖及范围
一.依赖范围(scope): 共5种,compile (编译).test (测试).runtime (运行时).provided.system compile:编译依赖范围,在编译,测试,运行时都需要 ...
- 深入C#的String类
- centos7 go ENV 部署
1.wget官网下载go 官网https://golang.org/dl/ 2.解压 tar -C /usr/local -xzf go$VERSION.$OS-$ARCH.tar.gz 3.配置环境 ...