Linux 内存管理分析

    在内核里分配内存可不像在其他地方分配内存那么容易,从根本上讲是内核本身不能像用户那样奢侈的使用内存. 内核与用户空间不同,它不具备这种能力,它不支持简单便捷的内存分配方法,比如内核一般不能睡眠.

     接下来让看看内核是内核获取内存的方法,哈哈,在深入了解实际分配接口之前,让我们先了解一下内核是如何管理内存的:

1)页:内核把物理页作为内存管理的基本单元.尽管处理器的最小可寻址单位通常是字(甚至是字节),但是MMU(内存管理单元,管理内存并把虚拟地址映射到物理地址的硬件),通常以页为单位来进行处理.正如所说的,MMU以页大小为单位来管理系统中的大小,(这也是页表的由来).从虚拟内存的角度来看,页就是最小单位...体系结构的不同,页的大小也是不同的,大多数32为体系架构支持4KB的页,而64位的体系架构一般会支持8KB的页.

内核中用 struct page 结构体表示系统中的每个物理页,该结构位于内核目录的<linux/mm_types.h>中, 下面是我简化的一个 struct page结构体,仅供参考

struct   page{

              unsigned long                flags;

              atomic_t                            _count;

              atomic_t                             _mapcount;

              unsigned long                  private;

               struct address_space     *mapping;

               pgoff_t                                index;

               struct list_head                 lru;

               void                                  *virtual;

}

flags :存放页的状态,包括页是不是脏的或是锁定到内存中的,flags的每一个单独表示一种状态,页就是说它至少可以表示32中状态.标志定义在<linux/page-flags.h>

_count:存放页的引用计数,页就是这个页被引用的多少次,当计数为-1时,表示当前内核并没有引用这一页,于是新的分配中就可以使用它了,内核代码一般不直接检查该域,而是使用一个函数page_count()进行检查,该函数的参数就是struct page 结构,返回0表示页是空闲的,返回一个正整数表示页正在使用,

一个页可以由页缓存使用(这时mapping域指向和这个页关联的address_space对象)或者作为私有结构(由private指向) 或者作为进程页表中的映射.

virtual 域是页的虚拟地址,它就是页在虚拟地址中的地址,

必须要理解的一点是page结构是与物理页相关,而并非与虚拟页相关

2)区:

         由于硬件的限制,内核不能对所有的页一视同仁,由于有些页位于内存中特定的物理地址上,所以不能将其用于一些特定的任务,由于这些所谓的限制,所以内核将不同的页划分为不同的区,内核使用区对具有相似特性的页进行分组,linux必须处理如下两种由于硬件存在缺陷而引起的内存寻址问题:

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

二,一些体系结构的内存的物理地址比虚拟寻址范围大的多.这样就有一些内存不能永久的映射到内核空间.

        因为存在这些制约条件,Linux主要使用了四个区:(到底哪四个区呢?,哈哈,详见下面)

(1)  ZONE_DMA   -这个区包含的页能够用来执行DAM操作.

(2) ZONE_DAM32 -这个区包含的页与ZONE_DAM类似,都可用来执行DMA操作; 不同的地方在于,这些页面只能被32位的设备访问,在某些体系结构中,该区将比ZONE_DMA更大.

(3) ZONE_NORMAL - 这个区包含的都是能正常映射的页.

(4) ZONE_HIGHEM -这个区包含"高端内存",其中的页并不能永久的映射到内核地址空间,这些区在<linux/mmzone.h>中定义

       区的实际使用与分布是与体系结构相关的,例如在某些体系结构中在内存的任何地方执行DMA都没有问题,在这些体系结构中ZONE_DMA为空,ZONE_NORMAL就可以直接用于分配,在x86体系结构中,ZONE_DMA包含的页都是在0~16M的内存范围里.

        ZONE_HIGEM的工作方式也差不多,能否直接映射取决与具体的体系结构,32位x86系统上,ZONE_HIGEM的为高于896MB的所有物理内存,但是在其他的体系结构中,由于内存的所有地址空间都能够被映射,所以ZONE_HIGEM为空,

      ZONE_HIGEM所在的内存为高端地址空间,系统的其余内存就是所谓的低端地址空间,

x86-32上的区

区                                            描述                                                              物理地址

ZONE_DMA                        DMA使用的页                                                 <16M

ZONE_NORMAL                正常可寻址的页                                             16~896MB

ZONE_HIGHEM                  动态映射的页                                                 >896MB

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

<一> 获得页的方法:

内核提供了一种请求内存的底层机制,并提供了对它进行访问的几个接口,所有的这些接口都是以页为单位分配内存,定义在<linux/gfp.h>中,最核心的函数是:

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

该函数是以2^order 次幂分配连续页的物理地址,返回的是指向第一个页的第一个page结构体;出错返回NULL

2) void* page_address(struct page *page)

返回参数struct page 的逻辑地址

3) unsigned long _get_free_pages( gfp_t gfp_mask, unsigned int order);

该函数与alloc_pages()作用相同,不过它直接返回的所请求的第一个页的逻辑地址,因为也是连续的,所以其他页也会紧随其后.

如果你只需要获得一页,可以使用如下函数进行

4) struct page* alloc_page(gfp_t gfp);

5) unsigned long  _get_free_page(gfp_t gfp_mask);

获得填充为0 的页:

unsigned long get_zeroed_page(unsigned int gfp_mask);

<二> 释放页

1) void _free_pages(struct page *page , unsigned int order);

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

3)void free_page(unsigned long addr);

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

看个例子吧(最直观)

unsigned long page;

page = _get_free_pages( GFP_KERNEL,  3);

if( !page){

   /* 没有足够的内存,你必须处理这种错误*/

}

/* "page" 现在指向8个连续页中第一个页的逻辑地址*/

使用完成后释放这3个页:

free_pages( page, 3);

当你需要以页为单位的一簇连续的物理页时,尤其是你需要一两个页时,这些低级的页函数很实用,但是大多数情况在写驱动时我们都是以字节为单位分配的,内核提供的函数是kmalloc();

下节详细描述SLAB层..........................................

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值