Linux内存管理宏观篇(六)物理内存:分配小内存块

简介: Linux内存管理宏观篇(六)物理内存:分配小内存块

前言

前面的关于内存的部分分配是伙伴系统算法,这个是基于以页面为最小单位,一个页面还是蛮大的,像很多嵌入式设备对于内存的请求分配可能只需要几十个字节,这样如果给别人一个页,也就太浪费了。

进一步,就算你是个大方的好人,但是如果有很多这样的请求,那对于内存的浪费是不是很可怕。就必须要有一种分配更小内存的方法:slab机制。

1、slab机制的必要性和大概样子

当然可能你在这个内存的学习过程中,还遇到过slob、slub。这是由slab机制针对特殊场景演化出来的。

slub:大型服务器

slob:嵌入式系统

这样的原因是因为slab机制存在两个问题:

  • 使用的元数据开销比较大,这个元数据是对于数据的管理成本。
  • slab分配器的代码量和复杂度很高

(对于元数据,你可能需要有点认识,这个第一次看我是在学数据结构中见到的,这个元数据的含义我决定写一篇解读一下)

这里我们还是基于slab机制来讲解。

不知道你在面对这个小内存分配有没有想到什么招数?根据伙伴系统的联想,我们可以在这里构建一个以2的order字节大小的内存分配机制。这个是kmalloc的机制思想。

这里slab是以数据结构为对象进行内存池的构建,比如内核常见的struct mm_struct数据结构、进程控制块mm_struct。slab就建立一个mm_struct的对象缓存池,这样在需要的时候就直接拿出来,也不会存在多余的内存浪费,速度也快。你想想要是基于其他的,你给申请者一片内存,我还需要切割成小字节的,然后再使用,又浪费又慢。

(还有个问题,就是页面分配器去申请物理页面,会产生阻塞、睡眠的可能,对于这个速度不稳定)

所以slab机制,解决了内存浪费的问题,也保证了内存分配速度的问题。

2、slab分配的核心思想

从上面大概认识了slab就是个分配小内存的机制,同时这个分配不是基于内存大小的,而是基于数据机构对象的。知道这个是干啥的,但是对于这个具体分配的过程我们这里来看看。

最核心的就是在空闲时建立缓存对象池。(挺好奇这个空闲时创建的机制是怎么运转的),建立的这个对象缓存池包括本地也包括共享的对象缓存池。

2.1、本地缓存池

本地缓存池是在创建每个slab描述符时,就为每个cpu创建一个本地的缓存池。(这个描述符就是数据结构)本地可以减少多核cpu之间的锁竞争。

共享对象缓存池是所有共享的,本地没有时,就从共享缓存池中搬移一批对象到本地缓存池。(共享缓存池是多久建立的?)

其中涉及到三个概念

2.2、slab描述符

struct kmem_cache数据结构是slab分配器中的核心数据结构,我们把它称为slab描述符。

struct kmem_cache数据结构的定义如下。

[include/linux/slab_def.h]
0 /*
1 * kmem cache数据结构的核心成员
2 */
3 struct kmem_cache {
4  struct array_cache __percpu *cpu_cache;
7   unsigned int batchcount;
8   unsigned int limit;
9   unsigned int shared;
11  unsigned int size;
12  struct reciprocal_value reciprocal_buffer_size;
15 unsigned int flags;
16 unsigned int num;
20 unsigned int gfporder;
23 gfp_t allocflags;
25 size_t colour;
26 unsigned int colour_off;
27 struct kmem_cache *freelist_cache;
28 unsigned int freelist_size;
31 void (*ctor)(void *obj);
34 const char *name;
35 truct list_head list;
36 int refcount;
37 int object_size;
38 int align;
41 struct kmem_cache_node *node[MAX_NUMNODES];
42    }

每个slab描述符都由一个struct kmem_cache数据结构来抽象描述。

cpu_cache:一个Per-CPU的struct array_cache数据结构,每个CPU一个,表示本地CPU的对象缓冲池。

batchcount:表示当前CPU的本地对象缓冲池array_cache为空时,从共享的缓冲池或者slabs_partial/slabs_free列表中获取对象的数目。

limit:当本地对象缓冲池的空闲对象数目大于limit时,会主动释放batchcount个对象,便于内核回收和销毁slab。

shared:用于多核系统。

size:对象的长度,这个长度要加上align对齐字节。

flags:对象的分配掩码。

num:一个slab中最多可以有多少个对象。

gfporder:一个slab中占用2^gfporder个页面。

colour:一个slab中有几个不同的cache line。

colour_off:一个cache colour的长度,和L1缓存行大小相同。

freelist_size:每个对象要占用1字节来存放freelist。

name:slab描述符的名称。

object_size: 对象的实际大小。

align:对齐的长度。

node:slab节点,在NUMA系统中每个节点有一个struct kmem_cache_node数据结构。

在ARM Vexpress平台中,只有一个节点。

struct array_cache数据结构定义如下。

struct array_cache {
unsigned int avail;
unsigned int limit;
unsigned int batchcount;
unsigned int touched;
void *entry[];
};

slab描述符给每个CPU都提供一个对象缓存池(array_cache)。

batchcount/limit:和struct kmem_cache数据结构中的语义一样。

avail:对象缓存池中可用的对象数目。

touched:从缓冲池移除一个对象时,将touched置1;而收缩缓存时,将touched置0。

entry:保存对象的实体。

2.3、slab

一个slab的组成如图7.17所示,它由1个或者多个(2的order次方)连续的物理页面组成。注意:这是连续的物理页面。

(注意这里是gforder)

一般根据缓存对象object大小、align大小等参数来统一计算出来究竟由多少个页面组成一个slab是最经济、最合适的。最后会计算出来,一个slab里面最多可以有多少个cache colour,这里cache colour指的cache着色区。这是在slab机制里特有的,但是slub里已经去掉了。着色区后面紧跟着freelist,用来管理后面的object的。freelist后面就是一个个object对象了。

2.4、slab机制

slab机制分两步完成。第一步是使用kmem_cache_create()函数创建一个slab描述符,使用struct kmem_cache数据结构来描述。struct kmem_cache数据结构里有几个主要的成员,一个是指向本地缓存池的指针,另一个是指向slab节点的node指针。每个内存节点有一个slab节点,通常ARM只有一个内存节点,这里就假设系统只有一个slab节点。其他是描述这个slab描述符的信息,比如这个slab的对象的大小、名字以及align等信息。

这个slab节点里有3个链表,分别是slab空闲链表、slab满链表和slab partial链表。这些链表的成员是slab,不是对象。另外,该节点里有一个指针指向一个共享缓存池,它和本地缓存池是相对的。

第二步是从这个slab描述符中去分配空闲对象。一个CPU要从这个slab描述符中分配对象,它首先去访问当前CPU对应的这个slab描述符里的本地缓存池。如果本地缓冲池里有空闲对象,就直接获取,没有其他 CPU 过来竞争。如果本地缓冲池里没有空闲对象,那么需要去共享缓冲池里查询是否有空闲对象。如果有,就从共享缓存池里搬移几个空闲对象到自己的缓存池中。

可是刚创建slab描述符时,本地缓冲池和共享对象缓冲池里都是空的,没有空闲对象,那slab是怎么建立的呢?

建立 slab 所使用的物理页面需要向页面分配器申请,这个过程可能会睡眠。如图 7.18所示,建好一个slab之后,会把这个slab添加到 slab节点的slab空闲链表里,所以slab中的3个链表的成员是slab,而不是对象。这里是通过slab的第一个页面的lru成员挂入链表中的。另外,空闲的对象会搬移到共享缓冲池和本地缓冲池,供分配器使用。

2.4、slab回收

slab回收就是slab运行的机制。当然,slab不能只分配,不用的slab还是会被回收的。如果一个slab描述符中有很多空闲对象,那么系统是否要回收一些空闲的缓存对象,从而释放内存归还系统呢?这是必须要考虑的问题,否则系统有大量的 slab 描述符,每个 slab 描述符还有大量不用的、空闲的slab对象。

slab系统有两种方式来回收内存。

使用kmem_cache_free释放一个对象。当发现本地和共享对象缓冲池中的空闲对象数目 ac->avail 大于等于缓冲池的极限值 ac->limit 时,系统会主动释放bacthcount个对象。当系统所有空闲对象数目大于系统空闲对象数目极限值,并且这个 slab没有活跃对象时,系统就会销毁这个slab,从而回收内存。

slab系统还注册了一个定时器,定时扫描所有的slab描述符,回收一部分空闲对象,达到条件的slab也会被销毁,实现函数为cache_reap()。

3、slab分配接口

上面知道了slab的分配机制,概念还是比较抽象。再结合api函数,以及其参数来看看。

创建slab描述符

struct kmem_cache *kmem_cache_create(const char *name, size_t size, size_t align,unsigned long flags, void (*ctor)(void *))

释放slab描述符

void kmem_cache_destroy(struct kmem_cache *s)

分配缓存对象

void *kmem_cache_alloc(struct kmem_cache *, gfp_t flags);

释放缓存对象

void kmem_cache_free(struct kmem_cache *, void *);

kmem_cache_create()函数中有如下参数。

name:slab描述符的名称。
size:缓存对象的大小。
align:缓存对象需要对齐的字节数。
flags:分配掩码。
ctor:对象的构造函数。

这个时候还记得描述符和对像是什么?

例如,在Intel显卡驱动中就大量使用了kmem_cache_create()创建自己的slab描述符。

[drivers/gpu/drm/i915/i915_gem.c]

voidi915_gem_load(struct drm_device *dev)
{
  dev_priv->slab =kmem_cache_create("i915_gem_object",sizeof(struct drm_i915_gem_object), 0,SLAB_HWCACHE_ALIGN,NULL);
}
void *i915_gem_object_alloc(struct drm_device *dev)
{
  #分配缓存对象
  return kmem_cache_zalloc(dev_priv->slab, GFP_KERNEL);
}

4、kmalloc机制

5、小结

待解决

问题

元数据是什么?

参考资料

《奔跑吧 Linux内核》

目录
相关文章
|
3天前
|
算法 安全 Linux
探索Linux内核的虚拟内存管理
【5月更文挑战第20天】 在本文中,我们将深入探讨Linux操作系统的核心组成部分之一——虚拟内存管理。通过剖析其关键组件和运作机制,揭示虚拟内存如何提供高效的内存抽象,支持庞大的地址空间,以及实现内存保护和共享。文章将重点讨论分页机制、虚拟内存区域(VMAs)的管理、页面置换算法,并简要分析这些技术是如何支撑起现代操作系统复杂而多变的内存需求的。
|
1天前
|
消息中间件 存储 安全
【Linux 系统】进程间通信(共享内存、消息队列、信号量)(下)
【Linux 系统】进程间通信(共享内存、消息队列、信号量)(下)
|
1天前
|
消息中间件 算法 Linux
【Linux 系统】进程间通信(共享内存、消息队列、信号量)(上)
【Linux 系统】进程间通信(共享内存、消息队列、信号量)(上)
|
8天前
|
存储 Linux 程序员
【操作系统原理】—— Linux内存管理
【操作系统原理】—— Linux内存管理
|
8天前
|
Java Linux Arthas
linux上如何排查JVM内存过高?
linux上如何排查JVM内存过高?
805 0
|
8天前
|
存储
浮点数在内存中的存储
浮点数在内存中的存储
27 0
|
8天前
|
存储
数据在内存中的存储之整数存储
数据在内存中的存储之整数存储
21 0
|
5天前
|
存储 算法 关系型数据库
实时计算 Flink版产品使用合集之在Flink Stream API中,可以在任务启动时初始化一些静态的参数并将其存储在内存中吗
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
18 4
|
7天前
|
存储 小程序 编译器
数据在内存中的存储(探索内存的秘密)
数据在内存中的存储(探索内存的秘密)
12 0