设为首页 加入收藏

TOP

Linux-3.14.12内存管理笔记【构建内存管理框架(1)】(一)
2019-10-09 20:00:11 】 浏览:89
Tags:Linux-3.14.12 内存 管理 笔记 构建 框架

传统的计算机结构中,整个物理内存都是一条线上的,CPU访问整个内存空间所需要的时间都是相同的。这种内存结构被称之为UMA(Uniform Memory Architecture,一致存储结构)。但是随着计算机的发展,一些新型的服务器结构中,尤其是多CPU的情况下,物理内存空间的访问就难以控制所需的时间相同了。在多CPU的环境下,系统只有一条总线,有多个CPU都链接到上面,而且每个CPU都有自己本地的物理内存空间,但是也可以通过总线去访问别的CPU物理内存空间,同时也存在着一些多CPU都可以共同访问的公共物理内存空间。于是乎这就出现了一个新的情况,由于各种物理内存空间所处的位置不同,于是访问它们的时间长短也就各异,没法保证一致。对于这种情况的内存结构,被称之为NUMA(Non-Uniform Memory Architecture,非一致存储结构)。事实上也没有完全的UMA,比如常见的单CPU电脑,RAM、ROM等物理存储空间的访问时间并非一致的,只是纯粹对RAM而言,是UMA的。此外还有一种称之为MPP的结构(Massive Parallel Processing,大规模并行处理系统),是由多个SMP服务器通过一定的节点互联网络进行连接,协同工作,完成相同的任务。从外界使用者看来,它是一个服务器系统。

回归正题,着重看一下NUMA。由于NUMA存储结构的引入,这就需要相应的管理机制来支持, linux 2.4版本就已经开始对其支持了。随着新增管理机制的支持,也随之引入了Node的概念(存储节点),把访问时间相同的存储空间归结为一个存储节点。于是当前分析的3.14.12版本,linux的物理内存管理机制将物理内存划分为三个层次来管理,依次是:Node(存储节点)、Zone(管理区)和Page(页面)。

image

存储节点的数据结构为pg_data_t,每个NUMA节点都有一个pg_data_t负责记载该节点的内存布局信息。其中pg_data_t结构体中存储管理区信息的为node_zones成员,其数据结构为zone,每个pg_data_t都有多个node_zones,通常是三个:ZONE_DMA、ZONE_NORMAL、ZONE_HIGHMEM。

ZONE_DMA区通常是由于计算机中部分设备无法直接访问全部内存空间而特地划分出来给该部分设备使用的区,x86环境中,该区通常小于16M。

ZONE_NORMAL区位于ZONE_DMA后面,这个区域被内核直接映射到线性地址的高端部分,x86环境中,该区通常为16M-896M。

ZONE_HIGHMEM区则是系统除了ZONE_DMA和ZONE_NORMAL区后剩下的物理内存,这个区不能直接被内核映射,x86环境中,该区通常为896M以后的内存。

为什么要有高端内存的存在?通常都知道内核空间的大小为1G(线性空间为:3-4G)。那么映射这1G内存需要多少页全局目录项?很容易可以算出来是256项,内核有这么多线程在其中,1G够吗?很明显不够,如果要使用超出1G的内存空间怎么办?如果要使用内存,很明显必须要做映射,那么腾出几个页全局目录项出来做映射?Bingo,就是这样,那么腾出多少来呢?linux内核的设计就是腾出32个页全局目录项,256的1/8。那么32个页全局目录项对应多大的内存空间?算一下可以知道是128M,也就是说直接映射的内存空间是896M。使用超过896M的内存空间视为高端内存,一旦使用的时候,就需要做映射转换,这是一件很耗资源的事情。所以不要常使用高端内存,就是这么一个由来。

接着看一下内存管理框架的初始化实现,initmem_init():

【file:/arch/x86/mm/init_32.c】
#ifndef CONFIG_NEED_MULTIPLE_NODES
void __init initmem_init(void)
{
#ifdef CONFIG_HIGHMEM
    highstart_pfn = highend_pfn = max_pfn;
    if (max_pfn > max_low_pfn)
        highstart_pfn = max_low_pfn;
    printk(KERN_NOTICE "%ldMB HIGHMEM available.\n",
        pages_to_mb(highend_pfn - highstart_pfn));
    high_memory = (void *) __va(highstart_pfn * PAGE_SIZE - 1) + 1;
#else
    high_memory = (void *) __va(max_low_pfn * PAGE_SIZE - 1) + 1;
#endif
 
    memblock_set_node(0, (phys_addr_t)ULLONG_MAX, &memblock.memory, 0);
    sparse_memory_present_with_active_regions(0);
 
#ifdef CONFIG_FLATMEM
    max_mapnr = IS_ENABLED(CONFIG_HIGHMEM) ? highend_pfn : max_low_pfn;
#endif
    __vmalloc_start_set = true;
 
    printk(KERN_NOTICE "%ldMB LOWMEM available.\n",
            pages_to_mb(max_low_pfn));
 
    setup_bootmem_allocator();
}
#endif /* !CONFIG_NEED_MULTIPLE_NODES */

将high_memory初始化为低端内存页框max_low_pfn对应的地址大小,接着调用memblock_set_node,根据函数命名,可以推断出该函数用于给早前建立的memblock算法设置node节点信息。

memblock_set_node的实现:

【file:/mm/memblock.c】
/**
 * memblock_set_node - set node ID on memblock regions
 * @base: base of area to set node ID for
 * @size: size of area to set node ID for
 * @type: memblock type to set node ID for
 * @nid: node ID to set
 *
 * Set the nid of memblock @type regions in [@base,@base+@size) to @nid.
 * Regions which cross the area boundaries are split as necessary.
 *
 * RETURNS:
 * 0 on success, -errno on failure.
 */
int __init_memblock memblock_set_node(phys_addr_t base, phys_addr_t size,
                      struct memblock_type *type, int nid)
{
    int start_rgn, end_rgn;
    int i, ret;
 
    ret = memblock_isolate_range(type, base, size, &start_rgn, &e
首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇嵌入式01 单片机最小系统改进02 下一篇嵌入式02 STM32 外部中断实验

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目