blog

Linux メモリ管理 - パートナー・システムとメモリ・アロケータ

Linux カーネルは、メモリ割り当てによって引き起こされる外部の断片化に対処するためにパートナー・システムを使用します。パートナーシステムのアロケータは大きく2つのクラスに分かれています。クラス関数...

Sep 24, 2013 · 31 min. read
シェア

Linuxカーネルは、メモリ割り当てによって引き起こされる外部の断片化に対処するために、パートナー・システムを使用します。パートナー・システムのアロケータは大きく2つのカテゴリに分けられます。get_free_pages()型の関数は最初に割り当てられたページのリニア・アドレスを返し、alloc_pages()型の関数はページ記述子のアドレスを返します。どちらの関数が割り当てに使われたとしても、最終的には alloc_pages() が呼び出されてページが割り当てられます。

その配信システムを明確に把握するために、まずパートナーシステムのデータ保存のブロック図を示しましょう。

つまり、各オーダーはfree_area構造体に対応し、free_areaはこれらのメモリブロックを異なるタイプで連鎖したリストに格納します。

I. ページフレームの管理

すべてのページ・フレーム記述子はmem_map配列に格納されます。

1. ページデータ構造

struct page {    
    page_flags_t flags; //フラグ                        
    atomic_t _count;//ページ・フレームの参照カウント。参照カウント-1は、そのページ・フレームが空きページ・フレームであることを意味する。     
    atomic_t _mapcount;//页框在页表项中的数目,即该页框在多少个页表中被引用为页表                    项    
    unsigned long  private;//可用于多种内核成分,若该页为缓冲区所组成的页,其用来组                        织缓冲区首部链表            
    struct address_space  *mapping;//当页面属于页高速缓存时指向对应文件的 address _space ,ページが匿名ゾーンに属する場合、anon_DMA 領域を指す。_vma ,anon_vmaは、すべてのvmをリンクする連鎖リストを持つ。_area_struct.    
    pgoff_t index;  //スワップアウト・ページ識別子またはページ・ディスク・イメージまたは匿名ゾーン内のデータ・ブロックの位置を格納する。     
    struct list_head lru; //最も最近使用されたチェーン・テーブルにページをチェーンする。          
};   

2.ページ枠管理エリア

struct zone {    
    unsigned long       free_pages;//管理ゾーンの空きページ数    
    unsigned long       pages_min, pages_low, pages_high;    
    //pages_min管理ゾーンの予約ページ数を記録する。,pages_low,pages_high    
    //ページ再利用のしきい値    
    struct per_cpu_pageset  pageset[NR_CPUS];//pagesetパートナー・システム用の各CPU用の単一ページ・キャッシュ    
    spinlock_t      lock; //保護のためのスピン・ロック    
    struct free_area    free_area[MAX_ORDER];//パートナー・システム用の管理領域に空きページをマークする。    
    spinlock_t      lru_lock;   //lruリンク・テーブルを保護するためのスピン・ロック    
    struct list_head     active_list;//管理ゾーンのアクティブ状態のページの連鎖リストを記録する。    
    struct list_head     inactive_list;//管理ゾーンの非活動状態にあるページの連鎖リストを記録する。    
    unsigned long       nr_scan_active;//リクレイム中にスキャンされるアクティブ・ページの数で、ページ・リクレイム・スキャンが実行されるときの優先順位に関連する。    
    unsigned long       nr_scan_inactive;//再利用時にスキャンされる非アクティブ・ページの数    
    unsigned long       nr_active;//管理ゾーンのアクティブ・ページ数    
    unsigned long       nr_inactive;//管理ゾーンの非活動ページ数    
    unsigned long       pages_scanned;//ページ・フレームを再割り当てするときに使用されるカウンタ    
    int         all_unreclaimable;//管理領域が再利用できないページでいっぱいになったら、このフラグ・ビットを設定する。    
    int temp_priority;  //ページを再利用する際の現在の優先順位    
    int prev_priority; //ページ再利用時の優先順位    
    wait_queue_head_t   *  wait_table;//プロセス待ち行列のハッシュ・テーブル。    
    unsigned long       wait_table_size;//ハッシュ・テーブルのサイズ    
    unsigned long       wait_table_bits;//ハッシュ・テーブル配列のサイズ    
    struct pglist_data  *zone_pgdat;//ゾーンが属するメモリノードを管理する。    
    struct page     *zone_mem_map;//管理領域の最初のページ記述子を指す    
    unsigned long       zone_start_pfn;//管理ゾーンの最初のページフレームに対する添え字    
    unsigned long       spanned_pages;//ゾーンのサイズをページ単位で管理する    
    unsigned long       present_pages;//ホールを除いた管理ゾーンのページ・サイズ    
    char            *name;//管理ゾーンの名前    
} ____cacheline_maxaligned_in_smp;  

3.メモリーノード

Linux 2.6は、numaのコンピュータモデルをサポートしています、このモデルでは、異なるメモリユニットへのCPUアクセスは同じアクセス時間ではありません、システムの物理メモリは、いくつかのノードに分割され、与えられたノードでは、メモリ時間への任意の与えられたCPUアクセスは同じです、もちろん、ノードのメモリへの異なるCPUアクセスは、そのアクセス時間は異なっている、カーネルは、ノードへのCPUアクセス時間を最小限に抑える必要があり、これは、CPUの共通カーネルデータの場所を慎重に選択する必要があります。カーネルはCPUがノードにアクセスする時間を最小化する必要があり、そのためにはCPUの共通カーネル・データが存在するメモリ・ノードの場所を注意深く選択する必要があります。

ノード記述子はpg_data_tデータ構造で表されます。

typedef struct pglist_data {   
    struct zone node_zones[MAX_NR_ZONES];//このノードのメモリー管理ゾーンの配列   
    struct zonelist  node_zonelists[GFP_ZONETYPES];//zonelistメモリ・アロケータがメモリ割り当てを行うための配列   
    int nr_zones;//メモリ管理ゾーンの数   
    struct page *node_mem_map;//このノードのメモリーの開始メモリー記述子   
    struct bootmem_data *bdata;   
    unsigned long node_start_pfn;//このノードのメモリの開始メモリの添え字   
    unsigned long node_present_pages;  //穴を除いたノードのサイズ   
    unsigned long node_spanned_pages;  //ホールを含むノードのサイズ   
    int node_id;//ノードID   
    struct pglist_data *pgdat_next;//メモリ・ノードの連鎖リストを形成するために使用される。   
    wait_queue_head_t     kswapd_wait;//kswapdkswapdが通常スリープするプロセスで使用される待ちキュー。   
    struct task_struct *kswapd;   //でプロセスによって使用される、このメモリ・ノード上のメモリ再利用に使用される kswapd メモリ記述子。_pageは、十分なメモリがないときに起動される   
} pg_data_t;  

次の図は、メモリ・ノード、メモリ管理ゾーン、およびページの関係を示しています。 page->flagの下位ビットには、ノードIDとゾーンIDが格納されています。

II.パートナー制度

通常、alloc_pages(), alloc_page(), __get_free_pages(), __get_free_page(), get_zeroed_page() はページ・フレームを取得するために使用され、最初の2つはページ・ディスクリプタのアドレスを返し、最後の2つは物理ページのリニア・アドレスを返します。ハイエンド・メモリは最初の2つのアロケーション関数を介してのみ割り当て可能で、最後の2つはリニア・アドレスのため直接使用できません。これら4つの関数インターフェイスは、最終的にメモリ割り当て作業のためにパートナーシステムの関連インターフェイスを呼び出します。

Linuxカーネルのパートナーアルゴリズムは、メモリの断片化を最小限に抑えます。このアイデアは、物理メモリをブロックの11のリンクされたテーブルに分割することで、それぞれが1,2,4,8.......512,1024の連続したページ・フレーム・ブロック。たとえば、256の連続したページフレームを割り当てるには、256の最初のブロックサイズは、チェーンテーブル内の空きブロックを探しますが、直接リターンがある場合は、ない場合は、512チェーンテーブルのサイズに行く2つの部分、リターンの1つの部分、256チェーンテーブルのサイズの挿入の一部に512ブロックのサイズを見つけるために、512チェーンテーブルのサイズはまだブロックのサイズを探すために1024チェーンテーブルのサイズに使用できない場合は、256を取り出すサイズのブロックは、残りの512,256ブロックは、各チェーンテーブルに挿入され、メモリ解放プロセスは逆です。

割り当てプロセス中にビッグ・チャンクから分解されたスモール・チャンクの未割り当てチャンクは、割り当てチャンクとマージするために、割り当てチャンクがリリースされるのを待ち続けます。マージ操作はまさにページ・リリースのプロセスにあり、最終結果は分解されたチャンクがない場合と同等で、パートナー・システムはこの結果に収束し続けるため、パートナー・システムはフラグメンテーションを回避することができます。パートナー・システムは、割り当てと解放の双方向で、分解と結合という2つの相互に可逆的な操作を実行します。 システムが最初に断片化されていなければ、相互に可逆的な操作が断片の生成を最大限に打ち消すため、最終的な断片化は最小限に抑えられます。これがパートナー・システムの本質です。

パートナーシステムのアルゴリズムは、主にゾーン記述子の zone_mem_map フィールドと free_area 配列を使用し、zone_mem_map はメモリ管理領域の物理ページの開始アドレスを指し、free_area は前述の11のリンクされたテーブルを表します。

struct free_area { 
struct list_head 
free_list;//ブロック・チェーン・テーブル・ヘッダ 
unsigned long  *map;//ブロック割り当てのビットマップ 
}; 

struct_free_areaのfree_listフィールドは、メモリ記述子のlruフィールドによって連鎖リストにリンクされた空きメモリブロックの連鎖リストのヘッダです。 サイズ1024ページフレームのメモリブロックの最初のページフレームのprivateフィールドは、対応するORDER、すなわち10を保持し、最初のページフレームはまた、それがバディのメモリブロックであるかどうかを示すフラグビットを持っています。バディのメモリブロックかどうかを示すフラグビットもあります。このフィールドは、ページフレームが解放されるときに、バディのメモリブロックと組み合わせてより大きなメモリブロックにできるかどうかを判断するために使用されます。

1.メモリブロックの割り当て

static struct page *__rmqueue(struct zone *zone, unsigned int order)   
{   
    struct free_area * area;   
    unsigned int current_order;   
    struct page *page;   
    //ORDERサイズから始まるメモリ・ブロックの連鎖リストを反復処理する。   
    for (current_order = order; current_order < MAX_ORDER; ++current_order) {   
        //現在の_order対応するメモリー・ブロックの連鎖リスト   
        area = zone->free_area + current_order;   
        //メモリ・ブロック・チェーン・テーブルが空である。   
        if (list_empty(&area->free_list))   
            continue;   
        //フリー・チェーン・テーブルからメモリ・ブロックを、それが含む最初のページのlruでチェーン・オフする。   
        page = list_entry(area->free_list.next, struct page, lru);   
        list_del(&page->lru);   
        //ページのバディ・フラグをクリアし、privateを0に設定する。   
        rmv_page_order(page);   
        area->nr_free--;   
        zone->free_pages -= 1UL << order;   
        //現在の_orderサイズのメモリ・ブロックはORDERサイズを削り、残りのメモリ・ページを   
        //各フリー・リンク・テーブルで   
        expand(zone, page, order, current_order, area);   
        return page;   
    }   
    return NULL;   
}   

2.メモリブロックの解放

static inline void __free_one_page(struct page *page,   
        struct zone *zone, unsigned int order)   
{   
    unsigned long page_idx;   
    int order_size = 1 << order;   
    if (unlikely(PageCompound(page)))   
        destroy_compound_page(page, order);   
    page_idx = page_to_pfn(page) & ((1 << MAX_ORDER) - 1);   
    //空きページの値を増やす   
    zone->free_pages += order_size;   
    while (order < MAX_ORDER-1) {   
        unsigned long combined_idx;   
        struct free_area *area;   
        struct page *buddy;   
        //対応するパートナー・ブロックを見つける   
        buddy = page + (page_idx^(1<<order) -page_idx);   
        //ページとパートナー・ブロックを1つのメモリ・ブロックにまとめることができるかどうかを判断する。   
        if (!page_is_buddy(page, buddy, order))   
            break;      /* Move the buddy up one level. */   
        //フリー・チェーン・テーブルからパートナー・ブロックを削除する   
        list_del(&buddy->lru);   
        area = zone->free_area + order;   
        area->nr_free--;   
        rmv_page_order(buddy);   
        //得到合并后的内存块的page index   
        combined_idx = __find_combined_index(page_idx, order);   
        //マージされたメモリ・ブロックの開始ページを取得する。   
        page = page + (combined_idx - page_idx);   
        page_idx = combined_idx;   
        order++;   
    }   
    //ページ・フレームを設定する。>privateメモリ・ブロックのサイズを示す   
    set_page_order(page, order);   
    //対応するメモリ・ブロックのフリー・チェーン・テーブルに挿入する。   
    list_add(&page->lru, &zone->free_area[order].free_list);   
    zone->free_area[order].nr_free++;   
}   

3.CPUごとのページフレームキャッシュ

各メモリ管理領域には、CPUごとにページフレームキャッシュが定義されており、各キャッシュには、ローカルCPUが1つのページフレームを割り当てるために発行した要求を満たす数のページフレームが格納されています。

各メモリバッファは、各CPUに2つのページフレームキャッシュを提供します:ホットページフレームキャッシュとコールドページフレームキャッシュ、ホットキャッシュは、ほとんどの場合に使用され、ページがフェッチされた場合、CPUはすぐに操作を読み取りまたは書き込み、それは、ハードウェアキャッシュの効率を向上させることができ、コールドキャッシュは、通常、ハードウェアキャッシュを使用する必要がないdmaで転送するような場合に使用されます。

CPUごとのページフレームキャッシュの主要なデータ構造は、per_cpu_pageset型でzoneのpagesetフィールドにあります。

struct per_cpu_pageset { 
struct per_cpu_pages pcp[2]; 
/* 0: hot.  1: cold */ 
} 
struct per_cpu_pages { 
int count;  //ローカル・キャッシュ・ページ数 
int high;  //ローカル・キャッシュ・ページ数がHIGHを超えたとき、つまり空にする必要があるとき、ハイ・ウォーター・マークが表示される。 
int batch;  //没有页框,或者页框数超过high时,向伙伴系统申请或者释放batc个 页框 
struct list_head list; 
//ローカル・キャッシュ・ページ・フレーム・チェーン・テーブル 
}; 

単一ページのフレームを確保するために使用されるカーネル関数は、buffered_rmqueue() です。

static struct page *buffered_rmqueue(struct zonelist *zonelist,   
            struct zone *zone, int order, gfp_t gfp_flags)   
{   
    unsigned long flags;   
    struct page *page;   
    //コールド・ローカル・ページ・フレーム・キャッシュとホット・ローカル・ページ・フレーム・キャッシュのどちらからページをフェッチするかを決定するフラグ・ビットを取得する。   
    int cold = !!(gfp_flags & __GFP_COLD);   
    int cpu;   
   
again:   
    //ローカルCPUを取得する   
    cpu  = get_cpu();   
    //単一ページを割り当て、キャッシュにフェッチする。   
    if (likely(order == 0)) {   
        struct per_cpu_pages *pcp;   
        pcp = &zone_pcp(zone, cpu)->pcp[cold];   
        local_irq_save(flags);   
        //ローカル・キャッシュにもうない、ローカル・キャッシュを埋める   
        if (!pcp->count) {   
            //ローカル・キャッシュにページを割り当てるために、パートナー・システムに行く。   
            //1ページを確保し、各ページが確保されるまで一括確保する。   
            pcp->count += rmqueue_bulk(zone, 0,   
                        pcp->batch, &pcp->list);   
            if (unlikely(!pcp->count))   
                goto failed;   
        }   
        //ローカル・キャッシュ・チェーン・テーブルからページを削除する   
        page = list_entry(pcp->list.next, struct page, lru);   
        list_del(&page->lru);   
        pcp->count--;   
    } else {   
        //を使用して、単一ページを非割り当てにする。__rmqueue()先に進んで直接割り当てる   
        spin_lock_irqsave(&zone->lock, flags);   
        page = __rmqueue(zone, order);   
        spin_unlock(&zone->lock);   
        if (!page)   
            goto failed;   
    }   
   
    local_irq_restore(flags);   
    put_cpu();   
    :   
    return page;   
failed:   
    local_irq_restore(flags);   
    put_cpu();   
    return NULL;   
}   
    1つのページ・フレームを解放する関数は free_hot_cold_page()    
static void fastcall free_hot_cold_page(struct page *page, int cold)   
{   
    //ページ・フレームを通してメモリを確保する。>flag数ビット下のゾーンを取得する   
    struct zone *zone = page_zone(page);   
    struct per_cpu_pages *pcp;   
    unsigned long flags;   
   
    kernel_map_pages(page, 1, 0);   
    //ホット・マーカーとコールド・マーカーに基づいて、対応するローカル・ページ・フレーム・キャッシュに対応する連鎖テーブルを見つける。   
    pcp = &zone_pcp(zone, get_cpu())->pcp[cold];   
    local_irq_save(flags);   
    __count_vm_event(PGFREE);   
    //ページを対応するチェーン・テーブルに挿入する   
    list_add(&page->lru, &pcp->list);   
    pcp->count++;   
    //ローカル・ページ・フレーム・キャッシュのページ数がpcp-pages()を超えた場合、ページ・フレーム・リサイクル・アルゴリズムが起動する。>high,ページを一括確保する   
    //パートナー・システムに戻る   
    if (pcp->count >= pcp->high) {   
        free_pages_bulk(zone, pcp->batch, &pcp->list, 0);   
        pcp->count -= pcp->batch;   
    }   
    local_irq_restore(flags);   
    put_cpu();   
}   

III.メモリ管理領域アロケータ

メモリ管理領域アロケータは、以下のようないくつかの目的を満たす必要があります:

  1. 保存が必要なメモリプールは保護されるべきです。
  2. 十分なページフレームがなく、プロセスがブロックすることが許可されている場合、いくつかのページフレームが解放され、再び割り当てられたときに、ページフレームリサイクルアルゴリズムがトリガーされるべきです。
  3. 可能な限り、小さいけれど貴重なZONE_DMA領域にメモリを確保してください。

__alloc_pages()

struct page * fastcall   
__alloc_pages(gfp_t gfp_mask, unsigned int order,   
        struct zonelist *zonelist)   
{   
    const gfp_t wait = gfp_mask & __GFP_WAIT;   
    struct zone **z;   
    struct page *page;   
    struct reclaim_state reclaim_state;   
    struct task_struct *p = current;   
    int do_retry;   
    int alloc_flags;   
    int did_some_progress;   
    might_sleep_if(wait);   
   
restart:   
    //zonelist->zonesは、メモリ割り当て時にメモリ管理ゾーンが使用される順序を表す   
    z = zonelist->zones;     
   
    //はゾーン>page_lowをメモリ割り当てのベースラインとして使用する。   
    page = get_page_from_freelist(gfp_mask|__GFP_HARDWALL, order,   
                zonelist, ALLOC_WMARK_LOW|ALLOC_CPUSET);   
    //ページ・フレームを割り当て、終了する     
    if (page)   
        goto got_pg;   
    //没有分配到页框,   唤醒kswapd进行内存回收   
    do {   
        wakeup_kswapd(*z, order);   
    } while (*(++z));   
   
    //kswapdプロセスを呼び出してメモリを再利用した後、2回目の再利用がZONE_DMA領域のページベース値で実行される。>min_pages,   
    alloc_flags = ALLOC_WMARK_MIN;   
    //現在のプロセスがリアルタイム・プロセスであり、割り込み中でない場合、またはプロセスが待機を許可されていない場合、メモリ割り当てがより多く行われる。   
    //は緊急であり、ALLOC_HARDERフラグを追加して、メモリのベースライン値を縮小する。   
    if ((unlikely(rt_task(p)) && !in_interrupt()) || !wait)   
        alloc_flags |= ALLOC_HARDER;   
    //__GFP_HIGH緊急メモリ・プールへのアクセスを許可するフラグ       
    if (gfp_mask & __GFP_HIGH)   
        alloc_flags |= ALLOC_HIGH;   
    //通常はGFP_ATOMICは、メモリが解放されるのを待つためにプロセスが休止することを許さない   
    if (wait)   
        alloc_flags |= ALLOC_CPUSET;   
   
    //ページを再取得する   
    page = get_page_from_freelist(gfp_mask, order, zonelist, alloc_flags);   
    //フェッチ、OK、終了   
    if (page)   
        goto got_pg;   
   
    //それでもページがフェッチできない場合は、システム・メモリが不足しているはずである。   
    //PF_MEMALLOCフラグは、メモリ割り当てに失敗したくない場合、緊急メモリを使用できることを意味する。   
    if (((p->flags & PF_MEMALLOC) || unlikely(test_thread_flag(TIF_MEMDIE)))   
            && !in_interrupt()) {   
        //__GFP_NOMEMALLOCはメモリ割り当てが許可されていないことを示し、3回目のスキャンが実行され、3回目のスキャンは閾値判定が無視されることを示す。,   
        //緊急メモリを移動する   
        if (!(gfp_mask & __GFP_NOMEMALLOC)) {   
nofail_alloc:   
            /* go through the zonelist yet again, ignoring mins */   
            page = get_page_from_freelist(gfp_mask, order,   
                zonelist, ALLOC_NO_WATERMARKS);   
            //割り当てられたページ、終了   
            if (page)   
                goto got_pg;   
            //ページが割り当てられていない。   
            if (gfp_mask & __GFP_NOFAIL) {   
                blk_congestion_wait(WRITE, HZ/50);   
                goto nofail_alloc;   
            }   
        }   
        goto nopage;   
    }   
   
    //プロセスはスリープすることも待機することも許されない。   
    if (!wait)   
        goto nopage;   
   
rebalance:   
    //実行をスケジューリングする必要がある他のプロセスがあるかどうかをチェックする。   
    cond_resched();   
   
    /* We now go into synchronous reclaim */   
    cpuset_memory_pressure_bump();   
    //プロセスのPFを設定する_MEMALLOCフラグ・ビット   
    p->flags |= PF_MEMALLOC;   
    reclaim_state.reclaimed_slab = 0;   
    p->reclaim_state = &reclaim_state;   
    //メモリ再利用のためにメモリ再利用関数を呼び出す。   
    did_some_progress = try_to_free_pages(zonelist->zones, gfp_mask);   
   
    p->reclaim_state = NULL;   
    p->flags &= ~PF_MEMALLOC;   
    //メモリの再利用処理に時間がかかりすぎる可能性がある。   
    cond_resched();   
    //メモリの再利用がいくつかのページを再利用する場合   
    if (likely(did_some_progress)) {   
        //ページの再割り当てを試みる   
        page = get_page_from_freelist(gfp_mask, order,   
                        zonelist, alloc_flags);   
        if (page)   
            goto got_pg;   
    } else if ((gfp_mask & __GFP_FS) && !(gfp_mask & __GFP_NORETRY)) {   
        //この場合、ページ・リクレイムはどのページ・フレームもリクレイムしなかった。   
        //クリティカル・タイム。!!!他のプロセスを1つ終了させ、すべてをやり直す!   
        page = get_page_from_freelist(gfp_mask|__GFP_HARDWALL, order,   
                zonelist, ALLOC_WMARK_HIGH|ALLOC_CPUSET);   
        if (page)   
            goto got_pg;   
   
        out_of_memory(zonelist, gfp_mask, order);   
        goto restart;   
    }   
   
       
    do_retry = 0;   
    //gfp_maskタグのGFP_NORETRYは、メモリ管理領域の再スキャンを許可するかどうかを示す   
    if (!(gfp_mask & __GFP_NORETRY)) {   
        //GFP_REPEAT,GFP_NOFAILは、どちらもメモリ管理領域を繰り返しスキャンする必要がある。   
        if ((order <= 3) || (gfp_mask & __GFP_REPEAT))   
            do_retry = 1;   
        if (gfp_mask & __GFP_NOFAIL)   
            do_retry = 1;   
    }   
    //メモリ割り当てを再度行う必要がある場合は、blk_alloc_pages()を呼び出す。_congestion_wait()しばらく待つ。,   
    if (do_retry) {   
        blk_congestion_wait(WRITE, HZ/50);   
        goto rebalance;   
    }   
   
nopage:   
    if (!(gfp_mask & __GFP_NOWARN) && printk_ratelimit()) {   
        dump_stack();   
        show_mem();   
    }   
got_pg:   
    return page;   
}   
3.3.2get_page_from_freelist   
    パートナー・システムに対するコードでのメモリ割り当てのコード実装は、実際にはget_alloc_pages()の中にある。_page_from_freelist()を使用する。   
static struct page *   
get_page_from_freelist(gfp_t gfp_mask, unsigned int order,   
        struct zonelist *zonelist, int alloc_flags)   
{   
    struct zone **z = zonelist->zones;   
    struct page *page = NULL;   
    int classzone_idx = zone_idx(*z);   
    do {   
        if ((alloc_flags & ALLOC_CPUSET) &&   
                !cpuset_zone_allowed(*z, gfp_mask))   
            continue;   
   
        if (!(alloc_flags & ALLOC_NO_WATERMARKS)) {   
            unsigned long mark;   
            //alloc_flagsは、このメモリ管理領域からページ・フレームを割り当てることができるかどうかの指標となる、マークのサイズを決定する   
            //は以下のゾーンに使用される_watermark_ok   
            if (alloc_flags & ALLOC_WMARK_MIN)   
                mark = (*z)->pages_min;   
            else if (alloc_flags & ALLOC_WMARK_LOW)   
                mark = (*z)->pages_low;   
            else   
                mark = (*z)->pages_high;   
            //zone__alloc_pages()の残りのメモリは条件を満たすことができないので、メモリを取り戻す必要がある。   
            if (!zone_watermark_ok(*z, order, mark,   
                    classzone_idx, alloc_flags))   
                if (!zone_reclaim_mode ||   
                    !zone_reclaim(*z, gfp_mask, order))   
                    continue;   
        }   
        //bufferdを呼び出す_rmqueueパートナー・システムにメモリ・ページ・フレームを要求する   
        page = buffered_rmqueue(zonelist, *z, order, gfp_mask);   
        if (page) {   
            break;   
        }   
    } while (*(++z) != NULL);   
    return page;   
}   
3.3.3 zone_watermark_ok   
int zone_watermark_ok(struct zone *z, int order, unsigned long mark,   
              int classzone_idx, int alloc_flags)   
{   
    //基本値はmarkである。>low,high,minの値のいずれかを使用する。   
    long min = mark, free_pages = z->free_pages - (1 << order) + 1;   
    int o;   
    // __GFP_WAITが設定され、通常はALLOC_HIGHが設定されている場合は、最小値から1/2を減算する。   
    if (alloc_flags & ALLOC_HIGH)   
        min -= min / 2;   
    // __GFP_WAITがセットされるのは、現在のプロセスがリアルタイム・プロセスであり、すでに   
    //メモリを確保し、次に_flagメソッドのALLOCは_HARDERが設定されると、最小値はさらに1/4減少する。   
    if (alloc_flags & ALLOC_HARDER)   
        min -= min / 4;   
    //空闲页面少于min 加上管理区的保留页面数   
    if (free_pages <= min + z->lowmem_reserve[classzone_idx])   
        return 0;   
    //サイズ0オーダーのページが連鎖したフリー・テーブルの場合、連鎖した各テーブルに対して   
    //少なくともmin/2ページ・フレームを持つk次ブロックの場合   
    for (o = 0; o < order; o++) {   
        /* At the next order, this order's pages become unavailable */   
        free_pages -= z->free_area[o].nr_free << o;   
   
        /* Require fewer higher order pages to be free */   
        min >>= 1;   
   
        if (free_pages <= min)   
            return 0;   
    }   
    return 1;      
}   

Read next

Androidゲームエンジンlibgdx使い方チュートリアル2:グラフィックの描き方

この記事は、主にlibgdxのグラフィック描画の使用方法を説明するために、記事では、グラフィック描画の方法と手順を詳細に説明し、あなたが注意深く読むことができます。\nまず第一に、理解するために英語の解釈に従って、テクスチャとは何かを理解する:元のフォーマットから画像をデコードし、GPUにアップロードされたテクスチャとして知られています。

Sep 24, 2013 · 9 min read