现在的位置: 首页 > 综合 > 正文

内存管理1(qdxx)

2013年10月05日 ⁄ 综合 ⁄ 共 2774字 ⁄ 字号 评论关闭

1. 页

内核把物理页作为内存管理的基本单位。内存管理单元(MMU)是把虚拟地址转换为物理地址的硬件。大多数32位体系结构支持4KB的页,而64位体系结构一般会支持8KB的页。内核用struct page结构表示系统中的每个物理页,在<linux/mm.h>中定义。

struct page{

   page_flags_t                flags;

   atomic_t                       _count;

   atomic_t                       _mapcount;

   unsigned long               private;

   struct address_space   *mapping;

   pgoff_t                          index;

   struct list                       lru;

   void                              *virtual;

};

flag域用来存放页的状态,包括是不是胀的,是不是锁定在内存等。

_count域存放页的引用计数--当计数值变为0时,就说明当前内核没有用该页,于是在新的分配中就可以使用它。

virual域是页的虚拟地址。

必须理解一点,page结构与物理页相关,而不是与虚拟页相关。内核仅仅用它来描述当前时刻在相关的物理页中存放的东西。它是描述物理内存本身,而不是描述包含在其中的数据。

 

2. 区

由于硬件的限制,内核把页划分为三种区(<linx/mmzone.h>):

ZONE_DMA--包含的页能用来执行DMA操作

ZONE_NORMAL--包含的页都能正常映射的页

ZONE_HIGHMEM--包含"高端内存",其中的页并不能永久地映射到内核地址空间。

在X86上,ZONE_HIGHMEM为高于896M的所有物理内存;ZONE_DMA为0-16M的物理内存。

每个区都用struct zone表示,详见<linux/mmzone.h>。内核启动期间,会初始化这三个区,代码位于<mm/page_alloc.c>。

原子操作可以保证指令以原子的方式执行,执行过程不被打断。内核提供了两组原子操作接口:一组是针对整数进行操作;另一组是针对单独的位进行操作。

3. 内存分配与释放

3.1. 获得页

内核提供了一种请求内存的底层机制,并提供了对它进行访问的接口。

标识
 描述
 
alloc_page(flags)
 只分配一页,返回指向页结构的指针
 
alloc_pages(flags, order)
 分配(1<<order)页,返回指向第一页页结构的指针
 
__get_free_page(flags)
 只分配一页,返回指向其逻辑地址的指针
 
__get_free_pages(flags, order)
 只分配(1<<order)页,返回指向第一页逻辑地址的指针
 
get_zeroed_page(flags)
 只分配一页,让其内容填充0,返回其逻辑地址的指针
 
Page_address(*page)
 把给定的页换成它的虚拟地址
 

调用__get_free_pages等函数后需要检查错误,因为内核分配内存可能会失败。

3.2. 释放页

释放页时需要谨慎,只能释放属于自己的页。传递了错误的struct page或地址,用了错误的order值,都可能导致系统崩溃。释放页的接口有:

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

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

void ree_page(unsigned long addr);

3.3. kmalloc和kfree函数

kmalloc函数可以获得以字节为单位的一块内核内存(<linux/slab.h>):

void *kmalloc(size_t size, int flags);

void kfree(const void *ptr);

该函数返回一个指向内存块的指针,所分配的内存区在物理上是连续的。这在出错时,它返回NULL。在调用kmalloc函数后,必须检查返回的是不是NULL。

Kfree函数释放由kmalloc分配出来的内存块,注意调用kfree(NULL)是安全的。

如果搜索的范围仅限于一个字,使用__ffs()和__ffz()这两个函数更好。

3.4. flags标志

这些标志分为三类:

1) 行为修饰符,表示内核应该如何分配所需内存。例如,中断程序在分配内存中的过程中不能睡眠。

2) 区修饰符,表示从哪儿(区)分配内存。

3) 类型,是组合了行为修饰符和区修饰符。

内核中常用的标志是GFP_KERNEL,这种分配可能会引起睡眠,使用的是普通优先级。另一个是GFP_ATOMIC,表示不能睡眠的内存分配,即使没有足够的连续内存块可供使用,内核可能无法释放出内存来,内核也不能让调用者睡眠。分配成功的几率比GPF_KERNEL小。GFP_DMA表示分配器必须满足从ZONE_DMA进行分配,满足某些驱动程序的需求。GFP_NOIO和GFP_NOFS,可能会引起阻塞,但它们会避免执行某些操作。用在某些低级块I/O或文件系统的代码中。

什么时候使用哪种标志,见下表:

情形
 标志
 
进程上下文,可以睡眠
 GFP_KERNEL
 
进程上下文,不可以睡眠
 GFP_ATOMIC
 
中断处理程序
 GFP_ATOMIC
 
软中断
 GFP_ATOMIC
 
Tasklet
 GFP_ATOMIC
 
需要用于DMA的内存,可以睡眠
 GFP_DMA| GFP_KERNEL
 
需要用于DMA的内存,不可以睡眠
 GFP_DDMA |GFP_ATOMIC
 

3.5. vmalloc和vfree函数

vmalloc函数分配的内存虚拟地址是连续的,而物理地址则无需连续。这也是用户空间的分配函数的工作方式。而kmalloc函数确保页在物理地址上时连续的,自然虚拟地址也是连续的。

在性能上,vmalloc函数为了把不连续的页转换为虚拟地址空间上连续的页,必须专门建立页表项。所以内核代码大多用kmalloc函数。在<linux/vmalloc.h>中定义:

void *vmalloc(unsigned long size);

void vfree(void * addr);

vmalloc函数返回一个指针,执行逻辑上连续的一块内存区,在发生错误时,函数返回NULL。vmalloc和vfree函数可能睡眠,因此,不能从中断上下文中进行调用,也不能从其他不允许阻塞的情况下进行调用。

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/wzhwho/archive/2009/05/21/4207542.aspx

抱歉!评论已关闭.