kmap函数:
    把某块高端内存映射到页表,然后返回给用户一个填好vitual字段的page结构
    建立永久地址映射,不是简单的返回virtual字段的page
ioremap:
    驱动程序无法直接访问io物理地址,所以ioremap是为了使将其映射到虚拟内存,然后直接像访问内存那样访问io
    当开启了CONFIG_HIGHMEM时,能操作大于896M的RAM
    所以当物理内存大于896M且内核开启了CONFIG_HIGHMEM,ioremap传入的phys_addr参数可以为高端的ram地址
kmalloc则是slab机制来分配内核对象
kzalloc zeroes the memory before returning a pointer
kcalloc allocates memory for an array, it is not a replacement for kmalloc :
 void *kcalloc(size_t n, size_t size, gfp_t flags)
vmalloc is the same as kmalloc, except it allocates memory that is only virtually contiguous. The underling physical memory can be discontiguous.

Linux内核中内存的管理不像在内核外这么简单。和用户空间最大的不同是内核的内从空间不像用户空间那么容易得到,并不是总能轻易的得到想要的内存。

页:

内核最基本的内存管理单元就是页(page),因为MMU管理的内存基本单位是page,其维护着提供虚拟地址到物理地址转换的页表。

内核使用如下数据结构表述page:

struct page {

unsigned long         flags; //the status of the page,eg if the page is dirty,is locked

atomic_t              _count; //the references to this page

atomic_t              _mapcount;

unsigned long         private;

struct address_space  *mapping;

pgoff_t               index;

struct list_head      lru;

void                  *virtual;//the page's virtual space

};

需要注意的page数据结构是对机器中所有物理内存的描述并关心页中具体的数据,它只关心诸如page是否空闲,谁在占用这个page等等。

区:

内核将整个内存分为三个区(zone)。

ZONE_DMA--可进行DMA的内存空间,在x86中是低于16M的地址空间

ZONE_NOMAL--在x86中是16M-896M的空间,这部分空间是永久直接映射到内核地址空间的

ZONE_HIGHMEM--动态映射的地址空间

为什么要分区呢?因为在一些体系(如x86)中只有部分内存是可以直接映射的,对x86在896M以上的内存都是动态映射的,所以内核对内存分区描述显得必要,DMA分区是能够进行DMA操作的内存空间。

内核页分配接口:

内核提供若干以page为分配单元的内存分配函数。

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

分配2的order次方个连续的页面,返回指向第一个page的指针。当然在实际中我们一般针对分配的内存的虚拟地址进行操作,下面这个

void * page_address(struct page *page);

返回page的逻辑地址指针。

当然为了方便内核也提供直接返回虚拟地址指针的函数,实际就是包装了这两个函数:

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

还有直接分配单个page的函数,实际就是order参数为0的版本:

struct page * alloc_page(gfp_t gfp_mask) ;

unsigned long __get_free_page(gfp_t gfp_mask);

如果你想获得的页面全部被初始化为0,内核提供这个调用:

get_zeroed_page(gfp_mask);

工作方式和__get_free_pages相同,除了该函数会将内核空间初始化为0。

这些函数都有相对应的页面释放函数,原型如下:

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

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

void free_page(unsigned long addr);

更常用的分配接口:

除了原始的以页为单元的内存分配函数,内核还提供更为方便的和C语言中接口类似的分配接口,最常用的就是kmalloc函数:

void * kmalloc(size_t size, gfp_t flags);

该接口返回至少size字节的物理连续的内存空间。用法和c语言malloc函数一样。

当然还有类似free的内存释放函数

void kfree(const void *ptr);

内核还提供vmalloc函数,用法和kmalloc一样,唯一的不同的vmalloc不保证分配的内存是物理连续的。

怎么选择用哪个分配函数呢?大多数情况下,我们需要分配的内存并没有需要是物理连续的,除了外设操作的内存需要内存连续,因为对很多外设来说并没有逻辑内存的概念,他只能看到和操作物理内存。其他情况下,我们并没有一定要使用连续物理内存的需要。然而事实是,大多是情况下,内核中还是使用kmalloc来分配内存,这基本上是基于效率考虑。因为vmalloc调用为了使不连续物理内存的逻辑地址连续会有很多附加对页表的操作,所以除了必须(需要使用相当大的内存空间),一般情况直接使用kmalloc函数。

vmalloc也有对应的vfree函数。

gfp_mask参数:

gfp_mask参数可以设置很多值,一下是各个取值的用处(直接引用至LKD):

GFP_ATOMIC  The allocation is high priority and must not sleep. This is the flag to use in interrupt handlers, in bottom halves, while holding a spinlock, and in other situations where you cannot sleep.

GFP_NOWAIT  Like GFP_ATOMIC, except that the call will not fallback on emergency memory pools. This increases the liklihood of the memory allocation failing.

GFP_NOIO  This allocation can block, but must not initiate disk I/O. This is the flag to use in block I/O code when you cannot cause more disk I/O, which might lead to some unpleasant recursion.

GFP_NOFS  This allocation can block and can initiate disk I/O, if it must, but it will not initiate a filesystem operation. This is the flag to use in filesystem code when you cannot start another filesystem operation.

GFP_KERNEL  This is a normal allocation and might block. This is the flag to use in process context code when it is safe to sleep. The kernel will do whatever it has to do to obtain the memory requested by the caller. This flag should be your default choice.

GFP_USER  This is a normal allocation and might block. This flag is used to allocate memory for user-space processes.

GFP_HIGHUSER  This is an allocation from ZONE_HIGHMEM  and might block. This flag is used to allocate memory for user-space processes.

GFP_DMA  This is an allocation from ZONE_DMA. Device drivers that need DMA-able memory use this flag, usually in combination with one of the preceding flags.

使用SLAB缓存分配:

当需要频繁的针对特定数据结构分配和释放内存时,为了避免反复内存分配和释放的开销,Linux内核提供了强大的内存缓存分配策略,即SLAB缓存分配。(实际上kmalloc函数就是建立在slab分配器上的!)

每个slab包含一定数量的对象,即需要被缓存的数据结构,每个slab都有三个状态:full,partial,empty。当试图从slab中分配一个object时,内核优先从partial的slab中获取对象,不行则从empty slab中,如果全部是full则创建一个新的slab。内核中管理slab缓存的组成主要有cache,slab,object.

cache在内核中使用 kmem_cache结构来描述,包含三个链表--—slabs_full, slabs_partial, 以及slabs_empty。这些list中就包含相应状态的slab,slab在内核中的描述如下:

struct slab {

struct list_head  list;       /* full, partial, or empty list */

unsigned long     colouroff;  /* offset for the slab coloring */

void              *s_mem;     /* first object in the slab */

unsigned int      inuse;      /* allocated objects in the slab */

kmem_bufctl_t     free;       /* first free object, if any */

};

下列函数可以创建一个新的cache:

struct kmem_cache * kmem_cache_create(const char *name, size_t size, 

                                                                   size_t align, unsigned long flags, void (*ctor)(void *));

各个参数说明如下:

const char *name:cache的名称。

size_t size:cache中需要缓存的object的大小。

size_t align:slab中第一个object的偏移,一般0即可,使用标准对齐方式。

unsigned long flags:可以为缓存分配执行一些附加操作,没有的话直接0即可。

void (*ctor)(void *):cache的构造函数,你可以赋值一个函数地址,当新的page加入cache后一定会执行该构造函数。可以复赋值为NULL。

销毁一个cache时使用

int kmem_cache_destroy(struct kmem_cache *cachep);

内核提供两个函数负责从指定的cache获取和交还内存:

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

void kmem_cache_free(struct kmem_cache *cachep, void *objp);

linux内核申请内存函数的更多相关文章

  1. (笔记)Linux内核中内存相关的操作函数

    linux内核中内存相关的操作函数 1.kmalloc()/kfree() static __always_inline void *kmalloc(size_t size, gfp_t flags) ...

  2. 24小时学通Linux内核之内存管理方式

    昨天分析的进程的代码让自己还在头昏目眩,脑子中这几天都是关于Linux内核的,对于自己出现的一些问题我会继续改正,希望和大家好好分享,共同进步.今天将会讲诉Linux如何追踪和管理用户空间进程的可用内 ...

  3. Linux内核笔记--内存管理之用户态进程内存分配

    内核版本:linux-2.6.11 Linux在加载一个可执行程序的时候做了种种复杂的工作,内存分配是其中非常重要的一环,作为一个linux程序员必然会想要知道这个过程到底是怎么样的,内核源码会告诉你 ...

  4. Linux内核之内存管理

    Linux内核之内存管理 Linux利用的是分段+分页单元把逻辑地址转换为物理地址; RAM的某些部分永久地分配给内核, 并用来存放内核代码以及静态内核数据结构; RAM的其余部分称动态内存(dyna ...

  5. Linux内核中内存cache的实现【转】

    Linux内核中内存cache的实现 转自:http://blog.chinaunix.net/uid-127037-id-2919545.html   本文档的Copyleft归yfydz所有,使用 ...

  6. Linux内核的ioctl函数学习

    Linux内核的ioctl函数学习 来源:Linux公社  作者:Linux 我这里说的ioctl函数是在驱动程序里的,因为我不知道还有没有别的场合用到了ioctl, 所以就规定了我们讨论的范围.为什 ...

  7. Linux内核空间内存申请函数kmalloc、kzalloc、vmalloc

    我们都知道在用户空间动态申请内存用的函数是 malloc(),这个函数在各种操作系统上的使用是一致的,对应的用户空间内存释放函数是 free(). 注意:动态申请的内存使用完后必须要释放,否则会造成内 ...

  8. Linux内核空间内存申请函数kmalloc、kzalloc、vmalloc的区别【转】

    转自:http://www.th7.cn/system/lin/201606/167750.shtml 我们都知道在用户空间动态申请内存用的函数是 malloc(),这个函数在各种操作系统上的使用是一 ...

  9. Linux内核之内存管理完全剖析

    linux虚拟内存管理功能 ? 大地址空间:? 进程保护:? 内存映射:? 公平的物理内存分配:? 共享虚拟内存.实现结构剖析   (1)内存映射模块(mmap):负责把磁盘文件的逻辑地址映射到虚拟地 ...

随机推荐

  1. PHP初学留神(四)

    这周去听了Google的演讲,从Idea到Code的商业宣传.不过因为是头一次听英文演讲,心里还是很舒服.这周主要做的是Bootstrap前端美化,这个框架也比较好玩.在php上面花的时间相对少了,也 ...

  2. Delphi 文字跑马灯

    //跑马灯 procedure Tfr_Main.tme_TitleTimer(Sender: TObject); var strTrim: Widestring; begin strTrim := ...

  3. 整理sed实战修改多行配置技巧

    老男孩老师有关sed实战技巧分享,来自课堂教学内容实战1.在指定行前插入两行内容,分别为oldboy和oldgirl.提示:被修改的文件内容必须要大于等于2行 1 sed -i '2 ioldboy\ ...

  4. poj 2187 Beauty Contest

    Beauty Contest 题意:给你一个数据范围在2~5e4范围内的横纵坐标在-1e4~1e4的点,问你任意两点之间的距离的最大值的平方等于多少? 一道卡壳凸包的模板题,也是第一次写计算几何的题, ...

  5. 蓝桥杯——FJ字符串

    FJ在沙盘上写了这样一些字符串: A1 = "A"1 A2 = "ABA"3 A3 = "ABACABA"7 A4 = "ABAC ...

  6. 对于shell脚本参数获取时的一点小技巧

    问题如下: 根据脚本参数的个数$#进行一个循环,在依次输出每个参数$1 $2 $3...... 我有一个循环变量i $i 取到这时的i为1,我想使用这个1再去调用$1,也是就是打印出第一个参数 就是$ ...

  7. Provider Communication with Apple Push Notification Service

    This chapter describes the interfaces that providers use for communication with Apple Push Notificat ...

  8. C#解压、压缩RAR文件

    using System; using System.Collections.Generic; using System.Text; using System.IO; using Microsoft. ...

  9. loadrunner 一个诡异问题

    最近使用loadrunner压测一个项目的时候,发现TPS波动巨大.且平均值较低.使用jmeter压测则没有这个问题.经过多方排查发现一个让人极度费解的原因: 原脚本: //脚本其他代码...... ...

  10. STL--自定义类型的排序

    STL的排序太坑了,尤其是在VS2010上重载sort函数的第三个比较参数的时候. invalid operator < 这个错在写多关键字排序的时候就没有停止过. 本来想查书解决,结果各种重载 ...