ノートを構築するためのLinux 3.14.12メモリ管理、メモリ管理フレームワーク[(1)]

従来のコンピュータ・アーキテクチャは、メモリ全体は、必要なCPU時間の全メモリ空間へのアクセスが同一の物理回線です。このようなメモリ構造は、UMA(ユニフォームメモリアーキテクチャ、一貫性のあるストレージ構造)として知られています。しかし、コンピュータの発達で、特にマルチCPUの場合、いくつかの新しいサーバアーキテクチャは、物理メモリ空間へのアクセスは同じのために必要な時間を制御することは困難であろう。マルチCPU環境では、システムは、複数のCPUを持つ唯一のバスは、上記にリンクされており、各CPUには、独自のローカル物理メモリ空間を持っていますが、あなたはまた、バスで他のCPU物理メモリ空間を訪問して行くことができますが、また、いくつかの一般的な物理メモリ空間とアクセスすることができ、より一般的なCPUがあります。これは新しい状況、内の異なる位置の物理メモリ空間の多様性を提示し、その時間の長さは、彼らが異なる訪問する、一貫性を保証することはできません以来。この場合のメモリ構成の場合、それは、NUMA(非均等メモリ・アーキテクチャ、非一様メモリ・アーキテクチャ)と呼ばれています。実際には何の完全なUMAは、このような一般的なシングルCPUのコンピュータへのアクセス時間、RAM、ROMおよび他の物理ストレージ容量として、存在しないと同じではなく、純粋にRAMの面で、UMAです。特定のインターネット接続によってノードSMPサーバの複数によって実行されるMPP(大規模並列処理、大規模な並列処理システム)と呼ばれる構造もあり、同じタスクを達成するために一緒に働きます。サーバシステムで、外部のユーザーからのようです。

トピックに戻る、NUMAを見て焦点を当てました。サポートするために、適切な管理の仕組みが必要でNUMAメモリアーキテクチャ、Linuxのバージョン2.4が導入されて以来、既にそのサポートを開始しました。新しい管理機構の支援を得て、また、ノード(ストレージノード)の概念を導入し、同じアクセス時間の収納スペースには、ストレージノードにダウンしています。ノード(記憶ノード)、ゾーン(管理領域)とページ(ページ)に電流解析の3.14.12バージョンは、Linuxの物理メモリの物理メモリ管理機構は、続いて、管理、3つのレベルに分割されます。

画像

ノードがデータ構造格納さpg_data_tあり、各NUMAノードは、ノードの責任に開示された情報pg_data_tメモリレイアウトを有しています。前記部材は、node_zonesあり、そのデータ構造は構造pg_data_t管理情報領域ゾーンに格納され、それぞれがnode_zonesのpg_data_tを複数有し、通常3:ZONE_DMA、ZONE_NORMAL、ZONE_HIGHMEM。

ZONE_DMA領域は、使用される機器、x86の環境の一部の外に刻まれた地区に直接、具体的にすべてのメモリにアクセスすることができない機器のコンピュータ部には通常、面積は16Mよりも通常小さいです。

ZONE_DMA領域の後ろに位置ZONE_NORMALは、コア領域は、線形アドレス、x86の環境の端部に直接マッピングされ、この領域は、一般16M-896Mです。

ZONE_HIGHMEM領域はZONE_DMA ZONE_NORMALは、物理メモリ領域を残りおよび後に加えて、この領域を直接カーネル、x86の環境でマッピングすることができないシステムであり、この領域は、通常、メモリ896Mの後です。

なぜべきでハイエンドメモリの存在?通常、我々はカーネル空間の大きさが1G(:3-4G線形空間)であることを知っています。それでは、どのくらいの1Gメモリは、このページにグローバルディレクトリエントリをマッピングされましたか?、十分に、1G非常に多くのカーネルスレッドが256ありますされて把握するのは簡単ですか!あなたが使用したい場合は明らかに十分で、メモリの1Gは、どのように行うために超えましたか?あなたがメモリを使用する場合、マッピングが行われなければならないことは明らかであるし、グローバルディレクトリエントリのうち、いくつかのページを作るマッピングを行いますか?ビンゴは、そうであること、どのくらいの作るには?Linuxカーネルの設計は、32ページのグローバルディレクトリエントリ、256 1/8を作ることです。次に、32ページのグローバルディレクトリエントリは、どのくらいのメモリ空間に対応しますか?Countが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低いメモリは、関数は、ノードに以前に確立されたノード情報memblockを設定するアルゴリズムに推測することができる、関数名、memblock_set_nodeを呼び出し、対応するページフレームmax_low_pfnサイズのアドレスに初期化されます。

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, &end_rgn);
    if (ret)
        return ret;
 
    for (i = start_rgn; i < end_rgn; i++)
        memblock_set_region_node(&type->regions[i], nid);
 
    memblock_merge_regions(type);
    return 0;
}

memblock_set_nodeメイン関数呼び出しは、3つの関連する操作を実行します。memblock_isolate_range、memblock_set_region_nodeとmemblock_merge_regionsを。

どのmemblock_isolate_range:

【file:/mm/memblock.c】
/**
 * memblock_isolate_range - isolate given range into disjoint memblocks
 * @type: memblock type to isolate range for
 * @base: base of range to isolate
 * @size: size of range to isolate
 * @start_rgn: out parameter for the start of isolated region
 * @end_rgn: out parameter for the end of isolated region
 *
 * Walk @type and ensure that regions don't cross the boundaries defined by
 * [@base,@base+@size). Crossing regions are split at the boundaries,
 * which may create at most two more regions. The index of the first
 * region inside the range is returned in *@start_rgn and end in *@end_rgn.
 *
 * RETURNS:
 * 0 on success, -errno on failure.
 */
static int __init_memblock memblock_isolate_range(struct memblock_type *type,
                    phys_addr_t base, phys_addr_t size,
                    int *start_rgn, int *end_rgn)
{
    phys_addr_t end = base + memblock_cap_size(base, &size);
    int i;
 
    *start_rgn = *end_rgn = 0;
 
    if (!size)
        return 0;
 
    /* we'll create at most two more regions */
    while (type->cnt + 2 > type->max)
        if (memblock_double_array(type, base, size) < 0)
            return -ENOMEM;
 
    for (i = 0; i < type->cnt; i++) {
        struct memblock_region *rgn = &type->regions[i];
        phys_addr_t rbase = rgn->base;
        phys_addr_t rend = rbase + rgn->size;
 
        if (rbase >= end)
            break;
        if (rend <= base)
            continue;
 
        if (rbase < base) {
            /*
             * @rgn intersects from below. Split and continue
             * to process the next region - the new top half.
             */
            rgn->base = base;
            rgn->size -= base - rbase;
            type->total_size -= base - rbase;
            memblock_insert_region(type, i, rbase, base - rbase,
                           memblock_get_region_node(rgn),
                           rgn->flags);
        } else if (rend > end) {
            /*
             * @rgn intersects from above. Split and redo the
             * current region - the new bottom half.
             */
            rgn->base = end;
            rgn->size -= end - rbase;
            type->total_size -= end - rbase;
            memblock_insert_region(type, i--, rbase, end - rbase,
                           memblock_get_region_node(rgn),
                           rgn->flags);
        } else {
            /* @rgn is fully contained, record it */
            if (!*end_rgn)
                *start_rgn = i;
            *end_rgn = i + 1;
        }
    }
 
    return 0;
}

基準ベースと区画メモリ・ノード・メモリタグのサイズ範囲に応じて、memblockアルゴリズムを構築しながら、分割操作の主な機能は、実行するが、フラグ操作をマージ行うのと同じ、その後、隣接するメモリか否かを判断するが、この時間は、ノードのノードを確立します来ます。領域内memblock境界の終わりにノードメモリにまたがる場合は、次に、単にノードのメモリ範囲の端までの領域においてmemblockがオフに分割した場合、それは領域インデックスレコードがstart_rgnするであろう、指数バック返すend_rgnする記録をインクリメントします境界領域は、現在のノードが境界ノードのメモリ範囲で調整され、他の部分()関数は、分割を完了するために、領域間を通るmemblock_insert_regionのmemblock管理に挿入されます。

道のmemblock_insert_region()関数を見てください:

【file:/mm/memblock.c】
/**
 * memblock_insert_region - insert new memblock region
 * @type: memblock type to insert into
 * @idx: index for the insertion point
 * @base: base address of the new region
 * @size: size of the new region
 * @nid: node id of the new region
 * @flags: flags of the new region
 *
 * Insert new memblock region [@base,@base+@size) into @type at @idx.
 * @type must already have extra room to accomodate the new region.
 */
static void __init_memblock memblock_insert_region(struct memblock_type *type,
                           int idx, phys_addr_t base,
                           phys_addr_t size,
                           int nid, unsigned long flags)
{
    struct memblock_region *rgn = &type->regions[idx];
 
    BUG_ON(type->cnt >= type->max);
    memmove(rgn + 1, rgn, (type->cnt - idx) * sizeof(*rgn));
    rgn->base = base;
    rgn->size = size;
    rgn->flags = flags;
    memblock_set_region_node(rgn, nid);
    type->cnt++;
    type->total_size += size;
}

ここでMEMMOVE()がバック領域情報の背後に移動され、元の領域のさらなるコールmemblock_set_region_node()ノードのノード番号がアウト分割された領域に残ります。

memblock_set_region_node()関数は、それを実現するためにのみ割り当てられます。

【file:/mm/memblock.h】
static inline void memblock_set_region_node(struct memblock_region *r, int nid)
{
    r->nid = nid;
}

既知のノードのノード番号を取得し、memblock_merge_regions()が既に解析されしたがって、バックmemblock_set_node次いでmemblock_isolate_range()memblock_set_region_nodeが(呼び出され()関数は、)、領域は合成のためです。

そして最後にバックinitmem_init()関数に、memblock_set_node()が戻った後、関数呼び出しは)(sparse_memory_present_with_active_regionsです。

ここではスパースメモリは、Linuxのメモリモデルの概念を含んでいます。フラットメモリ、非連続メモリとスパースメモリ:3つのLinuxカーネルのメモリモデルがあります。それぞれ、と彼は言いました:

  • フラットメモリ:名前が示すように、フラットな連続物理メモリは、システム全体が一つのノードのみのノードを持っています。
  • 不連続メモリ:物理メモリ不連続性、空隙がメモリ内に存在し、従って、システムは、複数のノードに物理メモリを分割する内部メモリの各ノードが、連続した平坦です。NUMA環境のモデルだけでなく、同じ環境で複数のノードUMAの場合であってもよいことは注目に値します。
  • スパースメモリ:物理メモリが連続していない、ノードの内部メモリは、不連続であってもよいので、システムは、1つのまたは複数のノードを有していてもよいです。また、このモデルは、ホットスワップ可能なメモリの基本です。

sparse_memory_present_with_active_regions()の実装を見て:

【file:/mm/page_alloc.c】
/**
 * sparse_memory_present_with_active_regions - Call memory_present for each active range
 * @nid: The node to call memory_present for. If MAX_NUMNODES, all nodes will be used.
 *
 * If an architecture guarantees that all ranges registered with
 * add_active_ranges() contain no holes and may be freed, this
 * function may be used instead of calling memory_present() manually.
 */
void __init sparse_memory_present_with_active_regions(int nid)
{
    unsigned long start_pfn, end_pfn;
    int i, this_nid;
 
    for_each_mem_pfn_range(i, nid, &start_pfn, &end_pfn, &this_nid)
        memory_present(this_nid, start_pfn, end_pfn);
}

for_each_mem_pfn_range内側()が空の関数である、CONFIG_HAVE_MEMORY_PRESENTに定義されていないによる実験環境にループマクロ定義、およびmemory_present()するように設計されています。当分の間、保留中の綿密な調査をしないでください。

(それが終了する前に、機能setup_bootmem_allocator)最後に、)(initmem_initを見て:

【file:/arch/x86/mm/init_32.c】
void __init setup_bootmem_allocator(void)
{
    printk(KERN_INFO " mapped low ram: 0 - %08lx\n",
         max_pfn_mapped<<PAGE_SHIFT);
    printk(KERN_INFO " low ram: 0 - %08lx\n", max_low_pfn<<PAGE_SHIFT);
}

元の関数はbootmem管理アルゴリズムを初期化するために使用されたが、今だけの情報のための印刷部を維持するために、x86の環境memblock管理アルゴリズムを使用されてきました。

おすすめ

転載: www.cnblogs.com/linhaostudy/p/11622450.html