文章目录
- RAM的某些部分永久地分配给内核,
- 并用来存放内核代码以及静态内核数据结构。
- RAM的其余部分称动态内存,这不仅是进程所需的宝贵资源,也
是内核本身所需的宝贵资源 - 整个系统的性能取决于如何有效地管理动态内存
- 现在所有多任务操作系统都在尽力优化对动态内存的使用,尽可能做
到当需要时分配,不需要时释放。 - 图8-1显示用作动态内存的页框,详细内容请参见第二章的“物理内存布局”
- 本章通过三部分描述内核如何给自己分配动态内存
- “页框管理”和“内存区管理”介绍对连续物理内存区处理的两种技术
- “非连续内存区管理“介绍处理非连续内存区的第三种
- 这几节将涉及内存管理区、内核映射、伙伴系统、slab高速缓存和内存池
页框管理
- “硬件中的分页”介绍, Intel的 Pentium可用两种不同页框:
- 4KB和4MB(如果PAE被激活,则为2MB第二章“物理地址扩展(PAE)分页机制”一节)
- Linux用4KB页框大小作为标准的内存分配单元。
- 基于以下两个原因
- 由分页单元引发的缺页异常很容易得到解释,
- 或者是由于请求的页存在但不允许进程对其访问,
- 或由于请求的页不存在。
- 第二种情况下,内存分配器必须找到个4KB的空闲页框,
- 并将其分配给进程。
- 4KB和4MB都是磁盘块大小的倍数,但绝大多数,主存和磁盘间传输小块数据时更高效
页描述符
- 内核须记录每个页框当前状态。
- 内核须区分哪些页框包含的是属于进程的页,
- 哪些包含内核代码或内核数据
- 内核还必须能够确定动态内存中的页框是否空闲。
- 如果动态内存中的页框不包含有用的数据,那么这个顶框就是空闲的。
- 以下情况下页框不空闲:包含用户态进程的数据、某个软件高速缓存的数据、动态分配的内核数据结构、设备驱动程序缓冲的数据、内核模块的代码
- 页框的状态信息保存在类型为page的页描述符
- 所有页描述符存在mem_map数组
- 每个描述符32字节,mem_map所需空间略小于整个RAM 1%
- virt_to_page(addr)宏产生线性地址对应的页描述符地址。
- pfn_to_page(pfn)宏产生与页框号Pfn对应的顶描述符地址。
- 接下来,常回到页描述符的字段
- 有几个字段的含义还取决于页框是否空闲及什么样的内核成分在使用页框。
- _count
- -1,则页框空闲,并可被分配给任一进程或内核本身;
- >=0,则页框被分配给了一个或多进程或
- 用于存放一些内核数据结构。
- page_count()返回_count加1后值,该页的使用者数目
- flags
- 含多达32个用来描述页框状态的标志(见表8-2)
- 对毎个PG_xyz标志,内核都定义操纵其值的一些宏。
- PageXyz宏返回标志的值,而 SetpageXyz和ClearpageXyz宏设置和清除相应的位。
struct page {
page_flags_t flags; /* Atomic flags, some possibly
* updated asynchronously */
atomic_t _count; /* Usage count, see below. */
atomic_t _mapcount; /* Count of ptes mapped in mms,
* to show when page is mapped
* & limit reverse map searches.
*/
unsigned long private; /* Mapping-private opaque data:
* usually used for buffer_heads
* if PagePrivate set; used for
* swp_entry_t if PageSwapCache
* When page is free, this indicates
* order in the buddy system.
*/
struct address_space *mapping; /* If low bit clear, points to
* inode address_space, or NULL.
* If page mapped as anonymous
* memory, low bit is set, and
* it points to anon_vma object:
* see PAGE_MAPPING_ANON below.
*/
pgoff_t index; /* Our offset within mapping. */
struct list_head lru; /* Pageout list, eg. active_list
* protected by zone->lru_lock !
*/
/*
* On machines where all RAM is mapped into kernel address space,
* we can simply calculate the virtual address. On machines with
* highmem some memory is mapped into kernel virtual memory
* dynamically, so we need a place to store that address.
* Note that this field could be 16 bits on x86 ... ;)
*
* Architectures with slow multiplication can define
* WANT_PAGE_VIRTUAL in asm/page.h
*/
#if defined(WANT_PAGE_VIRTUAL)
void *virtual; /* Kernel virtual address (NULL if
not kmapped, ie. highmem) */
#endif /* WANT_PAGE_VIRTUAL */
};
非一致内存访问(NUMA)
- 忽略高速缓存情况下,期望不管内存单元处何处,
- 也不管CPU处于何处,CPU对内存单元的访问都需要相同的时间。
- 对于某些多处理器 Alpha或MIPS计算机,不成立。
- Linux2.6支持NUMA,这种模型中,CPU对不同内存单元的访问时间可能不一样。
- 物理内存被划分为几个节点
- 一个单独的节点内,任一给定CPU访问页面所需时间都相同
- 对不同的CPU,可能不同
- 对每个CPU,内核都试图把耗时节点的访问次数减到最少,
- 这就要小心选择CPU最常引用的内核数据结构的存放位置(注1)。
- 每个节点中的物理内存又分为几个管理区(Eone),下一节介绍。
- 每个节点都有一个类型为pg_data_t的描述符,字段如表8-3。
- 所有节点的描述符存放在单向链表,
- 第一个元素由pgdat_list变量指向。
- 只关注80x86。
- IBM兼容PC使用UMA,因此,并不真正需要NUMA的支持。
- 然而,即使NUMA的支持没有编译进内核, Linux还是使用节点,这是个单独节点,含系统中所有的物理内存
- pgdat_list指向一个链表,此链表由一个元素组成,
- 这个元素就是节点0描述符,它被存在contig_page_data变量
- 80x86中,把物理内存分组在一个单独的节点中可能显得没有用处;
- 但这种方式有助于内存代码的处理更具有可移植性,
- 因为内核假定在所有体系结构中
- 物理内存都被划分为一个或多个节点(注2)。
内存管理区
- 理想计算机体系中,一个页框就是一个内存存储单元,可用于
- 存放内核数据和用户数据、缓冲磁盘数据
- 任何种类的数据页都可存放在任何页框中,没什么限制。
- 实际的计算机体系结构有硬件的制约,限制页框可以使用的方式
- Linux内核必须处理80x86的两种硬件约束
- ISA总线的直接内存存取(DMA)处理器有一个严格限制:
- 只能对RAM的前16MB寻址。
- 在有大容量RAM的现代32位计算机中,
- CPU不能直接访问所有的物理内存,因为线性地址空间太小。
- 为应对这两种限制, Linux2.6把每个内存节点的物理内存划分为3个管理区
- 在80x86UMA中的管理区为
- ZONE DMA
- 低于16MB的内存页框
- ZONE NORMAL
- 高于16MB且低于896MB的内存页框
- ZONE HIGHMEM
- 从896MB开始高于896MB的内存页框