ARM64 페이지 테이블 -1- (Basic)

<kernel v5.10>

가상 주소 공간

ARM64는 32비트 주소 공간을 갖고 있던 ARM과 달리 대폭 커진 64비트의 주소 공간을 갖고 있다.

 

다음 그림과 같이 ARM64 커널에서는 64비트 가상 주소의 시작 부분과 끝부분의 가상 주소 공간을 사용한다.

 

MMU 디바이스 내부에 있는 2개의 TTBR 레지스터(Translation Table Base Register)가 가리키는 페이지 테이블을 통해 커널 주소 공간과 유저 주소 공간에 매핑된 물리 자원에 접근할 수 있다. 커널 주소 공간과 유저 주소 공간은 각자 페이지 테이블을 갖고 있다.
커널 주소 공간을 위한 페이지 테이블은 TTBR1 레지스터가 가리킨다. 유저 주소 공간을 위한 페이지 테이블은 TTBR0 레지스터가 가리킨다. 각 레지스터를 사용하면 CPU는 두 주소 공간에 접근할 수 있다.

 

 

페이지 테이블은 여러 개의 엔트리로 구성되어 있다. 페이지 테이블 엔트리 하나는 페이지 1개에 대한 물리 주소로 변환하는 데 사용한다. ARM64 커널에서는 페이지 크기로 4KB, 16KB, 64KB 중 하나를 사용할 수 있다(기본 크기는 4KB). 페이지 테이블의 유지 관리 편의를 위해 커널을 설정할 때 페이지 크기를 혼용하여 사용하지 않고 1개의 페이지 크기를 선택하여 사용한다.

 

 

 

ARM64 커널이 64비트 가상 주소 전부를 사용하는 것이 아니라 36, 39, 42, 47, 48 및 최대 52 비트와 같이 아키텍처가 지원하는 비트만을 선택해 사용할 수 있고 이 범위를 가상 주소로 사용한다.

 

예를 들어, 48비트를 선택해 ARM64 커널을 빌드하여 사용할 경우 주소 공간의 크기를 계산하기 위해 2^48을 연산하면 256TB임을 알 수 있다. 그리고 이 공간에 필요한 페이지 테이블의 엔트리 수를 계산하기 위해서는 페이지 크기로 나누어야 한다. 예를 들어, 커널이 4K 페이지를 사용하는 경우 256TB/4KB를 연산하면 640억 개(64G)의 엔트리 수가 산출됨을 알 수 있다.

다음 그림을 보면 VA_BITS = 48 커널 옵션을 사용해 48비트를 선택하여 주소 공간으로 0x00000000_00000000~0x0000ffff_ffffffff 범위를 사용할 수 있다.

  • ARM64 시스템이 지원하는 세 가지 페이지 크기(4KB, 16KB, 64KB)에 따라 페이지 테이블 엔트리 수가 결정됨을 알 수 있다.

 

각 태스크에 주어지는 가상 주소 공간마다 64G개의 엔트리를 제공하기에는 너무 많은 메모리가 낭비되므로 ARM64 커널은 테이블을 최소 2 레벨부터 최대 4 레벨로 나누어 사용한다.

다음 그림은 36비트(VA_BITS)를 사용하는 주소 공간을 나타내는 2 레벨 페이지 테이블을 구성한 것이다. 필요한 페이지 테이블 엔트리가 2048개로 줄어듦을 알 수 있다

  • 페이지 테이블 인덱스에 해당하는 비트가 페이지 오프셋에 사용하는 비트 – 3인 것에 주목한다.

 

페이지 테이블 개요

커널이 지원하는 페이지 테이블은 5 단계이며, 아키텍처마다 최대 적용 레벨이 다르다.

  • x86의 경우 최대 5 단계까지 지원한다. PGD -> P4D -> PUD -> PMD -> PTE
  • ARM64의 경우 최대 4 단계까지 지원한다. PGD -> PUD -> PMD -> PTE
    • ARM64 커널 코드에서는 PGD 함수에서 아무것도 하지 않고 곧장 P4D 함수를 호출하는 것을 종종 볼 수 있다.

 

다음은 ARM64 시스템에서 사용하는 페이지 테이블들이다.

  • PGD(Page Global Directory)
    • 모든 단계의 레벨 구성에서 사용된다.
      • 2 단계 레벨 구성에서는 2 레벨 테이블이다.
      • 3 단계 레벨 구성에서는 1 레벨 테이블이다.
      • 4 단계 레벨 구성에서는 0 레벨 테이블이다.
  • PUD(Page Upper Directory)
    • 총 단계 3 레벨 구성에서는 제외되며, 구성되어 사용될 때에는 1 레벨 테이블이다.
  • PMD(Page Mid-level Directory)
    • 총 단계 2 레벨 구성에서는 제외되며, 구성되어 사용될 때에는 2 레벨 테이블이다.
  • PTE(Page Table Entry)
    • 모든 단계의 레벨 구성에서 3 레벨 테이블로 사용된다.

 

ARM64 커널에서 사용할 수 있는 페이지 크기(4K, 16K, 64K)와 가상 주소 비트 수(36, 39, 42, 47, 48, 52)에 따른 조합을 알아본다. defconfig 기준으로 ARM64 커널은 4KB의 페이지와 가상 주소 비트 수(이하 VA_BITS)가 48로 구성된 4레벨 테이블을 사용한다.

  • 주의: 커널 v4.7-rc1 이전에는 디폴트 값으로 VA_BITS=39, 3 레벨로 구성된 테이블을 사용하였다.

 

페이지 크기와 VA_BITS 조합에 의한 각 페이지 테이블 구성

2단계 페이지 테이블

다음 그림은 2 레벨로 구성된 페이지 테이블(pgd와 pte)을 사용해서 페이지 프레임에 연결되는 두 가지 방법을 보여준다.

  • ARM 아키텍처에서 pgd는 2 레벨 테이블, pte는 3 레벨 테이블이다.
  • pmd 사이즈 블럭을 직접 연결하여 사용할 수 있다.

VA_BITS = 36인 경우를 알아보자. 이 경우에는 pgd와 pte 테이블이 각각 11비트를 사용하므로 엔트리는 2048개가 된다. 최종 엔트리가 16K 페이지를 가리키면 64G(2048 * 2048 * 16K) 주소에 해당하는 페이지 프레임을 변환할 수 있다.

 

3단계 페이지 테이블

다음 그림은 3 레벨로 구성된 페이지 테이블(pgd, pmd, pte)을 사용해서 페이지 프레임에 연결되는 세 가지 방법을 보여준다.

  • 커널 v5.0-rc1 부터 가장 아래 3 레벨 페이지 테이블 + 64K 페이지 프레임을 사용하는 경우 USER_VA_BITS=52도 사용할 수 있다.
  • ARM 아키텍처에서 pgd는 1 레벨 테이블, pmd는 2레벨 테이블, pte는 3 레벨 테이블이다.
  • pud 사이즈 및 pmd 사이즈 블럭을 직접 연결하여 사용할 수 있다. (16K 페이지의 경우는 pmd 사이즈 블럭만 가능하다)
  • 커널 메인라인에서 4T 블럭에 대한 블럭(pud 섹션 사이즈) 매핑은 아직 구현하지 않은 상태이다.

 

4단계 페이지 테이블

다음 그림은 4 레벨로 구성된 페이지 테이블(pgd, pud, pmd, pte)을 사용해서 페이지 프레임에 연결되는 두 가지 방법을 보여준다.

  • ARM 아키텍처에서 pgd는 0 레벨 테이블, pud는 1 레벨 테이블, pmd는 2레벨 테이블, pte는 3 레벨 테이블이다.
  • pud 사이즈 및 pmd 사이즈 블럭을 직접 연결하여 사용할 수 있다. (16K 페이지의 경우는 pmd 사이즈 블럭만 가능하다)

 

Contiguous Bits

pmd 및 pte 매핑 속성에만 사용할 수 있는 contiguous bit 속성이 있다. 연속된 PMD 또는 PTE 사이즈(CONT_PMD_SIZE, CONT_PTE_SIZE) 만큼의 공간을 매핑하는 경우 TLB 엔트리를 절약하여 성능을 높일 수 있는 매핑 방법이다.

  • 커널 v4.12-rc1에 채택되어 사용된다.
  • 연속되는 공간 사이즈
    •  CONT_PMD_SIZE
      • 4K 페이지: 16 * 2MB = 32MB
      • 16K 페이지: 32 * 32MB = 1GB
      • 64K 페이지: 32 * 512MB = 16G
    •  CONT_PTE_SIZE
      • 4K 페이지: 16 * 4K = 64KB
      • 16K 페이지: 128 * 16K = 2MB
      • 64K 페이지: 32 * 64K = 2MB

 

페이지 테이블 구성 예)

다음 그림은 대표적으로 구성하여 사용하는 세 가지 가상 주소 공간의 크기를 보여준다.

 

4K 페이지 + VA_BITS=39 예)

4K 페이지와 VA_BITS = 39로 구성된 3 레벨 페이지 테이블을 사용하는 경우에 대해 알아보자. 64비트 가상 주소에서 상위 25비트는 커널 또는 유저 주소 공간의 구분에 사용하고, 그다음 9비트 세트 3개는 각 레벨의 페이지 테이블 엔트리에 접근할 때 사용한다. 마지막 12비트는 페이지 내의 오프셋으로 사용한다.

 

각 테이블에 대해 9비트 인덱스를 사용하여 총 512개씩의 엔트리에 접근할 수 있음을 알 수 있다. 커널은 2개의 공간, 즉 커널과 유저 주소 공간을 나누어 사용하므로 64비트 가상 주소 중 변환에 사용하지 않는 최상위 비트 [63:39]를 체크하여 비트 값이 0인 경우에는 유저 페이지 테이블을 가리키는 TTBR0 레지스터를 사용한다. 그리고 그 비트들 값이 모두 1인 경우에는 커널 페이지 테이블을 가리키는 TTBR1 레지스터를 사용한다.

 

그러나 ARM64 커널에서는 그 비트들을 모두 비교할 필요 없이 최상위 비트 하나만을 비교하여 커널 및 유저의 가상 주소 영역을 구분한다.

 

다음 그림은 4K 페이지와 VA_BITS=39를 사용하는 페이지 테이블의 연결 구성 예를 보여준다.

 

4K 페이지 + VA_BITS=48 구성 예)

커널 v4.7-rc1부터 가상 주소 크기가 VA_BITS=39에서 VA_BITS=48로 변경되었다.

 

다음 그림은 4K 페이지와 VA_BITS=48을 사용하는 페이지 테이블의 연결 구성 예를 보여준다.

 


 

페이지 테이블 관련 주요 레지스터

TCR_EL1

다음 그림은 페이지 테이블 변환 관련된 TCR_EL1을 보여준다.

  • TxSZ (TTBRx Size offset)
    • TTBRx가 관리할 영역 크기는 2^(64-T0SZ)이다.
    • 예) VA_BITS=48의 경우 TxSZ=16이 필요하다.
  • IRGNx (Inner cacheability Normal Memory attribute for TTBRx)
    • Normal Memory Inner 캐시 속성
      • 0b00: NC (Non Cache)
      • 0b01: WB-WA(Write Back, Write Allocation)
      • 0b10: WT (Write Through)
      • 0b11: WB (Write Back)
  • ORGNx (Outer cacheability Normal Memory attribute for TTBRx)
    • Normal Memory Outer 캐시 속성 (상동)
  • SHx (Shareability attributer for TTBRx)
    • 0b00: Non-shareable
    • 0b10: Outer Shareable
    • 0b11: Inner Shareable
  • TGx (Granule size for TTBRx)
    • 0b00: 4KB
    • 0b01: 64KB
    • 0b10: 16KB
  • A1 (select TTBR0 & TTBR1 for ASID)
    • 0: TTBR0를 위해 ASID 사용
    • 1: TTBR1을 위해 ASID 사용
  • IPS (Intermediate Physical Address Size)
    • 중간 물리 주소(IPA) 사이즈
      • 0b000: 32 bits (4GB)
      • 0b001: 36 bits (64GB)
      • 0b010: 40 bits (1TB)
      • 0b011: 42 bits (4TB)
      • 0b100: 44 bits (16TB)
      • 0b101: 48 bits (256TB)
      • 0b110: 52 bits (4PB)
  • AS (ASID Size)
    • 0: TTBR0 및 TTBR1 양쪽에서 8 bit ASID 사용
    • 1: TTBR0 및 TTBR1 양쪽에서 16 bit ASID 사용
  • TBIx (Top Byte ignored for TTBRx)
    • 이 값을 1로 하는 경우 가상 주소의 최상위 8 비트를 주소 변환에 사용하지 않게 한다.

 

TTBRx_EL1

다음 그림은 두 개의 페이지 테이블의 base 주소를 가리키는 TTBR0_EL1 과 TTBR1_EL1을 보여준다.

  • ASID
    • mm->context.id의 하위 8 bit 또는 16 bit ASID를 저장한다.
    • TLB 캐시의 변환 엔트리 hit에 ASID + 가상 주소(VA) 를 사용하게하여 context 스위치 시 TLB 플러시를 최소화한다.
  • BADDR
    • 페이지 테이블의 물리 주소가 담긴다.
    • ARMv8.2 아키텍처 이상의 경우 52 비트 가상 주소 및 물리 주소를 지원한다. 이 때 52비트 물리 주소를 지원해야 하는 경우 52 비트 물리 주소의 msb를 사용한다.
  • CnP
    • Inner shable 영역의 코어에 공유할지 여부를 지정한다.

 


 

페이지 테이블 엔트리 포맷

ARMv8 엔트리 포맷

다음은 ARMv8 아키텍처 페이지 테이블의 4가지 유형의  엔트리 디스크립터 포맷이다.

  • Invalid
    • 모든 레벨에서 사용되며 bit[1:0] 값이 00 이다.
  • Block 디스크립터
    • 레벨 0~2에서 사용되며 bit[1:0] 값이 01 이다.
  • Table 디스크립터
    • 레벨 0~2에서 사용되며 bit[1:0] 값이 11 이다.
  • Page 디스크립터
    • 마지막 레벨 3에서 사용되며 bit[1:0] 값이 11 이다.

 

다음 그림은 각 레벨에서 사용하는 디스크립터들의 포맷을 보여준다.

  • ARMv8.2-LPA 옵션을 사용하는 시스템의 경우 52비트까지 확장된다.

Level 0, 1, 2의 블럭 및 테이블 관련 비트를 알아본다.

  • nT
    • Level 1 및 Level 2에서 이 비트가 설정된 엔트리가 MMU에 의해 사용될 떄 변환 fault를 유발한다. 또한 TLB 캐시에 캐시되지 않는다.
    • ARMv8.4-TTRem 기능이 구현된 시스템에서 사용한다.
  •  NSTable
    • 다음 레벨의 테이블이 Non-Secure State 인지 여부를 나타낸다.
  • APTable
    • 2 비트의 접근 권한을 지정한다.
      • 0b00: 다음 레벨의 테이블에 어떠한 영향도 없다.
      • 0b01: EL0에서 다음 레벨의 테이블에 접근을 금지한다.
      • 0b10: 다음 레벨의 테이블에서 수정을 금지한다.
      • 0b11: 위의 두 기능(0b01 & 0b10)이 동시에 동작한다.
  • UXN or XN
    • 이 비트를 1로 하면 유저 레벨이 다음 레벨의 테이블에서 실행 코드에 대한 주소 변환을 하지 못하게 한다.
      • 하이라키로 실행 금지를 제어한다.
  • PXNTable
    • 이 비트를 1로 하면 커널 레벨이 다음 레벨의 테이블에서 실행 코드에 대한 주소 변환을 하지 못하게 한다.
      • 하이라키로 실행 금지를 제어한다.

 

모든 레벨의 블럭 및 페이지 디스크립터에서 사용되는 Upper 속성 비트를 알아본다.

  • PBHA
    • Page 기반의 하드웨어 속성 비트로 지정한 디스크립터 비트의 사용 유무를 제어한다.
    • ARMv8.2에서만 사용된다.
  • SW
    • 하드웨어 아키텍처는 사용하지 않는 4 개의 비트이지만 소프트웨어에서 활용할 수 있는 비트들이다.
  • UXN 또는 XN
    • 이 비트를 1로 하면 유저 레벨이 다음 레벨의 테이블 또는 페이지에서 실행 코드에 대한 주소 변환을 하지 못하게 한다.
      • 하이라키로 실행 금지를 제어한다.
  • PXN
    • 이 비트를 1로 하면 커널 레벨이 다음 레벨의 테이블 또는 페이지에서 실행 코드에 대한 주소 변환을 하지 못하게 한다.
      • 하이라키로 실행 금지를 제어한다.
  • Contiguous
    • 이 비트를 1로 하면 하나의 엔트리로 이어지는 블럭또는 페이지가 동일한 매핑을 사용하게 된다.
  • DBM
    • ARMv8.1 아키텍처가 지원하는 기능으로 이 비트를 1로 하면  하드웨어가 Access 플래그와 Dirty 비트를 직접 관리하도록 한다.
    • 기존 ARM 아키텍처는 이 기능을 지원하지 않아 fault exception 후에 소프트웨어가 Access 플래그와 Dirty 비트를 기록하였다.

 

모든 레벨의 블럭 및 페이지 디스크립터에서 사용되는 Lower 속성 비트를 알아본다.

  • AttrIndx
    • MAIR_ELx를 위해 사용되는 메모리 속성이다.
  • NS
    • Non-Secure 비트이다.
  • AP
    • 데이터 접근 권한이다.
      • 0b00: 상위 Exception 레벨에서 RW, 유저 레벨에서 None 이다.
      • 0b01: 상위 Exception 레벨에서 RW, 유저 레벨에서 RW 이다.
      • 0b00: 상위 Exception 레벨에서 R, 유저 레벨에서 None 이다.
      • 0b00: 상위 Exception 레벨에서 R, 유저 레벨에서 R 이다.
  • SH
    • 공유 비트
      • 0b00: Non-Shareable
      • 0b01: REserved
      • 0b10: Outer Shareable
      • 0b11: Inner Shareable
  • AF
    • 페이지나 블럭에 접근한 경우 설정되는 Access 플래그이다.
    • ARMv8.1의 DBM 설정에 따라 하드웨어가 갱신할지 아니면 소프트웨어가 갱신할 지 선택할 수 있다.
  • nG
    • not Global 비트로 이 비트가 설정되는 경우 지정된 ASID의 TLB 엔트리에서만 변환을 시도한다.
    • 0으로 설정하는 경우 ASID와 상관 없이 이 엔트리를 사용하여 변환을 한다.

 

PTE 속성

커널 레벨(stage 1)에서 사용하는 리눅스 PTE  속성이다. 하이퍼 바이저 레벨(stage 2)는 생략한다.

arch/arm64/include/asm/pgtable-hwdef.h

#define PTE_VALID               (_AT(pteval_t, 1) << 0)
#define PTE_TYPE_MASK           (_AT(pteval_t, 3) << 0)
#define PTE_TYPE_PAGE           (_AT(pteval_t, 3) << 0)
#define PTE_TABLE_BIT           (_AT(pteval_t, 1) << 1)
#define PTE_USER                (_AT(pteval_t, 1) << 6)         /* AP[1] */
#define PTE_RDONLY              (_AT(pteval_t, 1) << 7)         /* AP[2] */
#define PTE_SHARED              (_AT(pteval_t, 3) << 8)         /* SH[1:0], inner shareable */
#define PTE_AF                  (_AT(pteval_t, 1) << 10)        /* Access Flag */
#define PTE_NG                  (_AT(pteval_t, 1) << 11)        /* nG */
#define PTE_GP                  (_AT(pteval_t, 1) << 50)        /* BTI guarded */
#define PTE_DBM                 (_AT(pteval_t, 1) << 51)        /* Dirty Bit Management */
#define PTE_CONT                (_AT(pteval_t, 1) << 52)        /* Contiguous range */
#define PTE_PXN                 (_AT(pteval_t, 1) << 53)        /* Privileged XN */
#define PTE_UXN                 (_AT(pteval_t, 1) << 54)        /* User XN */

 

참고

22 thoughts to “ARM64 페이지 테이블 -1- (Basic)”

  1. 안녕하세요.

    페이지 테이블을 공부하는데 많은 도움이 되었습니다.

    페이지 크기와 VA_BITS 조합에 의한 각 페이지 테이블 구성에서 의문이 생겨 질문드립니다.pgd에 pud 사이즈의 블럭을 직접 연결하듯 pud, pmd가 자신보다 작은 사이즈의 블럭을 연결하는 경우가 있던데, 이 블럭들은 어디에 사용되나요?

    항상 좋은 글 감사드립니다.

    1. 안녕하세요?

      매핑을 할 리니어 메모리 사이즈가 작은 경우에는 페이지 단위로 매핑을 하고,
      다음 조건에 부합하면 블럭 매핑을 시도합니다.
      – pud 사이즈 블럭 매핑: 가상 주소와 물리 주소가 1G 단위 정렬되어 있고, 4K 페이지
      – pmd 사이즈 블럭 매핑: 가상 주소와 물리 주소가 pmd 사이즈 단위로 정렬되어 있는 경우

      실제 커널 부팅 업 시 4G DRAM 메모리를 리니어 매핑할 때 처음에 대부분을 블럭 매핑합니다.

      감사합니다.

  2. 안녕하세요.
    문씨블로그 잘보고있습니다.
    문서 초반부에 엔트리레벨을 두는 이유를 설명하는 과정에서 “각 태스크에 주어지는 가상 주소 공간마다 64G개의 엔트리를 제공하기에는 너무 많은 메모리가 낭비되므로 ARM64 커널은 테이블을 최소 2 레벨부터 최대 4 레벨로 나누어 사용한다” 라는 문구가 있는데. 이해가 잘 안되서 질문드립니다.

    우선 VA_BITS 48기준으로 1레벨 페이지테이블에 4K 페이지를 사용한다고 가정하면 2^36-1개의 엔트리가 생겨 64G개의 엔트리가 계산된다는 것은 이해했습니다.

    그런데 여기서 페이지 레벨을 3단계, 4단계를 구성할때 PTE는 줄어들지몰라도 PUD또는 PMD가 존재하기때문에 결국은
    2^36-1개의 테이블이 생긴다고 생각됩니다. 예를들어 VA_BITS 48, 4K page, 4레벨 구성이라고 한다면,
    pgd 9bits, pud 9bits, pmd 9bits, pte 9bits 따라서 512개 * 512개 * 512개 * 512개 로 64GiB 개의 테이블이 생깁니다.
    물론 PTE는 512개이지만요.

    질문을 정리하자면,
    1. 결국 64G의 테이블이 생기는것은 마찬가지인데 어떻게 메모리가 낭비되지 않을 수 있는지?
    2. 실제로 메모리를 사용하는것은 결국 PTE라서 그런것인지?
    이렇게 정리하고싶습니다.

    실제로 페이지테이블을 어떻게 시스템이 사용하는지 이해하지 못해서 드리는 질문같습니다. 그래도 조금 힌트를 주시면 좀더 수월하게 이해할 수 있어서 질문드립니다. 감사합니다.

  3. 안녕하세요?

    1 단계만 사용하면 테이블을 한꺼번에 만들기 때문에 메모리가 많이 낭비되지만,
    2 단계 이상으로 구성하는 경우 2 단계 부터는 필요한 테이블만 추가로 생성하여 만듭니다.

    하나의 유저 프로세스가 생성되면 해당 유저 페이지 테이블을 생성하는데 1단계 테이블만 생성하고,
    나머지는 cpu가 해당 주소를 읽어낼 때마다 fault가 발생하면 메모리를 로딩하여 2단계 이상 테이블을 사용하면서
    점점 테이블이 커지는 방식입니다. 즉 사용하지 않는 공간에 대해서는 2단계 이상의 테이블이 만들어지지 않습니다.

    감사합니다.

  4. 안녕하세요 .
    항상 블로그 잘 이용하고 있습니다.

    페이지 테이블 엔트리 포맷 항목의 그림 부분에,
    Level 0,1,2 descriptor -> block -> 4K page level 2에 수치가 2MB 되어야 할 것 같은데,
    일치하지 않아 이렇게 댓글을 남기게 되었습니다.

    혹시나 제가 잘못 생각하고 있다면 알려주시면 감사하겠습니다.

  5. 안녕하세요
    블로그 통해서 많이 배우고 있습니다.

    혹시 여러개의 NUMA 노드를 가진 시스템에서는 page table을 한 NUMA 노드 메모리에 두는지 궁금합니다.
    아니면 처음 부팅과정에서 page table의 위치를 어느 NUMA 노드에 두도록 지정해줄 수 있는지 궁금합니다.
    항상 좋은 글 감사드립니다.

    1. 안녕하세요?

      NUMA 시스템에서는 다음과 같이 동작합니다.
      1) 부팅 시 사용되는 커널용 페이지 테이블은 첫 번째 NUMA 노드에 생성됩니다.
      -> 컴파일 타임에 생성한 swapper_pg_dir (ARM64 기준 PAGE_SIZE)을 사용합니다.

      struct mm_struct init_mm = {
      .mm_rb = RB_ROOT,
      .pgd = swapper_pg_dir,
      ...

      2) 이후에 만들어지는 유저 페이지 테이블은 버디 시스템으로 동작하는 페이지 할당자를 통해 할당됩니다. NUMA 시스템과 연동된 페이지 할당자는 NUMA policy(numactl 명령으로 제어 가능)를 통해 어떠한 노드에서 메모리를 할당할지 결정하는데, 유저가 이를 제어하지 않는 경우 디폴트로 fork 시도 중인 현재 cpu가 소속된 NUMA 노드에 할당합니다. 단 cpu가 있는 노드가 메모리리스 노드인 경우에는 인접한 메모리 노드에서 할당합니다. (따라서 고성능을 유지하려면 NUMA 시스템의 경우 성능이 필요한 application에 대해서는 NUMA 노드를 지정하여 운영합니다.)

      다음은 함수 호출 관계입니다.
      참고: _do_fork() -> copy_process() -> copy_mm() -> dup_mm() -> mm_init() -> mm_alloc_pgd() -> pgd_alloc() -> __get_free_page() -> alloc_pages() -> alloc_pages_current()

      즐거운 하루되세요. 감사합니다.

      1. 답변 감사합니다.
        현재 page table, page work, TLB-miss에 대해 공부중인데,
        혹시 커널용 페이지 테이블을 첫 번째 NUMA 노드가 아닌 다른 NUMA 노드의 메모리에 생성하는게 가능할까요?

        친절한 답변 감사드립니다.

        1. 네. 가능합니다.

          다른 NUMA 노드에 속한 cpu에서 application을 동작시키도록 taskset 유틸리티 명령을 사용하시면 됩니다.
          (현재 로긴한 bash shell을 다른 노드에 속한 cpu로 옮긴 후 bash를 사용하면 모든 하위 명령들은 다른 노드에서 할당됩니다)
          예) taskset -c 4-7
          예) taskset -c 4-7 –p

          또 다른 자세한 제어 방법은 NUMA policy를 변경할 수 있는 numactl 유틸리티를 사용하셔도 좋습니다.

          감사합니다.

  6. 안녕하세요, 항상 좋은글들 감사합니다.
    페이지테이블 구성에 대해서 질문이 있습니다. 이 페이지에서 설명하신 내용에 ARM64의 2레벨, 3레벨, 4레벨 페이지 테이블 구성의 예시(Ex. VA_BITS_39, 4K_PAGES 조합, VA_BITS_36, 16K_PAGES 조합 등)가 있는데, 설명하신 조합들 외의 경우는 불가능한가요? 커널 config의 의존관계를 보면 VA_BITS_39는 4K_PAGES에 depend on 관계인데, 그렇다면 예를들어 VA_BITS_39와 16K_PAGES 조합은 불가능한가요? 만약 불가능하다면, 이렇게 구현되어 있는 이유가 있나요? 왜 모든 VA_BITS와 4,16,64K_PAGES 의 조합을 가능하게 해놓지 않았을까요?

  7. 좋은 자료 정리해주셔서
    항상 감사드립니다!

    Contiguous Bits
    연속되는 공간 사이즈

    # CONT_PMD_SIZE -> PTE
    4K 페이지: 16 * 4K = 64KB
    16K 페이지: 128 * 16K = 2MB
    64K 페이지: 32 * 64K = 2MB

    # CONT_PTE_SIZE -> PMD
    4K 페이지: 16 * 2MB = 32MB
    16K 페이지: 32 * 32MB = 1GB
    64K 페이지: 32 * 512MB = 16G

    PMD 뭉치가 더 커야할것 같은데 오타인가요?

    granule size | cont PTE | cont PMD |
    4 KB | 64 KB | 32 MB |
    16 KB | 2 MB | 1 GB* |
    64 KB | 2 MB | 16 GB* |

    P.S
    메모리 Compaction 내용보다가 너무 어려워서 복습 중입니다.

    1. 안녕하세요? 이파란님!

      정확히 보셨습니다. 본문 오타 확인하여 수정하였습니다.

      할당 보다 회수가 더 어렵습니다. 물리 메모리의 회수 쪽엔 compaction도 있고, lru reclaim도 있고 swap/pageout도 있고 복잡합니다. ^^
      나중에 vma 및 rmap 등의 가상 메모리 관리를 준비하시려면 물리 메모리에 대한 이해도가 높아야 하므로 단단히 준비하시기 바랍니다.

      감사합니다.

  8. 안녕하세요 좋은 자료로 정말 많은 도움을 받고 있습니다. 정말 감사합니다.

    궁금해지는 것이 하나 생기는데요,
    커널 주소 영역도 가상 주소이면,
    커널이 사용하는 모든 주소 공간이 페이지에 의해서 관리가 되는 것이고
    그렇다면 커널의 페이지 테이블도 페이징 되어 존재할텐데,
    ttbr 레지스터가 페이지 테이블의 물리 주소를 가리키고 있어야 하는데,
    커널은 페이지 테이블의 가상 주소만 알고 있는 상태가 아닌가 해서 자꾸 헷갈리는 것 같습니다.

    감사합니다..ㅎㅎ

    1. 안녕하세요? JANUS님!

      커널의 페이지 테이블도 페이징되어 존재하므로 해당 영역을 수정할 수 있습니다.
      그것과 별개로 ttbr 레지스터는 페이지 테이블이 위치한 가상 주소가 아닌 물리 주소를 지정하여 사용합니다.

      감사합니다.

DEWH에게 댓글 남기기 댓글 취소