天天看点

[笔记分享] [OS] Linux的内存管理

Platform: msm8x60

Kernel: 2.6

1.1 介绍

在内核中分配内存不像用户空间分配内存那么简单,如果分配出错,就会导致整个系统崩溃,而且内核内存没用户空间那么奢侈。

1.2 页

内核管理内存以页为单位,这个也是基于MMU来划分的。在32位体系上, 大小为4K。用struct page表示:

[笔记分享] [OS] Linux的内存管理

1.3 区

由于硬件限制,内核因此将内存划分为几个区,不同设备访问不同区域,如下:

[笔记分享] [OS] Linux的内存管理

但是这并不代表某种用途的区一定要对应的设备才能访问。要知道的是,内核如此划分只是为了在方便在逻辑上对内存进行管理。

每个区用struct zone 表示,如下:

[笔记分享] [OS] Linux的内存管理
[笔记分享] [OS] Linux的内存管理

1.4 分配和释放页

下面这些函数都以页为最小单位进行分配,最核心函数:

[笔记分享] [OS] Linux的内存管理

gfp_mask我们一般用GFP_KERNEL。Order表示分配

1<<order

个连续的物理页,返回的为被分配的第一个页的指针。可以用下面函数转换成逻辑地址使用:

[笔记分享] [OS] Linux的内存管理

当不需要用到页像直接到的逻辑地址时,我们可用:

[笔记分享] [OS] Linux的内存管理

从代码可看出,其实__get_free_pages()只是前两个函数的组合而已。当我们只需要一页时,我们可以用下面函数:

[笔记分享] [OS] Linux的内存管理

当需要返回全是0的页面时,我们就用下面这函数:

[笔记分享] [OS] Linux的内存管理

其实也就是多传了个__GFP_ZERO参数而已。下面用表总结下:

[笔记分享] [OS] Linux的内存管理

当不要分配的这些页时,Linux也提供了相对应的函数来释放页,如下:

[笔记分享] [OS] Linux的内存管理

1.5 kmalloc

当你需要以页为单位的连续物理页时,前一小节讲的函数非常有用,但是对常用的字节为单位分配来说,我们就可以用kmalloc()。定义如下:

[笔记分享] [OS] Linux的内存管理

该函数分配至少size大小内存,所分配的物理页也是连续的,基于slab分配,后面我们会讲到。

对于这里的flags我们有必要说下,这也就是前面说的gfp_mask值。可用的行为修饰符如下:

[笔记分享] [OS] Linux的内存管理

但是我们一般不会直接用,而是用到多个的组合,所以Linux又定义了一组宏:

[笔记分享] [OS] Linux的内存管理
[笔记分享] [OS] Linux的内存管理

内核中最常用的标志是GFP_KERNEL, 这种分配可能引起睡眠。另一个是GFP_ATOMIC,这个标志不能睡眠,而且分配成功的几率比较小。至于该什么时候要使用哪种标志,可参照下表:

[笔记分享] [OS] Linux的内存管理

相应的,释放内存函数用kfree, kfree(NULL)是安全。

[笔记分享] [OS] Linux的内存管理

1.6 vmalloc

vmalloc()类似于kmalloc(),只不过前者分配的内存虚拟地址是连续,但是它的物理地址则不需要连续。

vmalloc()为了将不连续的物理页转换成虚拟地址上连续的页,需要建立专门的页表项,页必须要一个个地映射,这就导致TLB抖动比直接内存映射大得多。所以,不到挖不得已我们才用,一般我们在获得大块内存时。如内核模块加载需要的内存。

函数定义如下:

[笔记分享] [OS] Linux的内存管理

另外需要注意的是vmalloc()可能引起睡眠,所以不能在中断上下文中使用,也不允许在阻塞的地方使用。相对应的内存释放函数如下:

[笔记分享] [OS] Linux的内存管理

1.7 slab

slab存在的目的主要是为了更好地管理空闲链表的频繁分配和回收。Slab层把不同对象划分为高速缓存组,每个高速缓存都存放不同类型的对象,每个对象对应一个高速缓存。然后这些高速缓存又被划分为多个slab,slab由一个或多个物理连续页组成,一般由一个页组成。这种策略能减少内存碎片。之间关系图如下:

[笔记分享] [OS] Linux的内存管理

高速缓存由kmem_cache_t结构表示:

[笔记分享] [OS] Linux的内存管理

其中kem_list3 *nodelists[MAX_NUMNODES];这个参数包含了slab_full、slab_partial、slab_empty这三种链表,也就是说所有可表示的slabl类型都放在这个元素中。

Slab结构如下:

[笔记分享] [OS] Linux的内存管理

Slab结构可在slab内存之内和之外分配。Slab可以创建新的slab,本质上通过__get_free_pages()分配实现,如下:

[笔记分享] [OS] Linux的内存管理

slab层只有给定的高速缓存无空间时才调用此函数。当可用内存变得紧缺时,系统才会调用释放函数,或者是显示调用释放高速缓存时。

创建高速缓存函数如下:

[笔记分享] [OS] Linux的内存管理

该函数会睡眠。相应的销毁高速缓存函数如下:

[笔记分享] [OS] Linux的内存管理

在销毁之前一定要确保如下条件:

1. 高速缓冲中的所有slab为空。

2. 在抵用kmem_cache_destroy()期间确保不再访问这个高速缓存了。

在创建了高速缓存后,我们就要分配一个对象,如下:

[笔记分享] [OS] Linux的内存管理

正如前面所说,当高速缓存无空slab时,我们通过kmem_getpages()获得新的页。相对应的释放对象函数如下:

[笔记分享] [OS] Linux的内存管理

因此,如果要频繁创建很多同类型的对象,那么就可以考虑使用slab,这样就不用自己实现空闲链表了。

继续阅读