Mem_map

특징

  • mem_map은 모든 물리 메모리 페이지 프레임에 대한 정보를 담아둔 page 구조체 배열이다.
  • NUMA 시스템과 UMA 시스템에서의 접근 방법이 다르고 메모리 모델별로도 접근 방법이 다르다.
  • mem_map의 초기화 함수 경로는 아키텍처 및 커널 설정마다 다르다.
    • arm with flatmem
    • arm with sparsemem
      • setup_arch() → paging_init() → bootmem_init()  → sparse_init()
    • arm64 with sparsemem
      • setup_arch() → bootmem_init() → sparse_init()

mem_map-1b

 

Flat Memory with mem_map

  • NEED_MULTIPLE_NODES 커널 옵션이 사용되지 않을 때 *mem_map 포인터 변수는 하나의 page[] 구조체 배열을 가리킨다.
  • FLAT_NODE_MEM_MAP 커널 옵션을 사용하는 경우 contig_page_data.node_mem_map를 통해서 page[] 구조체 배열을 가리킨다.

mm-8b

 

Discontiguous Memory with mem_map

  • node_data[].node_mem_map를 통해서 page[] 구조체 배열을 가리킨다.
  • x86_32에서는 섹션 to 노드 매핑을 하는 테이블을 별도로 구현하여 사용한다.
  • 실제 사용하는 메모리에 대해서만 관리하고 hole 영역에 대해서는 전혀 관리하지 않는다.

mm-6

 

Sparse Memory with mem_map

  • 하나의 mem_section 구조체는 PAGES_PER_SECTION 갯 수 만큼의 page[] 배열을 가리키고 관리한다.
  • hole을 포함한 메모리 전체를 mem_section을 통해 관리한다.
  • hole 영역에 대해서도 mem_section 테이블에 엔트리가 존재하고 멤버 변수 node_mem_map 값을 null로 하는 것으로 미사용 섹션을 의미한다. 그 외 사용되는 메모리의 관리는 mem_section[]과 page[] 구조체를 통해 관리된다..
  • 섹션의 크기는 페이지 테이블에서 사용하는 섹션 크기가 아니라 Sparsemem에서 사용하는 크기로 수십MB ~ 수GB 크기로 online/offline(pluglable memory)을 관리하기 위한 최소 크기 단위이므로 혼동되지 않도록 한다.
  • 메모리 할당 관리를 위해 mem_section 구조체의 구현이 두 개로 나뉘어 있다.
    •  SPARSEMEM_STATIC
      • mem_section[][] 이중 배열로 만들어 컴파일 타임에 각각의 page[] 배열을 가리키고 관리한다.
      • mem_section[][] 이중 배열은 hole을 포함한 모든 메모리를 관리하므로 hole의 size가 적절한 경우 별도의 dynamic 할당 없이 사용하므로 간편하나 hole size가 매우 큰 경우에는 mem_section 구조체 x hole 섹션 수 만큼의 메모리 낭비를 하므로 적합하지 않다.
    •  SPARSEMEM_EXTREME
      • *mem_section[] 포인터 배열만 컴파일 타임에 만들어지고 실제 mem_section[] 배열은 별도로 필요한 만큼 메모리를 할당 받아 page[] 배열을 가리키고 관리한다.
      • * mem_section[] 포인터 배열을 사용하여 hole size가 매우 큰 경우를 대비하여 메모리 낭비를 줄일 수 있도록 설계되었다.
    • NODE_NOT_IN_PAGE_FLAGS
      • page 구조체의 flags 멤버변수에 node 번호를 기록하지 않는 경우 node 번호를 별도로 섹션에 대해 1:1로 매핑하여 저장한다.
      • 32비트 시스템에서 비트 자리가 부족하여 노드 번호를 기록하지 못하는 경우 사용

 

다음 그림은 SPARSEMEM_EXTREME을 사용하지 않는 것으로 32bit arm – Realview-PBX 보드의 사용예이다.

mm-7b

다음 그림은 SPARSEMEM_EXTREME을 사용하는 것으로 arm64 아키텍처에서 4G DRAM을 사용한 예이다.

mm-9a

 

Zone 별 mem_map 관리

mem_map-3

  • rpi2: ZONE_NORMAL만 사용한다.
  • ARM의 경우 커널 설정에 따라 ZONE_DMA 및 ZONE_HIGHMEM을 사용할 수 있다.

 

mem_map에서 사용하는 page 구조체

mem_map-2a

  • flags
    PG_locked:0
    PG_uptodate:3
    PG_dirty:4
    PG_active:6
    PG_highmem:8
    PG_reserved:11
  • _count
    • 페이지 프레임 사용 카운터
  • _mapcount
    • pte의 이 페이지 사용 카운터
  • private
    • swap-space 관리 사용
  • index
    • mmap의 프레임 offset
  • lru.next
    • 다음 엘레멘트
  • lru.prev
    • 이전 엘레멘트

 

struct page

include/linux/mm_types.h

/*
 * Each physical page in the system has a struct page associated with
 * it to keep track of whatever it is we are using the page for at the
 * moment. Note that we have no way to track which tasks are using
 * a page, though if it is a pagecache page, rmap structures can tell us
 * who is mapping it.
 *
 * The objects in struct page are organized in double word blocks in
 * order to allows us to use atomic double word operations on portions
 * of struct page. That is currently only used by slub but the arrangement
 * allows the use of atomic double word operations on the flags/mapping
 * and lru list pointers also.
 */
struct page {
        /* First double word block */
        unsigned long flags;            /* Atomic flags, some possibly
                                         * updated asynchronously */
        union {
                struct address_space *mapping;  /* If low bit clear, points to
                                                 * inode address_space, or NULL.
                                                 * If page mapped as anonymous
                                                 * memory, low bit is set, and
                                                 * it points to anon_vma object:
                                                 * see PAGE_MAPPING_ANON below.
                                                 */
                void *s_mem;                    /* slab first object */
        };

 

        /* Second double word */
        struct {
                union {
                        pgoff_t index;          /* Our offset within mapping. */
                        void *freelist;         /* sl[aou]b first free object */
                        bool pfmemalloc;        /* If set by the page allocator,
                                                 * ALLOC_NO_WATERMARKS was set
                                                 * and the low watermark was not
                                                 * met implying that the system
                                                 * is under some pressure. The
                                                 * caller should try ensure
                                                 * this page is only used to
                                                 * free other pages.
                                                 */
                };

                union {
#if defined(CONFIG_HAVE_CMPXCHG_DOUBLE) && \
        defined(CONFIG_HAVE_ALIGNED_STRUCT_PAGE)
                        /* Used for cmpxchg_double in slub */
                        unsigned long counters;
#else
                        /*
                         * Keep _count separate from slub cmpxchg_double data.
                         * As the rest of the double word is protected by
                         * slab_lock but _count is not.
                         */
                        unsigned counters;
#endif

                        struct {

                                union {
                                        /*
                                         * Count of ptes mapped in
                                         * mms, to show when page is
                                         * mapped & limit reverse map
                                         * searches.
                                         *
                                         * Used also for tail pages
                                         * refcounting instead of
                                         * _count. Tail pages cannot
                                         * be mapped and keeping the
                                         * tail page _count zero at
                                         * all times guarantees
                                         * get_page_unless_zero() will
                                         * never succeed on tail
                                         * pages.
                                         */
                                        atomic_t _mapcount;

                                        struct { /* SLUB */
                                                unsigned inuse:16;
                                                unsigned objects:15;
                                                unsigned frozen:1;
                                        };
                                        int units;      /* SLOB */
                                };
                                atomic_t _count;                /* Usage count, see below. */
                        };
                        unsigned int active;    /* SLAB */
                };
        };

 

        /* Third double word block */
        union {
                struct list_head lru;   /* Pageout list, eg. active_list
                                         * protected by zone->lru_lock !
                                         * Can be used as a generic list
                                         * by the page owner.
                                         */
                struct {                /* slub per cpu partial pages */
                        struct page *next;      /* Next partial slab */
#ifdef CONFIG_64BIT
                        int pages;      /* Nr of partial slabs left */
                        int pobjects;   /* Approximate # of objects */
#else
                        short int pages;
                        short int pobjects;
#endif
                };

                struct slab *slab_page; /* slab fields */
                struct rcu_head rcu_head;       /* Used by SLAB
                                                 * when destroying via RCU
                                                 */
                /* First tail page of compound page */
                struct {
                        compound_page_dtor *compound_dtor;
                        unsigned long compound_order;
                };

#if defined(CONFIG_TRANSPARENT_HUGEPAGE) && USE_SPLIT_PMD_PTLOCKS
                pgtable_t pmd_huge_pte; /* protected by page->ptl */
#endif
        };

 

        /* Remainder is not double word aligned */
        union {
                unsigned long private;          /* Mapping-private opaque data:
                                                 * usually used for buffer_heads
                                                 * if PagePrivate set; used for
                                                 * swp_entry_t if PageSwapCache;
                                                 * indicates order in the buddy
                                                 * system if PG_buddy is set.
                                                 */
#if USE_SPLIT_PTE_PTLOCKS
#if ALLOC_SPLIT_PTLOCKS
                spinlock_t *ptl;
#else
                spinlock_t ptl;
#endif
#endif
                struct kmem_cache *slab_cache;  /* SL[AU]B: Pointer to slab */
                struct page *first_page;        /* Compound tail pages */
        };

#ifdef CONFIG_MEMCG
        struct mem_cgroup *mem_cgroup;
#endif

        /*
         * On machines where all RAM is mapped into kernel address space,
         * we can simply calculate the virtual address. On machines with
         * highmem some memory is mapped into kernel virtual memory
         * dynamically, so we need a place to store that address.
         * Note that this field could be 16 bits on x86 ... 😉
         *
         * Architectures with slow multiplication can define
         * WANT_PAGE_VIRTUAL in asm/page.h
         */
#if defined(WANT_PAGE_VIRTUAL)
        void *virtual;                  /* Kernel virtual address (NULL if
                                           not kmapped, ie. highmem) */
#endif /* WANT_PAGE_VIRTUAL */

#ifdef CONFIG_KMEMCHECK
        /*
         * kmemcheck wants to track the status of each byte in a page; this
         * is a pointer to such a status block. NULL if not tracked.
         */
        void *shadow;
#endif

#ifdef LAST_CPUPID_NOT_IN_PAGE_FLAGS
        int _last_cpupid;
#endif
}

 

pg_data_t 구조체 타입

include/linux/mmzone.h

/*
 * The pg_data_t structure is used in machines with CONFIG_DISCONTIGMEM
 * (mostly NUMA machines?) to denote a higher-level memory zone than the
 * zone denotes.
 *
 * On NUMA machines, each NUMA node would have a pg_data_t to describe
 * it's memory layout.
 *
 * Memory statistics and page replacement data structures are maintained on a
 * per-zone basis.
 */
struct bootmem_data;
typedef struct pglist_data {
        struct zone node_zones[MAX_NR_ZONES];
        struct zonelist node_zonelists[MAX_ZONELISTS];
        int nr_zones;
#ifdef CONFIG_FLAT_NODE_MEM_MAP /* means !SPARSEMEM */
        struct page *node_mem_map;
#ifdef CONFIG_PAGE_EXTENSION
        struct page_ext *node_page_ext;
#endif
#endif
#ifndef CONFIG_NO_BOOTMEM
        struct bootmem_data *bdata;
#endif
#ifdef CONFIG_MEMORY_HOTPLUG
        /*
         * Must be held any time you expect node_start_pfn, node_present_pages
         * or node_spanned_pages stay constant.  Holding this will also
         * guarantee that any pfn_valid() stays that way.
         *
         * pgdat_resize_lock() and pgdat_resize_unlock() are provided to
         * manipulate node_size_lock without checking for CONFIG_MEMORY_HOTPLUG.
         *
         * Nests above zone->lock and zone->span_seqlock
         */
        spinlock_t node_size_lock;
#endif
        unsigned long node_start_pfn;
        unsigned long node_present_pages; /* total number of physical pages */
        unsigned long node_spanned_pages; /* total size of physical page
                                             range, including holes */
        int node_id;
        wait_queue_head_t kswapd_wait;
        wait_queue_head_t pfmemalloc_wait;
        struct task_struct *kswapd;     /* Protected by
                                           mem_hotplug_begin/end() */
        int kswapd_max_order;
        enum zone_type classzone_idx;
#ifdef CONFIG_NUMA_BALANCING
        /* Lock serializing the migrate rate limiting window */
        spinlock_t numabalancing_migrate_lock;

        /* Rate limiting time interval */
        unsigned long numabalancing_migrate_next_window;

        /* Number of pages migrated during the rate limiting time interval */
        unsigned long numabalancing_migrate_nr_pages;
#endif
} pg_data_t;
  • node_mem_map
    • 관리하는 page[] 구조체 배열을 가리킨다.

 

참고

답글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.