欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  科技

【原创】(四)Linux内存模型之Sparse Memory Model

程序员文章站 2022-05-04 13:07:08
背景 By 鲁迅 By 高尔基 说明: 1. Kernel版本:4.14 2. ARM64处理器,Contex A53,双核 3. 使用工具:Source Insight 3.5, Visio 1. 介绍 顺着之前的分析,我们来到了 函数了,本以为一篇文章能搞定,大概扫了一遍代码之后,我默默的把它拆 ......

背景

  • read the fucking source code! --by 鲁迅
  • a picture is worth a thousand words. --by 高尔基

说明:

  1. kernel版本:4.14
  2. arm64处理器,contex-a53,双核
  3. 使用工具:source insight 3.5, visio

1. 介绍

顺着之前的分析,我们来到了bootmem_init()函数了,本以为一篇文章能搞定,大概扫了一遍代码之后,我默默的把它拆成了两部分。
bootmem_init()函数代码如下:

void __init bootmem_init(void)
{
    unsigned long min, max;

    min = pfn_up(memblock_start_of_dram());
    max = pfn_down(memblock_end_of_dram());

    early_memtest(min << page_shift, max << page_shift);

    max_pfn = max_low_pfn = max;

    arm64_numa_init();
    /*
     * sparsemem tries to allocate bootmem in memory_present(), so must be
     * done after the fixed reservations.
     */
    arm64_memory_present();

    sparse_init();
    zone_sizes_init(min, max);

    memblock_dump_all();
}

这一部分,我们将研究一下sparse memory model
在讲linux内存模型之前,需要补充两个知识点:pfnnuma

1.1 physical frame number(pfn)

前面我们讲述过了虚拟地址到物理地址的映射过程,而系统中对内存的管理是以页为单位的:
page:线性地址被分成以固定长度为单位的组,称为页,比如典型的4k大小,页内部连续的线性地址被映射到连续的物理地址中;
page frame:内存被分成固定长度的存储区域,称为页框,也叫物理页。每一个页框会包含一个页,页框的长度和一个页的长度是一致的,在内核中使用struct page来关联物理页。

如下图,pfn从图片中就能看出来了:
【原创】(四)Linux内存模型之Sparse Memory Model

至于__page_to_pfn这个实现取决于具体的物理内存模型,下文将进行介绍。

1.2 numa

  • uma: uniform memory access,所有处理器对内存的访问都是一致的:
    【原创】(四)Linux内存模型之Sparse Memory Model

从上图中可以看出,当处理器和core变多的时候,内存带宽将成为瓶颈问题。

  • numa: non uniform memory access,非一致性内存访问:
    【原创】(四)Linux内存模型之Sparse Memory Model

从图中可以看出,每个cpu访问local memory,速度更快,延迟更小。当然,整体的内存构成一个内存池,cpu也能访问remote memory,相对来说速度更慢,延迟更大。目前对numa的了解仅限于此,在内核中会遇到相关的代码,大概知道属于什么范畴就可以了。

2. linux内存模型

linux提供了三种内存模型(include/asm-generic/memory_model.h):
【原创】(四)Linux内存模型之Sparse Memory Model

一般处理器架构支持一种或者多种内存模型,这个在编译阶段就已经确定,比如目前在arm64中,使用的sparse memory model

  • flat memory
    物理内存地址连续,这个也是linux最初使用的内存模型。当内存有空洞的时候也是可以使用这个模型,只是struct page *mem_map数组的大小跟物理地址正相关,内存有空洞会造成浪费。

  • discontiguous memory
    物理内存存在空洞,随着sparse memory的提出,这种内存模型也逐渐被弃用了。

  • sparse memory
    物理内存存在空洞,并且支持内存热插拔,以section为单位进行管理,这也是下文将分析的。

linux三种内存模型下,struct page到物理page frame的映射方式也不一样,具体可以查看include/asm-generic/memory_model.h文件中的__pfn_to_page/__page_to_pfn定义。

关于内存模型,可以参考memory: the flat, the discontiguous, and the sparse

3. sparse memory

本节分析的是arm64, uma(linux4.14中不支持arm numa)下的sparse memory模型。

3.1 mem_section

sparse memory模型中,section是管理内存online/offline的最小内存单元,在arm64中,section的大小为1g,而在linux内核中,通过一个全局的二维数组struct mem_section **mem_section来维护映射关系。
函数的调用过程如下所示,主要在arm64_memory_present中来完成初始化及映射关系的建立:
【原创】(四)Linux内存模型之Sparse Memory Model

函数调用结束之后的映射关系如下图所示:
【原创】(四)Linux内存模型之Sparse Memory Model

已知一个pfn时,可以通过__pfn_to_section(pfn)来最终找到对应的struct page结构。

3.2 sparse_init

看看sparse_init函数的调用关系图:
【原创】(四)Linux内存模型之Sparse Memory Model

在该函数中,首先分配了usermap,这个usermap与内存的回收机制相关,用4bit的bitmap来描述page block(一个pageblock大小通常为2的次幂,比如max_order-1)的迁移类型:

/* bit indices that affect a whole block of pages */
enum pageblock_bits {
    pb_migrate,
    pb_migrate_end = pb_migrate + 3 - 1,
            /* 3 bits required for migrate types */
    pb_migrate_skip,/* if set the block is skipped by compaction */

    /*
     * assume the bits will always align on a word. if this assumption
     * changes then get/set pageblock needs updating.
     */
    nr_pageblock_bits
};

sparse memory模型会为每一个section都分配一个usermap,最终的物理页面的压缩,迁移等操作,都跟这些位相关,如下图所示:
【原创】(四)Linux内存模型之Sparse Memory Model

sparse_init函数中,另一部分的作用是遍历所有present section,然后将其映射到vmemmap区域空间。vmemmap区域空间,在之前的文章中也提到过。执行完后,整体的效果如下图所示:
【原创】(四)Linux内存模型之Sparse Memory Model

关于sparse memory model就先分析这么多,只有结合使用sparse memory的具体模块时,理解才会更顺畅。

一不小心就容易扣细节,而一旦陷入细节,内核就容易变成魔鬼,太难了。

【原创】(四)Linux内存模型之Sparse Memory Model