Memory Model – (1)

3가지 메모리 모델

리눅스는 FLATMEM, DISCONTIGMEM 및 SPARSEMEM의 3가지 모델을 제공하며 아키텍처 머신들은 이 중 하나 이상을 지원하며 커널 빌드 시 지원되는 메모리 모델 중 하나를 선택하여 빌드한다.

mm-1b

 

메모리 모델별 특징

FLATMEM

  • 설치된 메모리 뱅크들의 주소가 연속된다.
  • UMA에서만 사용

 

DISCONTIGMEM

  • 설치된 메모리 뱅크들의 간격이 발생된다. 홀(hole) 발생 가능.
  • UMA 및 NUMA에서 사용 가능
  • ARM은 2010년 이 옵션을 삭제하였다.

 

SPARSEMEM

  • 설치된 메모리 뱅크들의 간격이 발생된다. 홀(hole) 발생 가능.
  • DISCONTIGMEM 보다 성능이 떨어질 수 있지만 hotplug memory 기능이 지원된다.
    • 그러나 특정 아키텍처에 구현된 SPARSEMEM_VMEMMAP을 사용하는 경우 DISCONTIGMEM 보다 더 빠르고 FLATMEM에 필적하는 성능을 보여 64비트 시스템에서는 점점 Sparsemem으로 이전하고 있음.
    • 참고: x86_64: Make sparsemem/vmemmap the default memory model
  • 섹션 단위로 online/offline(hotplug memory) 관리가 되며 사이즈는 수십MB~수G로 아키텍처별로 다르다.
  • UMA에서 사용하는 경우
    • ARM의 경우 SA1100(xscale 아키텍처), ARCH-RPC 및 일부 ARM 머신(RealView-PBX)에서 두 개의 메모리 뱅크가 떨어져 있어서 default로 메모리 모델로 사용한다.
    • 또한 메인스트림에 적용된 코드는 아니지만 ARM에서 MULTI_PLATFORM 커널 옵션을 사용하는 경우에도 사용할 수 있도록 패치가 준비되어 있다.

 

ARM with NUMA

  • arm 및 arm64 모두 현재 커널에서는 지원되지 않는다.
  • 2012년 RFC 패치에 arm에서 NUMA를 지원하였었고, linaro 커널도 arm에서 NUMA를 지원하지만 절대적인 필요성이 없어서인지 메인스트림에는 포함되지 않고 있다.
  • 2014년 패치에 NUMA를 지원하는 arm64가 나왔다.
mm-2a

메모리 모델 주요 커널 옵션

mm-4

메모리 모델 부가 커널 옵션

mm-5
  • NEED_MULTIPLE_NODES
    • DISCONTIGMEM 메모리 모델을 사용하거나 NUMA 시스템인 경우 사용할 수 있다.
    • NODE_DATA() 매크로를 사용하여 각 노드의 mem_map을 가리킬 수 있게 한다.
    • 전역 node_data[] 배열을 사용하여 노드별로 mem_map에 접근한다.
  •  FLAT_NODE_MEM_MAP
    • FLATMEM 또는 DISCONTIGMEM 메모리 모델에서 사용할 수 있다.
    • 노드 정보를 기술해 놓은 pglist_data 구조체의 멤버 변수 node_mem_map을 통해 mem_map에 접근한다.
  • HAVE_MEMORY_PRESENT
    • SPARSEMEM 메모리 모델을 위해 요청한 범위에 hole이 발생할 수 있으므로 각 섹션 별로 메모리 존재 여부를 관리할 수 있도록 한다.
  • SPARSEMEM_EXTREME
    • mem_section[] 배열을 dynamic 하게 할당 받아 사용하려고 할 때 사용
    • 주로 생성해야 할 섹션 수가 많은 시스템에서 사용한다.
  • SPARSEMEM_STATIC
    • 컴파일 시 static하게 할당해둔 mem_section[]을 사용한다.
    • 주로 생성해야 할 섹션 수가 적은 시스템에서 사용한다.
  • SPARSEMEM_VMEMMAP
    • vmemmap을 사용하여 빠르게 pfn과 page descriptor 주소를 변환할 수 있다.
    • vmalloc 주소 범위가 큰 64비트 시스템에서 사용한다.
  • MEMORY_HOTREMOVE
    • 메모리를 시스템 동작 중에 제거할 수 있다.
    • MEMORY_HOTPLUG, ARCH_ENABLE_MEMORY_HOTREMOVE, MIGRATION 옵션이 있을 때 선택할 수 있다.
  • MOVABLE_NODE
    • ◦X86_64 누마 시스템에서만 사용가능하며 메모리 단편화 지원을 위해 사용한다.
    • 한 노드를 movable 메모리로만 사용할 수 있게 할당할 수 있다.
    • HAVE_MEMBLOCK, NO_BOOTMEM 커널 옵션과 같이 사용된다.
  • MEMORY_HOTPLUG_SPARSE
    • SPARSEMEM && MEMORY_HOTPLUG에서 선택할 수 있다.
  • NO_BOOTMEM
    • 기존 커널에서 사용하였던 BOOTMEM early 메모리 할당자를 사용하지 않는다.
    • ARM은 기본적으로 BOOTMEM을 사용하지 않고 memblock으로 대체되었다. 대부분의 아키텍처에서도 점점 사용하지 않는 추세이다.
  • HAVE_MEMBLOCK
    • memblock early 메모리 할당자를 사용한다.
    • ARM은 기본적으로 사용하고 대부분의 아키텍처도 BOOTMEM 대신 early 메모리 할당자로 사용한다.
  • HIGHMEM
    • 32bit에서 커널에 미리 1:1 매핑되지 않아 커널이 직접 access를 해야 할 때마다 매핑하여 사용해야 하는 메모리 범위이다.
    • 32bit 시스템에서 시스템 메모리가 커널 공간보다 큰 경우 HIGHMEM 옵션을 사용하여 더 많은 메모리를 활용할 수 있게 한다.
  • HIGHPTE
    • 32bit 시스템에서 2차 PTE를 highmem에 할당한다.
    • 32bit 시스템에 수 기가 이상의 메모리를 사용하는 경우 2차 PTE를 highmem에 로드하게 하여 lowmem 메모리를 소모하지 않게 유도한다.
  • NODE_NOT_IN_PAGE_FLAGS
    • page 구조체의 flags 멤버변수에 노드 번호를 기록하지 못하는 경우 노드 번호를 별도로 섹션에 대해 1:1로 매핑하여 저장한다
    • 32비트 시스템에서 비트 자리가 부족하여 노드 번호를 기록하지 못하는 경우 사용

메모리 모델과 페이지 관리맵(mem_map)

전체 물리 메모리의 페이지 프레임 만큼 page 구조체 배열이 mem_map이라하는데 다음과 같이 각 메모리 모델별로 mem_map을 관리한다.
  • FLATMEM
    • *mem_map 포인터가 page[] 구조체 배열을 가리킨다.
    • 노드가 하나 이므로 전역 구조체 contig_page_data를 사용하는데 이 구조체의 멤버 변수 node_mem_map 역시 page[] 구조체 배열을 가리킨다.
  • DISCONTIGMEM
    • 노드가 두 개 이상이므로 node_data[] 배열을 사용하는데 이 구조체의 멤버 변수 node_mem_map은 각 노드와 관련된 page[] 구조체 배열을 가리킨다.
  • SPARSEMEM
    • 두 가지 방법을 사용한다.
      • SPARSEMEM_STATIC
        • 섹션 수 만큼 mem_section[] 배열을 컴파일 타임에 정적 메모리에 두고 각 섹션 엔트리의 멤버 변수 section_mem_map이 해당 섹션 크기 만큼의 페이지를 관리하는 page[] 구조체 배열을 가리킨다.
      • SPARSEMEM_EXTREAM
        • 섹션 수 만큼 *mem_section[] 포인터 배열을 정적 메모리에 두고 실제 각 mem_section[] 배열은 커널 초기화를 진행할 때 mem_section[] 배열을 dynamic하게 할당받아 메모리를 사용하고 mem_section[]의 사용은 SPARSEMEM_STATIC과 같다.
mm-3b

참고

 

3 thoughts to “Memory Model – (1)”

  1. 안녕하세요. 좋은 정보 게시해주셔서 정말 감사합니다.
    아직 다 읽어보진 않았지만 궁금한 점이 있어서 질문 남깁니다.

    먼저 이 글에서 지칭하시는 메모리 뱅크라는 것이
    DRAM Device 내에서 여러 개의 Memory array로 구성된 Bank를 의미하는 것인지,
    아니면 단순히 메모리 주소 영역의 한 묶음을 나타내는 뱅크(ex. 캐시 뱅크)를 의미하는 것인지 궁금합니다.

    또한 SPARSEMEM으로 설정할 경우 Section단위로 Memory Hotplug가 가능하다고 되어있는데,
    여기서 Section이 지칭하는 것은 하드웨어적으로 어떤 부분으로 봐야하는지 궁금합니다.
    말하자면 Section이 DRAM Device의 내부 구조 상 어떤 단위로 이어지는지 알고 싶습니다.

    1. 안녕하세요?
      메모리 뱅크는 논리적으로 언급하였습니다.
      다음 2개의 노드로 구성된 NUMA 시스템 예를 설명해보겠습니다.
      각 노드에 사용한 DRAM 컨트롤러는 최대 물리 메모리를 64G까지 사용할 수 있다고 가정합니다. 또한 물리메모리 시작 주소를 0x0부터 시작하였다고 가정합니다.
      1st 노드: DRAM Controller – 0x0000 0000_0000 ~ 0x0010 0000 0000 까지 관리
      2nd 노드: DRAM Controller – 0x0010 0000 0000 ~ 0x0020 0000 0000 까지 관리

      참고로 cpu 아키텍처 또는 SoC 디자인 시 두 번째 노드를 첫 번째 노드가 사용하는 64G 공간 다음에 붙일 수도 있고 적당히 떨어뜨려 배치할 수도 있습니다.

      사용자는 첫 번째 노드에 4GB 메모리를 4개 꽂고, 두 번째 노드에도 4GB 메모리를 4개 꽂았습니다. 이러한 경우 총 32GB를 구성한 경우죠.

      이런식으로 시스템을 구성하면 제가 표현한 논리적 메모리 뱅크는 2개입니다.
      (하드웨어에서 사용하는 메모리 뱅크, 메모리 슬롯 등등과 연결하여 생각하면 안됩니다)

      또 한가지 예를 들어봅니다.
      하나의 컨트롤러를 사용하더라도 아키텍처 설계 시 메모리 배치가 일정 용량을 초과하는 경우 일정 공간을 건너띄웁니다.
      DDR 메모리 컨트롤러가 64G까지 관리하지만 물리메모리에 배치할 때 다음과 같이 사용하지만 arm64의 경우는 다음과 같이 일정 공간을 벌려놓습니다.

      0x00 0000 0000 – 0x00 FFFF FFFF …
      0x00 8000 0000 – 0x00 FFFF FFFF (1st 뱅크: 첫 번째 물리 공간에 2G를 배치)
      0x01 0000 0000 – 0x08 7FFF FFFF …
      0x08 8000 0000 – 0x0F FFFF FFFF (2nd 뱅크: 메모리가 2G를 초과하는 경우 여기서 추가로 30G까지 배치)
      0x10 0000 0000 – 0x88 FFFF FFFF …
      0x88 0000 0000 – 0x8F FFFF FFFF (3rd 뱅크: 메모리가 32G를 초과하는 경우 여기서 나머지 32G까지 배치)

      이러한 경우 최대 3개의 뱅크가 존재하는 것일까요?

      만일 4G 메모리를 모두 꽂아 64G를 채웠습니다. 이 때에는 3개의 뱅크가 맞죠.
      그런데 만일 두 번째 뱅크 중간의 위치에 있는 4GB DRAM을 제거하였습니다.
      그럼 몇 개의 뱅크일까요? 따라서 이러한 것을 하드웨어와 연결하여 생각하지 마시고,
      커널 입장에서 물리 메모리가 한꺼번에 연달아 배치가 불가능하면 각각을 뱅크라고 생각하시면됩니다.

      – – –

      이번에는 섹션에 대해 말씀드립니다.
      일단 arm 아키텍처에서 언급하는 섹션 매핑(1M)를 언급하는 것은 아닙니다.
      동일한 용어를 사용하지만 리눅스 커널의 sparsemem에서 사용하는 섹션은 다른 것임을 먼저 말씀드립니다.

      섹션은 hotplug를 고려하여 메모리를 추가 및 삭제하거나,
      SOC 디자인 시 메모리의 배치가 어느 일정 규칙에 의해 배치되는 것을 고려하여 커널 컴파일 시 사이즈를 결정할 수 있습니다.
      물론 커널 파라메터나 CONFIG로 시작하는 커널 옵션으로 설정할 수 없고, 보통 SoC 개발자가 결정합니다.
      arm64의 경우 SECTION_SIZE_BITS=30을 사용하여 1GB를 디폴트로 사용합니다.
      이 사이즈는 작으면 수십M에서 큰 경우 수G 단위로 지정하여 사용하는데 하드웨어적으로 물리메모리가 추가/제거될 수 있는 용량을 선택합니다.
      예를 들어 내 서버가 최소 메모리의 증설이 512MB 단위이면 섹션 크기를 512MB로 설정합니다.
      아직 ARM 시스템에서 하드웨어적으로 메모리를 핫플러그 하거나 핫리무브하는 경우는 없지만 다른 아키텍처들은 가능한 서버들이 있습니다.
      또한 소프트웨어적으로는 하이퍼 바이저를 사용하여 리눅스 커널을 운영하는 경우 핫플러그 메모리 증설 단위를 128MB 단위로 운영하는 것이 효과적이다라고 판단하면 섹션 크기를 128M로 할 수 있습니다. (자주 사용되는 단위는 128MB, 256MB, 512MB, 1G, 4G 단위를 사용합니다)

답글 남기기

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