您的位置:首页 > 运维架构 > Linux

linux内存管理机制

2012-09-13 00:36 344 查看
一、存管理单元MMU

高性能处理器一般都会提供一个内存管理单元MMU,该单元辅助操作系统进行内存管理,提供虚拟地址和物理地址的映射、内存访问权限保护和Cache缓存控制等硬件支持。

TLB:Translation Lookaside Buffer 转换旁路缓存,是MMU的核心部件,它缓存少量的虚拟地址与物理地址的转换关系,是转换表Cache,也称为“快表”。

TTW:Translation Table walk 转换漫游,当TLB中没有缓存对应的地址转换关系时,需要通过对内存转换表的访问来获得虚拟地址与物理地址的对应关系。TTW成功后,写入TLB 。

MMU具有虚拟地址与物理地址转换,内存访问权限保护等功能,这使得linux操作系统能单独为系统的每个进程分配独立的内存空间并保证用户空间不能访问内核空间的地址,为操作系统的虚拟内存管理模块提供硬件基础。

二、linux内存分配

对有MMU的处理器而言,Linux系统的进程所能访问的内存空间达到4G。4G空间分为两部分:用户空间和内核空间。用户空间一般为0~3G,内核空间为3~4G,用户进程通常只能访问到用户空间的虚拟地址,要通过系统调用才可以访问到内核空间。每个进程的用户空间都是完全独立、互不干扰的,而内核空间是由内核映射,它不会跟着进程改变,是固定的。3~4G的内核空间中,从低到高的地址依次为:物理内存映射区--隔离带--vmalloc虚拟内存分配器--高端内存映射区--专用页面映射区--保留区。



三、内存存取

1、用户空间内存动态申请

malloc(); //用户空间动态申请内存

free(); //内存的释放

两者成对出现,谁申请,就有谁释放。

如: char *p=malloc(...);

if(p==NULL)

....;

function();

...;

free(p);

p=NULL;

对于内核而言,C库的malloc()函数通常由brk()和mmap()两个系统调用来实现。

2、内核空间内存动态申请

Linux内核空间内存涉及的函数主要包括kmalloc() 、__get_free_pages()和vmalloc()等。kmalloc() 、__get_free_pages()(及类似函数)申请的内存位于物理内存映射区,而且物理上也是连续的,它们和物理地址只有一个固定的偏移,因此存在简单的转换关系。vmalloc()在虚拟内存空间给出一块连续的内存区,实质上映射到物理空间上是不连续的,映射的转换关系相对复杂。

kmalloc()

函数原型:
#include <linux/slab.h>

     void *kmalloc(size_t size, int flags);
void kfree(void *);
size--- 分配的块的大小;flags----分配标志,用于控制Kmalloc()的行为。kmalloc()的底层依赖__get_free_pages()实现。申请的内存由kfree()释放。

size 参数
内核管理系统的物理内存,物理内存只能按页面进行分配。kmalloc 和典型的用户空间 malloc 在实际上有很大的差别,内核使用特殊的基于页的分配技术,以最佳的方式利用系统 RAM。
必须注意的是:内核只能分配一些预定义的、固定大小的字节数组。kmalloc 能够处理的最小内存块是 32 或 64 字节(体系结构依赖),而内存块大小的上限随着体系和内核配置而变化。考虑到移植性,不应分配大于 128 KB的内存。若需多于几个 KB的内存块,最好使用其他方法。
flags 参数
内存分配最终总是调用 __get_free_pages 来进行实际的分配,这就是 GFP_ 前缀的由来。
所有标志都定义在 <linux/gfp.h> ,有符号代表常常使用的标志组合。
主要的标志常被称为分配优先级,包括:
GFP_KERNEL
最常用的标志,意思是这个分配代表运行在内核空间的进程进行。内核正常分配内存。当空闲内存较少时,可能进入休眠来等待一个页面。当前进程休眠时,内核会采取适当的动作来获取空闲页。所以使用 GFP_KERNEL 来分配内存的函数必须是可重入,且不能在原子上下文中运行。
GFP_ATOMIC
内核通常会为原子性的分配预留一些空闲页。当当前进程不能被置为睡眠时,应使用 GFP_ATOMIC,这样kmalloc 甚至能够使用最后一个空闲页。如果连这最后一个空闲页也不存在,则分配返回失败。常用来从中断处理和进程上下文之外的其他代码中分配内存,从不睡眠。
GFP_USER
用来为用户空间分配内存页,可能睡眠。
GFP_HIGHUSER
类似 GFP_USER,如果有高端内存,就从高端内存分配。
GFP_NOIO
GFP_NOFS
功能类似 GFP_KERNEL,但是为内核分配内存的工作增加了限制。具有GFP_NOFS 的分配不允许执行任何文件系统调用,而 GFP_NOIO 禁止任何 I/O 初始化。它们主要用在文件系统和虚拟内存代码。那里允许分配休眠,但不应发生递归的文件系统调用。

__get_free_pages()

__get_free_pages()系列函数/宏包括 get_zeroed_page()、 __get_free_page()和__get_free_pages().这些函数/宏是linux内核本质上最底层的用于获取空闲内存的方法。最底层的内存申请总是以页为单位的。

get_zeroed_page(unsigned flags);返回一个指向新页的指针并且将该页清零。

__get_free_page(unsigned flags);返回一个指向新页的指针,但该页并不清零

__get_free_pages(unsigned flags ,unsigned int order);该函数可分配多页并返回分配内存的首地址,分配的页数为2^order,分配的页也不清零。order允许的最大值为10或11。

都调用的函数alloc_pages()函数,其可在内核空间分配,也可以在用户空间分配。alloc_pages()函数的返回值是分配第一页的描述符而非首地址。

分配内存的释放:

void free_page(unsigned long adder);

void free_pages(unsigned long adder , unsigned long order);

vmalloc()

vmalloc 是一个基本的 Linux 内存分配机制,它在虚拟内存空间分配一
4000
块连续的内存区(注意是虚拟内存空间连续,不一定物理内存连续),尽管这些页在物理内存中不连续 (使用一个单独的 alloc_page 调用来获得每个页),但内核认为它们地址是连续的。 应当注意的是:vmalloc 在大部分情况下不推荐使用。因为在某些体系上留给 vmalloc 的地址空间相对小,且效率不高。函数原型如下:

#include <linux/vmalloc.h>

void *vmalloc(unsigned long size);//分配

void vfree(void * addr); //释放
kmalloc 和 _get_free_pages 返回的内存地址也是虚拟地址,其实际值仍需 MMU 处理才能转为物理地址(-3G)。vmalloc和它们在使用硬件上没有不同,不同是在内核如何执行分配任务上:kmalloc 和 __get_free_pages 使用的(虚拟)地址范围和物理内存是一对一映射的, 可能会偏移一个常量 PAGE_OFFSET 值,无需修改页表。
而vmalloc使用的地址范围完全是虚拟的,且每次分配都要通过适当地设置页表来建立(虚拟)内存区域。 vmalloc 可获得的地址在从 VMALLOC_START 到 VAMLLOC_END 的范围中,定义在 <asm/pgtable.h> 中。vmalloc 分配的地址只在处理器的 MMU 之上才有意义。当驱动需要真正的物理地址时,就不能使用 vmalloc。 调用 vmalloc 的正确场合是分配一个大的、只存在于软件中的、用于缓存的内存区域时。注意:vamlloc
比 __get_free_pages 要更多开销,因为它必须即获取内存又建立页表。因此, 调用 vmalloc 来分配仅仅一页是不值得的。vmalloc 的一个小的缺点在于它无法在原子上下文中使用。因为它内部使用 kmalloc(GFP_KERNEL) 来获取页表的存储空间,因此可能休眠。

kmem_cache_alloc()

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

  size_t align, unsigned long flags,

  void (*ctor)(void*, struct kmem_cache *, unsigned long),

  void (*dtor)(void*, struct kmem_cache *, unsigned long))

  void *kmem_cache_alloc(struct kmem_cache *c, gfp_t flags)

kmem_cache_create/ kmem_cache_alloc是基于slab分配器的一种内存分配方式,适用于反复分配释放同一大小内存块的场合

首先用kmem_cache_create创建一个高速缓存区域,然后用kmem_cache_alloc从
该高速缓存区域中获取新的内存块

kmem_cache_alloc一次能分配的最大内存由mm/slab.c文件中的MAX_OBJ_ORDER宏
定义,在默认的2.6.18内核版本中,该宏定义为5, 于是一次最多能申请1<<5 * 4KB也就是128KB的 连续物理内存

分析内核源码发现,kmem_cache_create函数的size参数大于128KB时会调用BUG()

测试结果验证了分析结果,用kmem_cache_create分
配超过128KB的内存时使内核崩溃


 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息