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

Linux内核&驱动学习笔记(二)

2013年05月31日 ⁄ 综合 ⁄ 共 2124字 ⁄ 字号 评论关闭
文章目录

2007.6.14  pm

Linux是如何管理内存的?今天系统的整理一下这个问题。

  • 物理内存是如何组织的
  • 如何分配和释放物理内存
  • 虚拟内存到物理内存的映射
  • 进程的地址空间是怎么样的
  • 虚拟内存是如何组织的
  • 如何分配和释放虚拟内存
  • 启动时内存的分配
  • 什么是slab
  • 什么是页缓存

物理内存是如何组织的

在系统的初始化阶段, 内核根据检测到的物理内存的大小,为每一个页面都建立一个page结构,形成一个page结构的数组,并使一个全局量mem_map指向这个数组。同时又按需要将这些页面拼合成许多内存页面块,再把块组成管理区ZONE,分配和释放物理内存见下节。

如何分配和释放物理内存

主要用的算法是二进制伙伴分配器,其基本概念是:内存被分成了含有很多页面的大块,每一块都是2 个页面大小的方幂。如果找不到想要的块,一个大块会被分成两半,这两半彼此就成了伙伴。其中一半被用来分配而另一半空闲。这些块会继续被二分直至产生一个所需大小的块。当一个块被最终释放时,其伙伴将被检测出来,如果它空闲则合并两者。

分配器维护空闲页面所组成的块,这里每一块都是2 的方幂个页面。方幂的指数被称为阶,内核对于每一个阶都维护结构数组free_area_t,用于指向一个空闲页面块的链表。所以,数组的第0 个元素将会指向具有20 个页面大小的块的链表,第一个元素将会是一个具有21 个页面大小的块的链表, 直到2MAX _ORDER−1 个页面大小的块,MAX_ORDER 的值一般为10。这消除了一个大页面被分开来满足一个小页面块即能满足的要求的可能性。页面块通过page->list 这个线性链表来维护。每一个管理区都有一个free_area_t 结构数组,即free_area[MAX_ORDER],它在<linux/mm.h>中的定义如下所示:

typedef struct free_area_struct {
          struct list_head free_list;
          unsigned long *map;
} free_area_t;

此结构的字段:free_list 空闲页面块的链表;map 表示一对伙伴状态的位图。

用作分配的API 函数都使用核心函数__alloc_pages(),分配通常依据一个特定的次序进行,如果空闲块不能满足所
需的层,则一个高次的块会被分成两个伙伴,一个用于分配,另一个放入低次的空闲链表中。第二步决定使用哪个内存节点以及哪个pg_data_t。Linux 使用了本地节点的分配策略,这是为了使用和运行与页面分配进程的CPU 相关联的内存库。无论使用哪一个API,mm/page_alloc.c 中的函数__alloc_pages()都是分配器的核心。这个函数从不被直接调用,它会检查选定的管理区,看这个管理区可用的页面数量上是否适合分配。如果这个管理区不适合,分配器将会退回到其它管理区。退回的管理区的次在启动时由函数build_zonelists()决定。但通常是从ZONE_ HIGHMEM 退回到ZONE_ NORMAL,从ZONE_ NORMAL 再退回到ZONE_ DMA。如果空闲页面的数量达到pages_low 的要求,系统激活kswapd 开始从管理区中释放页面。如果内存空间极度紧张,调用者将自己完成kswapd 的工作。一旦最终选定了管理区,系统会调用函数rmqueue()分配页面块,或在没有合适大小的情况下切分高次的块。

用于释放页面的API 比较简单,它们可以帮助记忆将要释放的页面块的次。伙伴分配器的一个缺点是调用者必须记住最初分配的大小。用于释放页面的主要函数是__free_pages_ok(),它不能被直接调用,而是提供了函数__free_pages(), 它会首先进行一些简单的检查,为了探测伙伴们是不是可以合并,Linux 在 free_areamap 中检查与受影响的伙伴相对应的位。由于一个伙伴刚刚被此函数释放,很显然它知道至少有一个伙伴是空闲的。如果切
换以后位图中的位是0,那么另外一个伙伴也肯定是空闲的,因为如果这个位是0,就意味着两个伙伴或者同为空闲或者同被分配。如果两个都是空闲的,系统可以合并它们。计算这个伙伴地址有一个著名的方法[Knu68]。由于分配是以2k 个块进行的,块的地址,或者说至少它在zone_mem_map 的起始地址将会是2k 的整次幂。最终的结论是总会有至少k 个数字的0 在地址的右边。为了得到伙伴的地址,从右边数的第k 位检测。如果为0,伙伴将会翻转自己的位。为了得到这个位的值,Linux 引入了一个掩码,其计算如下:
mask=(~0<<k)
我们感兴趣的掩码是:
imask=1+~imask
Linux 在计算此掩码时采用了这个技巧:
imask=-mask=1+~imask
合并伙伴后,它便从空闲链表中被移除,并且这个新的合并对会被移到下一个高次链表中以确定是否可以再次合并。

虚拟内存到物理内存的映射

介绍MMU 

进程的地址空间是怎么样的

内核空间 用户空间 每个进程的映射表

虚拟内存是如何组织的

 虚拟内存的结构

如何分配和释放虚拟内存

 如何分配和释放虚拟内存  vmalloc

启动时内存的分配

bootmem

什么是slab

slab分析

什么是页缓存

page cache

抱歉!评论已关闭.