Linux - 3.14.12 메모리 관리 노트 [파트너 관리 알고리즘 (1)]

12031 단어
앞에서 memblock 알고리즘, 커 널 시트 의 구축, 메모리 관리 프레임 워 크 의 구축 을 분 석 했 는데 모두 x86 처리 setup 입 니 다.arch () 함수 에서 초기 화 된 것 은 지역 에 따라 적합 하 며 프로세서 의 특징 이 뚜렷 합 니 다.그리고 startkernel () 의 다음 초기 화 는 Liux 에서 통용 되 는 메모리 관리 알고리즘 프레임 워 크 입 니 다.
build_all_zonelists () 는 메모리 분배 기 에 사용 되 는 저장 노드 의 관리 구역 링크 를 초기 화 하 는 데 사용 되 며 메모리 관리 알고리즘 (파트너 관리 알고리즘) 을 위 한 준비 작업 입 니 다.구체 적 인 실현:
【file:/mm/page_alloc.c】
/*
 * Called with zonelists_mutex held always
 * unless system_state == SYSTEM_BOOTING.
 */
void __ref build_all_zonelists(pg_data_t *pgdat, struct zone *zone)
{
    set_zonelist_order();
 
    if (system_state == SYSTEM_BOOTING) {
        __build_all_zonelists(NULL);
        mminit_verify_zonelist();
        cpuset_init_current_mems_allowed();
    } else {
#ifdef CONFIG_MEMORY_HOTPLUG
        if (zone)
            setup_zone_pageset(zone);
#endif
        /* we have to stop all cpus to guarantee there is no user
           of zonelist */
        stop_machine(__build_all_zonelists, pgdat, NULL);
        /* cpuset refresh routine should be here */
    }
    vm_total_pages = nr_free_pagecache_pages();
    /*
     * Disable grouping by mobility if the number of pages in the
     * system is too low to allow the mechanism to work. It would be
     * more accurate, but expensive to check per-zone. This check is
     * made on memory-hotadd so a system can start with mobility
     * disabled and enable it later
     */
    if (vm_total_pages < (pageblock_nr_pages * MIGRATE_TYPES))
        page_group_by_mobility_disabled = 1;
    else
        page_group_by_mobility_disabled = 0;
 
    printk("Built %i zonelists in %s order, mobility grouping %s. "
        "Total pages: %ld
", nr_online_nodes, zonelist_order_name[current_zonelist_order], page_group_by_mobility_disabled ? "off" : "on", vm_total_pages); #ifdef CONFIG_NUMA printk("Policy zone: %s
", zone_names[policy_zone]); #endif }

먼저 setzonelist_order():
【file:/mm/page_alloc.c】
static void set_zonelist_order(void)
{
    current_zonelist_order = ZONELIST_ORDER_ZONE;
}

이 곳 은 zonelist 의 순 서 를 설정 하 는 데 사 용 됩 니 다. ZONELISTORDER_ZONE 은 순서 (- zonetype, [node] distance) 를 나타 내 는 데 사용 되 며, ZONELIST 도 있다.ORDER_NODE 는 순서 ([node] distance, - zonetype) 를 표시 합 니 다.그러나 NUMA 환경 에 만 차이 가 있 고 비 NUMA 환경 은 차이 가 없다.
시스템 상태 systemstate 는 SYSTEMBOOTING, 시스템 상 태 는 startkernel 마지막 함수 rest 까지 실행init 이후 에 야 SYSTEMRUNNING, 그래서 초기 화 할 때 다음build_all_zonelists () 함수:
【file:/mm/page_alloc.c】
/* return values int ....just for stop_machine() */
static int __build_all_zonelists(void *data)
{
    int nid;
    int cpu;
    pg_data_t *self = data;
 
#ifdef CONFIG_NUMA
    memset(node_load, 0, sizeof(node_load));
#endif
 
    if (self && !node_online(self->node_id)) {
        build_zonelists(self);
        build_zonelist_cache(self);
    }
 
    for_each_online_node(nid) {
        pg_data_t *pgdat = NODE_DATA(nid);
 
        build_zonelists(pgdat);
        build_zonelist_cache(pgdat);
    }
 
    /*
     * Initialize the boot_pagesets that are going to be used
     * for bootstrapping processors. The real pagesets for
     * each zone will be allocated later when the per cpu
     * allocator is available.
     *
     * boot_pagesets are used also for bootstrapping offline
     * cpus if the system is already booted because the pagesets
     * are needed to initialize allocators on a specific cpu too.
     * F.e. the percpu allocator needs the page allocator which
     * needs the percpu allocator in order to allocate its pagesets
     * (a chicken-egg dilemma).
     */
    for_each_possible_cpu(cpu) {
        setup_pageset(&per_cpu(boot_pageset, cpu), 0);
 
#ifdef CONFIG_HAVE_MEMORYLESS_NODES
        /*
         * We now know the "local memory node" for each node--
         * i.e., the node of the first zone in the generic zonelist.
         * Set up numa_mem percpu variable for on-line cpus. During
         * boot, only the boot cpu should be on-line; we'll init the
         * secondary cpus' numa_mem as they come on-line. During
         * node/memory hotplug, we'll fixup all on-line cpus.
         */
        if (cpu_online(cpu))
            set_cpu_numa_mem(cpu, local_memory_node(cpu_to_node(cpu)));
#endif
    }
 
    return 0;
}

그 중 buildzonelists_node () 함수 구현:
【file:/mm/page_alloc.c】
/*
 * Builds allocation fallback zone lists.
 *
 * Add all populated zones of a node to the zonelist.
 */
static int build_zonelists_node(pg_data_t *pgdat, struct zonelist *zonelist,
                int nr_zones)
{
    struct zone *zone;
    enum zone_type zone_type = MAX_NR_ZONES;
 
    do {
        zone_type--;
        zone = pgdat->node_zones + zone_type;
        if (populated_zone(zone)) {
            zoneref_set_zone(zone,
                &zonelist->_zonerefs[nr_zones++]);
            check_highest_zone(zone_type);
        }
    } while (zone_type);
 
    return nr_zones;
}

populated_zone () 관리 구역 zone 을 판단 하 는 presentpages 구성원 이 0 인지, 0 이 아니라면 이 관리 구역 에 페이지 가 존재 한 다 는 뜻 이 며, zoneref 를 통 해set_zone () 이 를 zonelist 에 설정 한zonerefs 안, checkhighest_zone () 은 NUMA 를 열지 않 은 상태 에서 빈 함수 입 니 다.buildzonelists_node () 는 사실 ZONEHIGHMEM—>ZONE_NORMAL—>ZONE_DMA 의 순 서 를 반복 해서zonerefs 에 서 는 메모 리 를 신청 하 는 대가 가 저렴 한 것 에서 비 싼 것 으로 나 타 났 습 니 다. 이것 은 메모 리 를 분배 할 때의 예비 순서 입 니 다.
build 로 돌아 가기zonelists () 함수 에서 코드 는 로 컬 메모리 관리 구역 을 분배 예비 순서 로 정렬 하 는 것 을 보 여 줍 니 다. 그 다음 에 분배 메모리 대가 가 로 컬 보다 낮 고 마지막 에 분배 메모리 대가 가 로 컬 보다 높 습 니 다.
build 분석 완료zonelists (), 다시 돌아 가기build_all_zonelists () buildzonelist_cache():
【file:/mm/page_alloc.c】
/* non-NUMA variant of zonelist performance cache - just NULL zlcache_ptr */
static void build_zonelist_cache(pg_data_t *pgdat)
{
    pgdat->node_zonelists[0].zlcache_ptr = NULL;
}

이 함수 와 CONFIGNUMA 관련, zlcache 관련 멤버 를 설정 합 니 다.이 설정 을 열지 않 았 기 때문에 NULL 로 직접 설정 합 니 다.
build 기반all_zonelists () 호출build_all_zonelists () 입 참 은 NULL 로 알 수 있 습 니 다build_all_zonelists () 가 실행 하 는 코드 는:
for_each_online_node(nid) {

    pg_data_t *pgdat = NODE_DATA(nid);

    build_zonelists(pgdat);

    build_zonelist_cache(pgdat);

}

주로 각 메모리 관리 노드 node 에 있 는 각자 의 메모리 관리 구역 zone 의 메모리 분배 순 서 를 설정 합 니 다.
__build_all_zonelists () 다음은:
for_each_possible_cpu(cpu) {

    setup_pageset(&per_cpu(boot_pageset, cpu), 0);

#ifdef CONFIG_HAVE_MEMORYLESS_NODES
    if (cpu_online(cpu))
        set_cpu_numa_mem(cpu, local_memory_node(cpu_to_node(cpu)));
#endif

}

그 중 CONFIGHAVE_MEMORYLESS_NODES 가 설정 되 지 않 았 습 니 다. 주로 setup 을 분석 합 니 다.pageset():
【file:/mm/page_alloc.c】
static void setup_pageset(struct per_cpu_pageset *p, unsigned long batch)
{
    pageset_init(p);
    pageset_set_batch(p, batch);
}

setup_pageset () 에서 호출 된 두 함수 가 비교적 간단 하 니 바로 넘 어 갑 니 다.먼저:
【file:/mm/page_alloc.c】
static void pageset_init(struct per_cpu_pageset *p)
{
    struct per_cpu_pages *pcp;
    int migratetype;
 
    memset(p, 0, sizeof(*p));
 
    pcp = &p->pcp;
    pcp->count = 0;
    for (migratetype = 0; migratetype < MIGRATE_PCPTYPES; migratetype++)
        INIT_LIST_HEAD(&pcp->lists[migratetype]);
}

pageset_init () 는 주로 struct percpu_pageset 구조 체 는 초기 화 되 고 pagesetset_batch () 는 설정 합 니 다.pageset_set_batch () 구현:
【file:/mm/page_alloc.c】
/*
 * pcp->high and pcp->batch values are related and dependent on one another:
 * ->batch must never be higher then ->high.
 * The following function updates them in a safe manner without read side
 * locking.
 *
 * Any new users of pcp->batch and pcp->high should ensure they can cope with
 * those fields changing asynchronously (acording the the above rule).
 *
 * mutex_is_locked(&pcp_batch_high_lock) required when calling this function
 * outside of boot time (or some other assurance that no concurrent updaters
 * exist).
 */
static void pageset_update(struct per_cpu_pages *pcp, unsigned long high,
        unsigned long batch)
{
       /* start with a fail safe value for batch */
    pcp->batch = 1;
    smp_wmb();
 
       /* Update high, then batch, in order */
    pcp->high = high;
    smp_wmb();
 
    pcp->batch = batch;
}
 
/* a companion to pageset_set_high() */
static void pageset_set_batch(struct per_cpu_pageset *p, unsigned long batch)
{
    pageset_update(&p->pcp, 6 * batch, max(1UL, 1 * batch));
}

setup_pageset () 함수 입 참 p 는 struct per 입 니 다.cpu_pageset 구조 체 의 지침, percpu_pageset 구 조 는 커 널 의 각 zone 이 모든 CPU 의 페이지 캐 시 관리 구조 에 사용 되 는 것 입 니 다.이 캐 시 는 로 컬 CPU 가 보 내 는 단일 메모리 요청 을 충족 시 키 기 위해 미리 분 배 된 페이지 를 포함 하고 있 습 니 다.and struct 타격 percpu_페이지 가 정의 하 는 pcp 는 이 관리 구조의 구성원 으로 구체 적 인 페이지 관리 에 사용 된다.원래 모든 관리 구 조 는 두 개의 pcp 배열 구성원 이 있 었 는데 그 안의 두 대기 열 은 각각 냉 페이지 와 열 페이지 관리 에 사용 되 었 으 며 현재 분석 한 3.14.12 버 전 은 두 가 지 를 합 쳐 냉 열 페이지 를 통일 적 으로 관리 하고 열 페이지 는 대기 열 앞 에 있 으 며 냉 페이지 는 대기 열 뒤에 있 습 니 다.일단 이렇게 많이 기억 하고 나중에 버디 알고리즘 을 사용 할 때 상세 하 게 분석 하 겠 습 니 다.
* * 이로써 알 수 있 습 니 다build_all_zonelists () 는 메모리 관리 프레임 워 크 가 후속 메모리 페이지 관리 알고리즘 을 준비 하고 메모리 관리 구역 zone 의 배분 순 서 를 배정 하 는 동시에 냉 열 페이지 관 리 를 초기 화 합 니 다. * *
마지막 build 로 돌아 가기all_zonelists () 함수.메모리 가 열 리 지 않 아 디 버 깅 기능 초기 화 CONFIGDEBUG_MEMORY_INIT,mminit_verify_zonelist () 는 빈 함수 입 니 다.
CONFIG 기반CPUSETS 설정 항목 이 열 린 상태 에서 cpusetinit_current_mems_allowed () 는 다음 과 같이 실현 합 니 다.
【file:/kernel/cpuset.c】
void cpuset_init_current_mems_allowed(void)
{
    nodes_setall(current->mems_allowed);
}

이 안의 current 는 cpuset 의 데이터 구조 로 cgroup 의 작업 에 사용 할 수 있 는 cpu 와 메모리 노드 를 관리 합 니 다.멤버 memsallowed, 이 멤버 는 nodemaskt 유형의 구조 체
【file:/include/linux/nodemask.h】
typedef struct { DECLARE_BITMAP(bits, MAX_NUMNODES); } nodemask_t;

이 구 조 는 사실 하나의 비트 필드 를 정의 하 는 것 입 니 다. 각 비트 는 하나의 메모리 노드 에 대응 하고 1 을 설치 하면 이 노드 의 메모리 가 사용 가능 하 다 는 것 을 표시 합 니 다.반면 nodessetall 은 이 필드 의 모든 위 치 를 1 로 설정 합 니 다.
마지막 으로 buildall_zonelists () 내 nrfree_pagecache_pages () 의 실현:
【file:/mm/page_alloc.c】
/**
 * nr_free_pagecache_pages - count number of pages beyond high watermark
 *
 * nr_free_pagecache_pages() counts the number of pages which are beyond the
 * high watermark within all zones.
 */
unsigned long nr_free_pagecache_pages(void)
{
    return nr_free_zone_pages(gfp_zone(GFP_HIGHUSER_MOVABLE));
}

안에서 호출 된 nrfree_zone_pages () 구현:
【file:/mm/page_alloc.c】
/**
 * nr_free_zone_pages - count number of pages beyond high watermark
 * @offset: The zone index of the highest zone
 *
 * nr_free_zone_pages() counts the number of counts pages which are beyond the
 * high watermark within all zones at or below a given zone index. For each
 * zone, the number of pages is calculated as:
 * managed_pages - high_pages
 */
static unsigned long nr_free_zone_pages(int offset)
{
    struct zoneref *z;
    struct zone *zone;
 
    /* Just pick one node, since fallback list is circular */
    unsigned long sum = 0;
 
    struct zonelist *zonelist = node_zonelist(numa_node_id(), GFP_KERNEL);
 
    for_each_zone_zonelist(zone, z, zonelist, offset) {
        unsigned long size = zone->managed_pages;
        unsigned long high = high_wmark_pages(zone);
        if (size > high)
            sum += size - high;
    }
 
    return sum;
}

nrfree_zone_pages () 는 모든 메모리 관리 구역 을 옮 겨 다 니 며 각 관리 구역 의 메모리 공간 을 구 합 니 다. 실질 적 으로 모든 관리 구역 이 분배 할 수 있 는 메모리 페이지 수 를 통계 하 는 데 사 용 됩 니 다.
이어서 buildall_zonelists () 뒤 에는 현재 시스템 의 메모리 페이지 상자 수 를 판단 하여 유동 그룹 메커니즘 (Mobility Grouping) 을 사용 할 지 여 부 를 결정 합 니 다. 이 메커니즘 은 큰 메모리 블록 을 할당 할 때 메모리 조각 을 줄 일 수 있 습 니 다.보통 메모리 가 충분 할 때 만 이 기능 을 사용 합 니 다. 그렇지 않 으 면 소 모 를 줄 이 고 성능 을 떨 어 뜨 릴 수 있 습 니 다.그 중 pageblocknr_페이지 는 파트너 시스템 의 최고 단계 블록 에 포 함 될 수 있 는 페이지 수 를 표시 합 니 다.
이로써 메모리 관리 프레임 워 크 알고리즘 이 기본적으로 준비 되 었 습 니 다.

좋은 웹페이지 즐겨찾기