Linux内核——内存管理


内存管理

内核把物理页作为内存管理的基本单位;内存管理单元(MMU,管理内存并把虚拟地址转换为物理地址)通常以页为单位进行处理。MMU以页大小为单位来管理系统中的页表。从虚拟内存的角度看,页就是最小单位。

32位系统:页大小4KB

64位系统:页大小8KB

在支持4KB页大小并有1GB物理内存的机器上,物理内存会被划分为262144个页。内核用 struct page 结构表示系统中的每个物理页。

struct page {

page_flags_t flags; /* 表示页的状态,每一位表示一种状态*/

atomic_t _count; /* 存放页的引用计数,0代表没有被引用 */

atomic_t _mapcount;

unsigned long private;

strcut address_space *mapping;

pgoff_t index;

struct list_head lru;

void *virtual; /* 页在虚拟内存中的地址,动态映射物理页 */

}

下面,我们来解释下其中的重要字段。

flags:这个字段用于存放页的状态。这些状态包括页是不是脏的,是不是被锁定在内存中等。 flag 的每一位单独表示一种状态,所以,它至少可以同时表示出32种不同的状态。

_count:这个字段存放页的使用计数,也就是这个页被引用了多少次。很奇怪,技术值变为 -1 时,就说明当前内核并没有引用这一页,于是,在新的分配中就可以使用它,注意,这个字段使用的是 -1 代表未使用,而不是 0 。

virtual:这个字段是页的虚拟地址。

mapping:这个域指向和这个页关联的address_space 对象。

private:这个根据名字就可以看得出,它指向私有数据。

内核通过这样的数据结构管理系统中所有的页,因为内核需要知道一个页是否空闲,谁有拥有这个页。拥有者可能是:用户空间进程、动态分配的内核数据、静态内核代码、页高速缓存等等。系统中每一个物理页都要分配这样一个结构体,进行内存管理。

Linux基础篇之内存管理机制

Linux内存管理之高端内存

Linux内存管理之分段机制

Linux内存管理伙伴算法

由于硬件的限制,内核并不能对所有的页一视同仁。Linux必须处理如下两种由于硬件存在缺陷而引起的内存寻址问题:

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

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

由于存在这种限制,内核把具有相似特性的页划分为不同的区(ZONE):

1)ZONE_DMA——这个区包含的页能用来执行DMA操作。

2)ZONE_NORMAL——这个区包含的都是能正常地映射网页。

3)ZONE_DMA32——同上,不过只能被32位设备访问

4)ZONE_HIGHMEM——这个区包含“高端内存”,其中的页并能不永久地映射到内核地址空间。

Linux把系统的页划分为区,形成不同的内存池,这样就可以根据用途进行分配。注意,区的划分没有任何物理意义,这只是内核为了管理页而采取的一种逻辑上的分组。用于DMA的内存必须从ZONE_DMA中进行分配,但是一般用途的内存却既能从ZONE_DMA分配,也能从ZONE_NORMAL分配。

获得页

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

structpage *alloc_pages( unsigned int gfp_mask, unsigned int order );

该函数分配 2order 个连续的物理页,并返回一个指向第一页的 page 结构体指针,如果出错就返回NULL。

void*page_address( struct page *page );

把给定的页转换成它的逻辑地址。如果无须用到 struct page,可以调用:

unsignedlong __get_free_pages( unsigned int gfp_mask, unsigned int order );

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

如果只需要一页,可以用以下两个函数:

structpage *alloc_page( unsigned int gfp_mask );

unsignedlong _get_free_page( unsigned int gfp_mask );

如果需要让返回页的内容全为0,可以使用下面这个函数

unsignedlong get_zeroed_page(unsigned int gfp_mask );

方法

描述

alloc_page(gfp_mask)

只分配一页,返回指向页结构的指针

alloc_pages(gfp_mask, order)

分配 2^order 个页,返回指向第一页页结构的指针

__get_free_page(gfp_mask)

只分配一页,返回指向其逻辑地址的指针

__get_free_pages(gfp_mask, order)

分配 2^order 个页,返回指向第一页逻辑地址的指针

get_zeroed_page(gfp_mask)

只分配一页,让其内容填充为0,返回指向其逻辑地址的指针

当不再需要页时可以使用以下函数来释放它。

void__free_pages( struct page *page, unsigned int order );

voidfree_pages( unsigned long addr, unsigned int order );

voidfree_page( unsigned long addr );

释放页时要谨慎,只能释放属于你的页。传递了错误的 struct page 或地址,用了错误的 order 值都可能导致系统崩溃。请记住,内核是完全依赖自己的。

更多详情见请继续阅读下一页的精彩内容:

  • 1
  • 2
  • 3
  • 下一页

相关内容