一 页

内核把物理页作为内存管理的基本单位;内存管理单元(MMU)把虚拟地址转换为物理

地址,通常以页为单位进行处理。MMU以页大小为单位来管理系统中的也表。

32位系统:页大小4KB

64位系统:页大小8KB

内核用相应的数据结构表示系统中的每个物理页:

  <linux/mm_types.h>

  struct page {}

内核通过这样的数据结构管理系统中所有的页,因此内核判断一个页是否空闲,谁有拥有这个页

,拥有者可能是:用户空间进程、动态分配的内核数据、静态内核代码、页高速缓存……

系统中每一个物理页都要分配这样一个结构体,进行内存管理。

二 区

Linux内存寻址存在问题:

一些硬件只能用某些特定的内存来执行DMA(直接内存访问)

一些体系结构其内存的物理寻址范围必须你寻址范围大得多。这样导致一些内存不能永久映射到内核空间上。

通常32位Linux内核地址空间划分0~3G为用户空间,3~4G为内核空间。当内核模块代码或线程访问内存时,

代码中的内存地址都为逻辑地址,而对应到真正的物理内存地址,需要地址一对一的映射。因此内核空间地址为3~4G,

最多只能映射到1G空间的内存,超出1G大小的内存将如何去问呢!

由于存在上述条件的限制。Linux将内核空间地址划分为三个区:

ZONE_DMA、ZONE_NORMAL和ZONE_HIGHMEM。

ZONE_HIGHMEM即为高端内存,这就是内存高端内存概念的由来。

在x86结构中,三种类型的区域如下:

  ZONE_DMA        内存开始的16MB

  ZONE_NORMAL       16MB~896MB

  ZONE_HIGHMEM       896MB ~ 结束

同样每个区包含众多页,形成不同内存池,按照用途进行内存分配。

用相应的数据结构来表示区:

  <linux/mmzone.h>

  struct zone {}

三 获取页/内存

static inline struct page *alloc_pages(gfp_t gfp_mask, unsigned int order)

该函数分配2的order次方个连续的物理页,返回指向第一个页的page结构体指针。

void *page_address(const struct page *page)

返回指向给定物理页当前所在的逻辑地址

extern unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order);

extern unsigned long get_zeroed_page(gfp_t gfp_mask);

释放:

extern void __free_pages(struct page *page, unsigned int order);

extern void free_pages(unsigned long addr, unsigned int order);

内存的分配可能失败,内存的释放要准确!

1 kmalloc

kmalloc()函数与用户空间malloc一组函数类似,获得以字节为单位的一块内核内存。

void *kmalloc(size_t size, gfp_t flags)

void kfree(const void *objp)

分配内存物理上连续。

gfp_t标志:表明分配内存的方式。如:

GFP_ATOMIC:分配内存优先级高,不会睡眠

GFP_KERNEL:常用的方式,可能会阻塞。

2 vmalloc    

void *vmalloc(unsigned long size)

void vfree(const void *addr)

vmalloc()与kmalloc方式类似,vmalloc分配的内存虚拟地址是连续的,而物理地址则无需连续,与用户空间分配函数一致。

vmalloc通过分配非连续的物理内存块,在修正页表,把内存映射到逻辑地址空间的连续区域中,虚拟地址是连续的。

是否必须要连续的物理地址和具体使用场景有关。在不理解虚拟地址的硬件设备中,内存区都必须是连续的。

通过建立页表转换成虚拟地址空间上连续,肯定存在一些消耗,带来性能上影响。

所以通常内核使用kmalloc来申请内存,在需要大块内存时使用vmalloc来分配。

四 slab层

内核中经常进行内存的分配和释放。为了便于数据的频繁分配和回收,通常建立一个空

闲链表——内存池。当不使用的已分配的内存时,将其放入内存池中,而不是直接释放掉。

Linux内核提供了slab层来管理内存的分配和释放。

频繁分配和回收必然导致内存碎片,缓存他们.

slab层得设计实现

slab层把不同的对象划分为所谓的高速缓存组。每个高速缓存组存放不同类型的对象。高速缓存划分为slab,

slab由一个或多个物理上连续的页组成。每个slab处于三种状态之一:满,部分满,空。

高速缓存,slab,对象之间的关系:

     

与传统的内存管理模式相比, slab 缓存分配器提供了很多优点。首先,内核通常依赖于对小对象的分配,

它们会在系统生命周期内进行无数次分配。slab 缓存分配器通过对类似大小的对象进行缓存而提供这种功能,

从而避免了常见的碎片问题。slab 分配器还支持通用对象的初始化,从而避免了为同一目而对一个对象重复

进行初始化。最后,slab 分配器还可以支持硬件缓存对齐和着色,这允许不同缓存中的对象占用相同的缓存行,

从而提高缓存的利用率并获得更好的性能。

slab数据结构和接口:

每个高速缓存用kmem_cache结构来表示:

struct kmem_cache {

struct kmem_list3 **nodelists;

……

}

缓存区包含三种slab:满,未满,空闲

struct kmem_list3 {

struct list_head slabs_partial; /* partial list first, better asm code */

struct list_head slabs_full;

struct list_head slabs_free;

……

};

每一个slab包含多个对象:

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;

};

相关接口:mm/slab.c

内核函数 kmem_cache_create 用来创建一个新缓存。这通常是在内核初始化时执行的,或者在首次加载内核模块时执行。

struct kmem_cache *kmem_cache_create (

  const char *name,

  size_t size,

  size_t align,

  unsigned long flags,

  void (*ctor)(void *))

name 参数定义了缓存名称,proc 文件系统(在 /proc/slabinfo 中)使用它标识这个缓存。

size 参数指定了为这个缓存创建的对象的大小,

align 参数定义了每个对象必需的对齐。

flags 参数指定了为缓存启用的选项:

  kmem_cache_create 的部分选项(在 flags 参数中指定)

  SLAB_RED_ZONE    在对象头、尾插入标志,用来支持对缓冲区溢出的检查。

  SLAB_POISON  使用一种己知模式填充 slab,允许对缓存中的对象进行监视(对象属对象所有,不过可以在外部进行修改)。

  SLAB_HWCACHE_ALIGN      指定缓存对象必须与硬件缓存行对齐。

ctor 和 dtor 参数定义了一个可选的对象构造器和析构器。构造器和析构器是用户提供的回调函数。当从缓存中分配新对象时,可以通过构造器进行初始化。

    要从一个命名的缓存中分配一个对象,可以使用 kmem_cache_alloc 函数。

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

这个函数从缓存中返回一个对象。注意如果缓存目前为空,那么这个函数就会调用 cache_alloc_refill 向缓存中增加内存。

kmem_cache_alloc 的 flags 选项与 kmalloc 的

cachep:所建立的缓存区

flags参数:

  GFP_USER 为用户分配内存(这个调用可能会睡眠)。

  GFP_KERNEL    从内核 RAM 中分配内存(这个调用可能会睡眠)。

  GFP_ATOMIC   使该调用强制处于非睡眠状态(对中断处理程序非常有用)。

  GFP_HIGHUSER      从高端内存中分配内存。

五 高端内存的映射

永久映射:可能会阻塞

  映射一个给定的page结构到内核地址空间:

  void *kmap(struct page *page)

  解除映射:

  void kunmap(struct page *page)

临时映射:不会阻塞

void *kmap_atomic(struct page *page)

六 分配函数的选择

  l  连续的物理页:kmalloc或者低级页分配器

  l  高端内存分配:alloc_pages 指向page结构指针,不是逻辑地址指针。再通过kmap()把高端地址内存映射到内核的逻辑地址空间。

  l  无需连续物理地址:vmalloc 虚拟地址连续物理地址可能不连续,相对存在性能损失

  l  频繁创建和销毁很多较大数据结构:建立slab缓存区,提高对象分配和回收性能。

Linux高端内存:
  http://ilinuxkernel.com/?p=1013
Linux slab 分配器剖析:
  https://www.ibm.com/developerworks/cn/linux/l-linux-slab-allocator/

Linux内核学习笔记——内核内存管理方式的更多相关文章

  1. linux kernel学习笔记-5内存管理_转

    void * kmalloc(size_t size, gfp_t gfp_mask); kmalloc()第一个参数是要分配的块的大小,第一个参数为分配标志,用于控制kmalloc()的行为. km ...

  2. Linux内核学习笔记-2.进程管理

    原创文章,转载请注明:Linux内核学习笔记-2.进程管理) By Lucio.Yang 部分内容来自:Linux Kernel Development(Third Edition),Robert L ...

  3. XV6学习笔记(2) :内存管理

    XV6学习笔记(2) :内存管理 在学习笔记1中,完成了对于pc启动和加载的过程.目前已经可以开始在c语言代码中运行了,而当前已经开启了分页模式,不过是两个4mb的大的内存页,而没有开启小的内存页.接 ...

  4. Linux内核设计笔记12——内存管理

    内存管理学习笔记 页 页是内核管理内存的基本单位,内存管理单元(MMU,管理内存并把虚拟地址转化为物理地址的硬件)通常以页为单位进行处理,从虚拟内存的角度看,页就是最小单位. struct page{ ...

  5. Linux System Programming 学习笔记(九) 内存管理

    1. 进程地址空间 Linux中,进程并不是直接操作物理内存地址,而是每个进程关联一个虚拟地址空间 内存页是memory management unit (MMU) 可以管理的最小地址单元 机器的体系 ...

  6. 嵌入式linux学习笔记1—内存管理MMU之虚拟地址到物理地址的转化

    一.内存管理基本知识 1.S3C2440最多会用到两级页表:以段的方式进行转换时只用到一级页表,以页的方式进行转换时用到两级页表.页的大小有三种:大页(64KB),小页(4KB),极小页(1KB).条 ...

  7. arm-linux学习笔记3-linux内存管理与文件操作

    配置好linux系统之后需要vim配置一下,有助于我们的编程,主要的配置如下 在/etc/vim/vimrc文件中 "显示行号 set number "自动缩进 set autoi ...

  8. 《C#高级编程》学习笔记----c#内存管理--栈VS堆

    本文转载自Netprawn,原文英文版地址 尽管在.net framework中我们不太需要关注内存管理和垃圾回收这方面的问题,但是出于提高我们应用程序性能的目的,在我们的脑子里还是需要有这方面的意识 ...

  9. COCOS学习笔记--Cocod2dx内存管理(三)-Coco2d-x内存执行原理

    通过上两篇博客.我们对Cocos引用计数和Ref类.PoolManager类以及AutoreleasePool类已有所了解,那么接下来就通过举栗子来进一步看看Coco2d-x内存执行原理是如何的. / ...

随机推荐

  1. git 如何恢复只是提交到本地的文件(或者commit)

    今天早上傻逼了,把四天的代码commit到了本地,然后fetch一下,然后就全没了,不过git还是挺强大的 参考:http://blog.163.com/jiams_wang/blog/static/ ...

  2. Spark Job Scheduling

    最近由于项目需要在研究spark相关的内容,形成了一些技术性文档,发布这记录下,懒得翻译了. There are some spaces the official documents didn't e ...

  3. Laxcus大数据管理系统2.0(2)- 第一章 基础概述 1.1 基于现状的一些思考

    第一章 基础概述 1.1 基于现状的一些思考 在过去十几年里,随着互联网产业的普及和高速发展,各种格式的互联网数据也呈现爆炸性增长之势.与此同时,在数据应用的另一个重要领域:商业和科学计算,在各种新兴 ...

  4. [设计模式 3] 用设计模式的眼光看MVC框架

    导读:之前一直在区分MVC和设计模式的区别,但是,既然有些人认为MVC是一种设计模式,那么它们之间肯定是有共通之处的.所以,本篇博客,就用设计模式的眼光来看MVC框架.仅是本人对于MVC的粗鄙看法,还 ...

  5. CLRS:Max_sunsequence_sum O(n*n) O(nlgn) O(n)

    #include<stdio.h>#include<stdlib.h>#include<time.h>#define ARRAY_SIZE 1000int buf ...

  6. Json.net实现方便的Json转C#(dynamic动态类型)对象

    以前需要将一段json字符串转换为C#对象时,一般都是定义一个与之对应的实体类来接收.这样做有一个很大的缺点,就是当字符串特别长,属性特别多,又有嵌套时,手敲这个实体类就非常痛苦. 比如之前做的一个接 ...

  7. ios开发之OC基础-oc小程序

    本系列的文章主要来自于个人在学习前锋教育-欧阳坚老师的iOS开发教程之OC语言教学视频所做的笔记,边看视频,边记录课程知识点.建议大家先过一遍视频,在看视频的过程中记录知识点关键字,把把握重点,然后再 ...

  8. @media screen解决移动web开发的多分辨率问题

    当今移动设备的发展已经越来越迅速,移动web开发的需求也越来越多多.许多大平台.大门户都纷纷推出了自己的移动web版网站. 随着移动设备飞速的发展,移动产品的屏幕规格越来越多.从几年前的320×240 ...

  9. 【MVC】ASP.NET MVC中实现多个按钮提交的几种方法

    有时候会遇到这种情况:在一个表单上需要多个按钮来完成不同的功能,比如一个简单的审批功能. 如果是用webform那不需要讨论,但asp.net mvc中一个表单只能提交到一个Action处理,相对比较 ...

  10. c# winform 关于DataGridView的一些操作(很全,绝对够用)

    转自:http://heisetoufa.iteye.com/blog/405317 设置字段名 设置字段值 设定单元格表示 Error图标 设定当前单元格 取得当前单元格内容 取得当前单元格的列 I ...