【版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet,文章仅供学习交流。请勿用于商业用途】

上一节最后说到对于小内存区的请求,假设採用伙伴系统来进行分配,则会在页内产生非常多空暇空间无法使用。因此产生slab分配器来处理对小内存区(几十或几百字节)的请求。Linux中引入Slab的主要目的是为了降低对伙伴算法的调用次数。

内核常常重复使用某一内存区。比如。仅仅要内核创建一个新的进程,就要为该进程相关的数据结构(task_struct、打开文件对象等)分配内存区。当进程结束时。收回这些内存区。由于进程的创建和撤销很频繁。linux把那些频繁使用的页面保存在快速缓存中并又一次使用。

slab分配器基于对象进行管理,同样类型的对象归为一类(如进程描写叙述符就是一类),每当要申请这样一个对象。slab分配器就分配一个空暇对象出去,而当要释放时,将其又一次保存在slab分配器中,而不是直接返回给伙伴系统。

对于频繁请求的对象。创建适当大小的专用对象来处理。对于不频繁的对象。用一系列几何分布大小的对象来处理(详见通用对象)。

Slab分配模式把对象分组放进缓冲区,为缓冲区的组织和管理与硬件快速缓存的命中率密切相关,因此。Slab缓冲区并不是由各个对象直接构成。而是由一连串的“大块(Slab)”构成,而每一个大块中则包括了若干个同种类型的对象。这些对象或已被分配。或空暇。实际上。缓冲区就是主存中的一片区域,把这片区域划分为多个块。每块就是一个Slab,每一个Slab由一个或多个页面组成,每一个Slab中存放的就是对象。

slab相关数据结构:

缓冲区数据结构使用kmem_cache结构来表示。

struct kmem_cache {
/* 1) per-cpu data, touched during every alloc/free */struct array_cache *array[NR_CPUS];
/* 2) Cache tunables. Protected by cache_chain_mutex */unsigned int batchcount;unsigned int limit;unsigned int shared;unsigned int buffer_size;u32 reciprocal_buffer_size;
/* 3) touched by every alloc & free from the backend */unsigned int flags;      /* constant flags */unsigned int num;       /* # of objs per slab *//* 4) cache_grow/shrink *//* order of pgs per slab (2^n) */unsigned int gfporder;/* force GFP flags, e.g. GFP_DMA */gfp_t gfpflags;size_t colour;           /* cache colouring range */unsigned int colour_off; /* colour offset */struct kmem_cache *slabp_cache;unsigned int slab_size;unsigned int dflags;       /* dynamic flags *//* constructor func */void (*ctor)(void *obj);/* 5) cache creation/removal */const char *name;struct list_head next;/* 6) statistics */
#ifdef CONFIG_DEBUG_SLABunsigned long num_active;unsigned long num_allocations;unsigned long high_mark;unsigned long grown;unsigned long reaped;unsigned long errors;unsigned long max_freeable;unsigned long node_allocs;unsigned long node_frees;unsigned long node_overflow;atomic_t allochit;atomic_t allocmiss;atomic_t freehit;atomic_t freemiss;/** If debugging is enabled, then the allocator can add additional* fields and/or padding to every object. buffer_size contains the total* object size including these internal fields, the following two* variables contain the offset to the user object and its size.*/int obj_offset;int obj_size;
#endif /* CONFIG_DEBUG_SLAB *//** We put nodelists[] at the end of kmem_cache, because we want to size* this array to nr_node_ids slots instead of MAX_NUMNODES* (see kmem_cache_init())* We still use [MAX_NUMNODES] and not [1] or [0] because cache_cache* is statically defined, so we reserve the max number of nodes.*/struct kmem_list3 *nodelists[MAX_NUMNODES];/** Do not add fields after nodelists[]*/
};

当中struct kmem_list3结构体链接slab,共享快速缓存。其定义例如以下:

/** The slab lists for all objects.*/
struct kmem_list3 {struct list_head slabs_partial;  /* partial list first, better asm code */struct list_head slabs_full;struct list_head slabs_free;unsigned long free_objects;unsigned int free_limit;unsigned int colour_next;   /* Per-node cache coloring */spinlock_t list_lock;struct array_cache *shared;   /* shared per node */struct array_cache **alien;    /* on other nodes */unsigned long next_reap;    /* updated without locking */int free_touched;      /* updated without locking */
};

该结构包括三个链表:slabs_partial、slabs_full、slabs_free,这些链表包括缓冲区全部slab。slab描写叙述符struct slab用于描写叙述每一个slab:

/** struct slab** Manages the objs in a slab. Placed either at the beginning of mem allocated* for a slab, or allocated from an general cache.* Slabs are chained into three list: fully used, partial, fully free slabs.*/
struct slab {struct list_head list;unsigned long colouroff;void *s_mem;     /* including colour offset */unsigned int inuse;    /* num of objs active in slab */kmem_bufctl_t free;unsigned short nodeid;
};

一个新的缓冲区使用例如以下函数创建:

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

函数创建成功会返回一个指向所创建缓冲区的指针;撤销一个缓冲区调用例如以下函数:

<span style="font-family:Microsoft YaHei;">void kmem_cache_destroy(struct kmem_cache *cachep);</span>

上面两个函数都不能在中断上下文中使用。由于它可能睡眠。

在创建来缓冲区之后,能够通过下列函数获取对象:

/*** kmem_cache_alloc - Allocate an object* @cachep: The cache to allocate from.* @flags: See kmalloc().** Allocate an object from this cache.  The flags are only relevant* if the cache has no available objects.*/
void *kmem_cache_alloc(struct kmem_cache *cachep, gfp_t flags)
{void *ret = __cache_alloc(cachep, flags, __builtin_return_address(0));trace_kmem_cache_alloc(_RET_IP_, ret,obj_size(cachep), cachep->buffer_size, flags);return ret;
}

该函数从给点缓冲区cachep中返回一个指向对象的指针。

假设缓冲区的全部slab中都没有空暇对象,那么slab层必须通过kmem_getpages()获取新的页。參数flags传递给_get_free_pages()。

<span style="font-family:Microsoft YaHei;">static void *kmem_getpages(struct kmem_cache *cachep, gfp_t flags, int nodeid);</span>

释放对象使用例如以下函数:

/*** kmem_cache_free - Deallocate an object* @cachep: The cache the allocation was from.* @objp: The previously allocated object.** Free an object which was previously allocated from this* cache.*/
void kmem_cache_free(struct kmem_cache *cachep, void *objp)
{unsigned long flags;local_irq_save(flags);debug_check_no_locks_freed(objp, obj_size(cachep));if (!(cachep->flags & SLAB_DEBUG_OBJECTS))debug_check_no_obj_freed(objp, obj_size(cachep));__cache_free(cachep, objp);local_irq_restore(flags);trace_kmem_cache_free(_RET_IP_, objp);
}

假设你要频繁的创建非常多同样类型的对象,就要当考虑使用slab快速缓存区。

实际上上一节所讲kmalloc()函数也是使用slab分配器分配的。

static __always_inline void *kmalloc(size_t size, gfp_t flags)
{struct kmem_cache *cachep;void *ret;if (__builtin_constant_p(size)) {int i = 0;if (!size)return ZERO_SIZE_PTR;#define CACHE(x) \if (size <= x) \goto found; \else \i++;
#include <linux/kmalloc_sizes.h>
#undef CACHEreturn NULL;
found:
#ifdef CONFIG_ZONE_DMAif (flags & GFP_DMA)cachep = malloc_sizes[i].cs_dmacachep;else
#endifcachep = malloc_sizes[i].cs_cachep;ret = kmem_cache_alloc_notrace(cachep, flags);trace_kmalloc(_THIS_IP_, ret,size, slab_buffer_size(cachep), flags);return ret;}return __kmalloc(size, flags);
}

kfree函数实现例如以下:

/*** kfree - free previously allocated memory* @objp: pointer returned by kmalloc.** If @objp is NULL, no operation is performed.** Don't free memory not originally allocated by kmalloc()* or you will run into trouble.*/
void kfree(const void *objp)
{struct kmem_cache *c;unsigned long flags;trace_kfree(_RET_IP_, objp);if (unlikely(ZERO_OR_NULL_PTR(objp)))return;local_irq_save(flags);kfree_debugcheck(objp);c = virt_to_cache(objp);debug_check_no_locks_freed(objp, obj_size(c));debug_check_no_obj_freed(objp, obj_size(c));__cache_free(c, (void *)objp);local_irq_restore(flags);
}

最后。结合上一节。看看分配函数的选择:

假设须要连续的物理页,就能够使用某个低级页分配器或kmalloc()。
假设想从高端内存进行分配,使用alloc_pages()。

假设不须要物理上连续的页,而不过虚拟地址上连续的页,那么就是用vmalloc。
假设要创建和销毁非常多大的数据结构,那么考虑建立slab快速缓存。

把握linux内核设计思想(十二):内存管理之slab分配器相关推荐

  1. 《Linux内核设计与实现》内存管理札记

    1.页 芯作为物理页存储器管理的基本单元,MMU(内存管理单元)中的页表,从虚拟内存的角度来看,页就是最小单位. 内核用struct page结构来标识系统中的每个物理页.它的定义例如以下: flag ...

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

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

  3. Linux内核源码分析之内存管理

    本文站的角度更底层,基本都是从Linux内核出发,会更深入.所以当你都读完,然后再次审视这些功能的实现和设计时,我相信你会有种豁然开朗的感觉. 1.页 内核把物理页作为内存管理的基本单元. 尽管处理器 ...

  4. 内存管理之slab分配器

    基本思想 与传统的内存管理模式相比, slab 缓存分配器提供了很多优点.首先,内核通常依赖于对小对象的分配,它们会在系统生命周期内进行无数次分配.slab 缓存分配器通过对类似大小的对象进行缓存而提 ...

  5. 《深入理解Linux内核》笔记5:内存管理

    本文介绍内核如何给自己分配物理内存并管理.对应<深入>第8章. 在<深入>第2章"内存寻址"(或者是我博客中的这篇文章,点这里)中,已经介绍了内核如何给自己 ...

  6. 【Linux 内核】Linux 操作系统结构 ( Linux 内核在操作系统中的层级 | Linux 内核子系统及关系 | 进程调度 | 内存管理 | 虚拟文件系统 | 网络管理 | 进程间通信 )

    文章目录 一.Linux 内核在操作系统中的层级 二.Linux 内核子系统 三.Linux 内核子系统之间的关系 一.Linux 内核在操作系统中的层级 Linux 内核 所在层级 : 整个计算机系 ...

  7. linux内核编程13期:内存管理

    内管管理子系统是Linux内核中比较复杂的一个模块,也是很多Linux开发者的"梦魇",无论是Linux新手.运维.应用开发者,还是有多年经验的驱动工程师,在学习内存管理时,面对错 ...

  8. Linux内核分析 - 网络[十二]:UDP模块 - socket

    内核版本:2.6.34 这部分内容在于说明socket创建后如何被内核协议栈访问到,只关注两个问题:sock何时插入内核表的,sock如何被内核访问的.对于核心的sock的插入.查找函数都给出了流程图 ...

  9. Linux内核分析 - 网络[十二]:UDP模块 - 收发

    内核版本:2.6.34 UDP报文接收        UDP报文的接收可以分为两个部分:协议栈收到udp报文,插入相应队列中:用户调用recvfrom()或recv()系统调用从队列中取出报文,这里的 ...

最新文章

  1. 可视化应用实战案例:metacoder-相关进化树图的绘制
  2. python opencv 批量将视频转化为图片
  3. 页面缓存导致数据错误
  4. 《好未来编程题》 输入n个整数,输出出现次数大于等于数组长度一半的数
  5. sql中数据类型的转换(自己写比较累哈,偷偷懒,转下别人的)
  6. ScreenFlow for mac(屏幕录像软件)
  7. 2022年武汉CMMI3-CMMI5认证企业名录
  8. 怎么在Windows系统中制作Mac系统U盘启动盘?
  9. 考研作息时间安排表(19通信考研党)
  10. 苹果个人开发者账号出售_国内苹果企业级开发者账号申请需要多久
  11. 解决数据库数据粘贴到excel中换行、换列问题
  12. ERROR in ./node_modules/element-plus/es/components/menu-item-group/style/css2.mjs 2:0-54
  13. 滚动抽奖html怎么做的,抽奖.html
  14. com lofter android,LOFTER
  15. flutter packages get 慢 解决方案
  16. python图片批量处理(水印、重命名)
  17. python日记_递归
  18. 仿微信、微博发朋友圈,文字+图片+视频
  19. 大时代势不可挡,隔行扫描已经消失。BT.709色彩空间也开始离我们远去
  20. 妈妈再也不用担心我的面试,满满干货指导

热门文章

  1. android wifi工作流程
  2. 过桥问题——图论解法
  3. 常用的 WEB 服务器
  4. 51nod 1575 Gcd and Lcm
  5. 安卓活动间的传值问题
  6. Mysql允许外网接入
  7. Mongodb 笔记04 特殊索引和集合、聚合、应用程序设计
  8. 调用python 报R6034 错误
  9. button-xml 中android:clickable=false 属性
  10. BNUOJ 4215 最长公共连续子序列