MMU에서 가상 주소를 물리 주소로 변환하는 구조를 알아봤으니 변환 테이블의 내부 구조와 가상 주소가 물리 주소로 변환되는 실행 흐름을 알아봅시다.


페이지 테이블 엔트리


변환 테이블을 통해 가상 주소를 물리 주소로 변환하려면 먼저 가상 주소의 형식을 알아야 합니다. 가상 주소 영역을 일정한 크기의 블록으로 나눌 수 있는데, 여기서 한 개의 테이블 엔트리는 한 개의 블록에 해당됩니다.

 


그림 19.7 가상 주소가 변환 테이블을 통해 물리 주소로 변환되는 흐름

각각의 엔트리는 물리 메모리에서 해당되는 블록 주소와 물리 주소에 접근할 때 사용할 속성으로 구성돼 있습니다.

변환 테이블은 일종의 엔트리 블록으로 구성돼 있습니다. 변환 테이블에 있는 엔트리 0은 블록 0, 엔트리 1는 블록 1에 대한 매핑 정보를 제공합니다. 또한 각각 엔트리는 물리 주소의 블록에 해당되는 주소와 속성 정보를 담고 있습니다.


페이지 테이블 룩업

테이블 룩업은 변환 작업이 진행될 때 실행됩니다. 운영체제의 프로세스가 주소에 접근하면 주소 변환이 실행됩니다. 이때 가상 주소는 다음 다이어그램과 같이 두 가지 부분으로 나뉩니다.

 


그림 19.8 가상 주소의 구조와 페이지 룩업이 진행되는 흐름

위 다이어그램은 싱글 페이지 룩업 동작을 나타냅니다. ①에서 '엔트리'로 명시된 상위 비트는 찾을 블록 엔트리를 알려주는데, 테이블에 접근하는 인덱스로 활용됩니다. 변환 테이블을 구성하는 각각 엔트리 블록은 가상 주소에 대한 물리 주소의 베이스 주소를 담고 있습니다. 

그림을 통해 전체 구조를 확인했으니 주소가 변환되는 과정을 알아봅시다. 위 그림에서 ①로 표시된 부분은 CPU가 가상 주소를 실행하는 동작을 나타냅니다. CPU에서 실행되는 프로세스 입장에서 바라보는 주소는 가상 주소입니다. 이번에는 화살표와 함께 ②로 표시된 부분은 가상 주소의 베이스 주소를 바탕으로 주소 변환 정보를 담고 있는 페이지 테이블에 접근하는 동작입니다. 페이지 테이블의 시작 주소를 나타내는 TTBR1_EL1 레지스터를 통해 페이지 테이블의 베이스 주소를 참고해 변환 정보가 담긴 페이지 테이블 엔트리의 주소를 찾습니다. 그림의 ③은 페이지 테이블 엔트리 레코드를 읽어서 물리 주소로 변환하는 동작을 나타냅니다. 물리 주소에서 보이는 베이스는 페이지 테이블 엔트리에 있는 주소 정보입니다. ①에서 보이는 '오프셋'으로 명시된 하위 비트는 블록 내부의 오프셋을 나타내며, 변환 과정에서 변경되지 않습니다.


멀티 레벨 페이지 변환


이제까지 한 개의 주소 변환 테이블을 통해 주소가 변환되는 과정을 알아봤습니다. 한 개의 변환 테이블을 통해 한 번에 주소가 변환되므로 이를 싱글 룩업이라고 합니다. 그런데 Aarch64에서는 다수의 변환 페이지 테이블을 사용해 주소 변환 작업이 수행될 수 있는데, 이를 멀티 레벨 페이지 룩업을 통해 가상 주소가 물리 주소로 변환된다고 할 수 있습니다. 

이어서 다음 그림을 보면서 멀티 레벨 페이지 룩업의 실행 흐름을 알아봅시다.
 
출처: Learn the architecture - AArch64 memory management https://developer.arm.com/documentation/101811/latest/

그림 19.9 가상 주소가 멀티 레벨 변환 페이지를 통해 변환되는 과정

ARMv8-A 아키텍처에서 지원하는 최대 페이지 변환 레벨은 4개이며, 실제 레벨의 범위는 0 ~ 3 사이입니다. 멀티 레벨로 구성된 페이지 테이블을 구성하면 큰 사이즈의 블록과 작은 사이즈의 블록을 모두 활용할 수 있습니다. 멀티 레벨 변환 테이블을 구성하는 블록의 특징은 다음과 같습니다.

 큰 블록은 작은 블록보다 페이지 룩업에 걸리는 연산이 더 적습니다.(추가로 큰 블록들은 TLB에 더 효율적으로 캐싱됩니다.)
 작은 블록들은 TLB에서 캐싱할 때 비효율적입니다. 캐싱이 비효율적인 이유는 작은 블록들이 주소를 변환하는 과정에서 메모리에 더 자주 액세스해 데이터 읽기를 시도하기 때문입니다.


[정보] 큰 크기 블록과 작은 크기 블록을 사용할 때의 트레이드오프
큰 사이즈의 블록과 작은 사이즈의 블록으로 구성된 멀티 변환 테이블을 사용해 가상 주소를 변환할 때 트레이드오프가 있습니다. 이는 멀티 레벨 캐시와 비슷한 개념으로 볼 수 있습니다. L1 캐시의 가장 중요한 성능 지표는 캐시 라인을 읽어 오는 속도이고 L2 캐시의 가장 중요한 성능 지표는 캐시 히트 비율입니다. 따라서 큰 사이즈 블록과 작은 사이즈의 블록을 적절히 활용해 멀티 레벨 테이블을 구성할 필요가 있습니다.

 

 



 

Documentation – Arm Developer

 

developer.arm.com

 

 

MMU의 주된 역할은 가상 주소를 물리 주소로 변환하는 일입니다. 그렇다면 MMU는 어떻게 구성돼 있고 어떤 과정으로 가상 주소를 물리 주소로 변환할까요? 다음 그림을 보면서 MMU의 실행 흐름에 대해 알아봅시다.

 
출처: Learn the architecture - AArch64 memory management https://developer.arm.com/documentation/101811/latest/

그림 19.6 MMU를 통해 주소가 변환되는 흐름

프로세스가 가상 주소에 접근하면 먼저 MMU에게 주소가 전달됩니다. MMU는 먼저 TLB(Translation Lookaside Buffer)에 해당 주소에 대한 변환 정보가 있는지 확인합니다. 여기서 TLB는 최근에 사용된 주소 변환 정보를 담고 있는 일종의 캐시입니다. 

만약 가상 주소에 대한 변환 정보가 TLB에 있으면 바로 물리 주소로 변환합니다. 만약 MMU가 TLB 내에 최근에 변환된 테이블 정보를 읽지 못하면 ‘Table Walk Unit’을 통해 메모리로부터 적절한 테이블 엔트리를 읽습니다.

[중요] 캐시에 접근하기 전에 물리 주소로 변환하는 이유

Arm 코어 옆에 캐시가 있는데, 캐시에 접근하려면 먼저 MMU를 통해 가상 주소를 물리 주소로 변환하는 과정이 완료돼야 합니다. 메인 메모리 주소에 접근하기 전에 캐시에 접근해 메모리 주소가 있는 지 체크하기 때문입니다. 이 같은 과정을 캐시 룩업이라고 합니다.


이번에는 다음 그림을 통해 가상 주소와 물리 주소 공간이 어떻게 구성됐는지 알아봅시다. 그리고 가상 주소를 물리 주소로 변환하는 변환 테이블에 대해서도 배워 봅시다.

 

 

 

그림 19.5 소프트웨어 관점에서 가상 주소 공간과 물리 주소 공간

 

그림의 왼쪽 부분부터 봅시다. 왼쪽 부분은 소프트웨어 관점에서 가상 주소 공간을 나타냅니다. 가상 주소에는 각각 영역별로 특정 속성을 지닌 코드와 데이터가 존재합니다. 소프트웨어를 실행하는 주인공인 프로세스는 가상 주소 공간에서 실행됩니다.

 

프로세스가 가상 주소 공간에서 어떤 명령어를 실행하면 메모리에 위치한 변환 테이블에 접근합니다. 이는 그림의 가운데 부분에 해당됩니다. 변환 테이블에 의해 가상 주소는 물리 주소 공간에 접근합니다.

 

여기서 가운데 존재하는 '변환 테이블' 동적으로 업데이트되며, 운영체제에서 실행되는 프로세스마다 각자의 '변환 테이블' 갖게 됩니다. 프로세스마다 가상 주소를 물리 주소로 변환할 있는 '변환 테이블' 있다는 것은 각각 프로세스마다 독립적인 가상 주소 공간에서 실행될 있다고 있습니다.

 

이렇게 해서 전체 그림을 보면서 운영체제와 메모리 구조 관점에서 메모리 매니지먼트가 무엇인지 알아봤습니다. 가상 주소와 물리 주소 공간이 존재할 있게 Arm 아키텍처에서는 MMU 제공하며, 이를 활용해 가상 메모리를 관리합니다. 이어지는 절에서 MMU 대해 자세히 알아봅시다.

리눅스를 비롯한 대부분의 운영체제에서는 가상 메모리를 메모리 관리 기법으로 활용합니다. 이번에는 가상 메모리와 물리 주소에 대해 알아보고 가상 메모리 기법이 적용된 이유를 알아봅시다.

대부분의 운영체제는 다양한 메모리(DRAM) 상에서 실행될 수 있는데, 다음 그림은 가상 메모리 기법을 적용하기 전의 시스템 구조도입니다.
 

그림 19.2 물리 메모리와 메모리 시스템의 관계

물리 메모리가 '물리 메모리 A' ~ '물리 메모리 D'까지 있습니다. 만약 소프트웨어 개발자가 물리 메모리 타입에 따라 주소 오프셋을 변경하는 설정을 하거나 추가로 물리 메모리와 관련된 설정을 하면 소프트웨어의 복잡도가 많이 늘어날 수 있습니다. 물리 메모리에 대한 예외 상황을 점검해야 하니 골치가 아플 것입니다.

하드웨어 측면에서는 다양한 메모리 공급사가 있으며, 대표적인 업체로 삼성전자, 하이닉스, 도시바를 예로 들 수 있습니다. 또한 SoC 업체(브로드컴, 인텔, 퀄컴)별로 서로 다른 물리 메모리 맵을 구성하며, 리눅스 커널와 같은 운영체제 커널이 실행하는 물리 메모리의 주소가 다릅니다. 다음 그림에서 Soc A는 0x8000_0000 물리 주소에 '리눅스 커널' 이미지를 실행하고, SoC B는 0x4000_0000 물리 주소에서 '리눅스 커널' 이미지를 실행합니다.   

 

그림 19.3 SoC 벤더별 메모리 맵

그런데 만약 프로세스 입장에서 물리 메모리별로 서로 다른 주소에 접근한다면 시스템 복잡도가 높아질 것입니다. 물리 메모리별로 무엇인가 따로 설정을 해야 하기 때문입니다. 그런데 가상 메모리 기법을 적용하면 시스템의 전체 구조를 다음과 같이 그릴 수 있습니다. 

 

그림 19.4 가상 메모리 기법을 적용한 메모리 시스템의 전체 구조

시스템 소프트웨어 개발자 입장에서는 가상 메모리 범위 내의 가상 주소 처리에만 신경 쓰면 됩니다. 시스템에 어떤 물리 메모리를 탑재했는지 걱정할 필요가 없습니다.

MMU(Memory Management Unit)란 무엇일까요? 가상 주소를 물리 주소로 변환하는 일을 하는 주인공이 MMU입니다. MMU는 하드웨어 블록으로 Arm 프로세서 내부에 존재합니다. MMU를 눈으로 직접 확인하고 싶지만 Arm 코어와 함께 MMU가 내부에 실장돼 있어 확인할 수는 없습니다.

최근에 개발되는 대부분 시스템에서 프로세스는 MMU를 활성화한 상태에서 실행됩니다. 그래서 MMU를 활성화하면 운영체제에서 실행되는 프로세스가 바라보는 주소는 가상 주소입니다. 여기서 ‘가상’이라는 용어는 말 그대로 세상에 존재하지 않는 논리적인 개념으로 볼 수 있습니다. 그런데 가상 주소가 존재하려면 MMU가 있어야 하는데, MMU의 역할은 가상 주소를 물리 주소로 변환하는 것입니다.

그렇다면 MMU는 가상 주소를 물리 주소로 어떻게 바꿀까요? MMU 내에는 TLB(Translation Lookaside Buffer)가 있는데, 이 버퍼는 페이지 테이블 레코드 정보를 담고 있습니다. TLB 에는 최근에 변환된 가상 주소에 대한 페이지 테이블 정보가 들어있고, 이 정보를 참고해 가상 주소를 물리 주소로 변환합니다.

Arm에서 제공하는 메모리 아키텍처에서 MMU는 가장 중요한 기능이므로 잘 익혀둡시다. 좀 더 자세한 내용은 19.2절 'MMU의 세부 동작'에서 설명합니다.


[정보] MMU와 운영체제
대부분 CPU 코어 위에서 운영체제가 실행되는데 CPU의 특징을 잘 알아야 운영체제의 세부 동작 원리도 잘 파악할 수 있습니다. 이를 메모리 관점에서 "운영체제에서 메모리를 관리하는 주요 기능은 CPU에서 지원하는 MMU를 활용한다"라고 설명할 수 있습니다.


 

 

Arm 아키텍처에서 메모리 매니지먼트란 무엇일까요? Arm 아키텍처에서 메모리와 관련된 내용을 다룰 때 '메모리 매니지먼트'와 MMU를 함께 설명하는 경우가 많아 메모리 매니지먼트에 대해 한 문장으로 정의하기 어렵습니다. Arm 아키텍처에서 메모리 매니지먼트는 다음과 같은 기능을 활용해 운영체제 커널에서 메모리 관련 시스템을 구축할 수 있는 환경을 제공하는 것입니다.

 MMU: 가상 주소를 물리 주소로 변환
 메모리 컨트롤 레지스터: MMU와 메모리 시스템 관련 속성 설정 

그렇다면 MMU는 무엇일까요? 가상 주소를 물리 주소로 변환하는 일을 하는 하드웨어 블록을 MMU라고 합니다.

메모리 매니지먼트의 전체 구조

다음 그림을 보면서 메모리 매니지먼트를 구성하는 주요 기능에 대해 알아봅시다.

 

그림 19.1 가상 주소가 물리 메모리에 접근하는 과정

그림에서 먼저 ①로 표시된 부분을 봅시다. ①은 CPU 코어 입장에서 바라보는 주소를 나타내며, 이는 가상 주소입니다. ②로 표시된 박스는 MMU이며, CPU 코어에서 접근하는 가상 주소를 물리 주소로 변환하는 역할을 맡습니다. MMU가 가상 주소를 물리 주소로 변환할 때 주소 변환 테이블 정보가 있는 페이지 테이블을 참고합니다. ③에 표시된 박스는 주소 변환 정보를 지닌 페이지 테이블을 나타냅니다. 

MMU는 CPU 코어가 접근하는 가상 주소인 ①에 해당되는 주소 변환 정보가 ③으로 표시된 페이지 테이블에 있는지 체크합니다. 이 정보가 페이지 테이블에 있으면 ④와 같이 물리 주소가 존재하는 메인 메모리에 접근합니다. 메인 메모리에 존재하는 데이터나 명령어를 로딩하는데, 이는 그림에서 ⑤에 해당됩니다.

만약 CPU 코어가 접근하는 가상 주소의 변환 정보가 페이지 테이블에 없으면 폴트(fault)를 유발하는데, 이는 ⑥에서 표시된 부분에 해당합니다. 익셉션 관점에서 Synchronous 익셉션이 유발됩니다.

소프트웨어 관점에서는 어셈블리 명령어로 구성된 루틴에서 메모리에 접근해 데이터를 읽는 코드를 보면 명령어가 한 줄씩 실행되는 것처럼 보입니다. 하지만 그림 19.1에서 표시된 전체 구조 내에서 주소가 처리됩니다.

[정보] 그림 19.1의 전체 구조에 대해

그림 19.1의 전체 구조는 메모리 시스템이 큰 그림에서 어떤 방식으로 동작하는지를 간단한 구조로 표현한 것입니다. 실제 메모리 시스템은 이보다 더 복잡한 구조로 구성돼 있습니다.


실전 개발에서 메모리 매니지먼트 

'메모리 매니지먼트'와 관련된 기능은 실전 개발에 어떻게 활용될까요? 다음과 같은 업무를 진행할 때 알아야 하는 기반 지식이 메모리 매니지먼트입니다.

 메모리 컨트롤 레지스터 설정
 MMU 관련 레지스터 설정
 캐시나 메모리를 설정하는 명령어 실행
 가상 주소 맵 변경

특히 브링업할 때 메모리 시스템을 구성하는 MMU와 캐시를 설정합니다. 이때 메모리 매니지먼트와 관련된 내용을 알아야 시스템을 제대로 브링업할 수 있습니다.

또한 크래시와 같은 신뢰성 이슈를 잘 해결하기 위한 기반 지식이 메모리 매니지먼트입니다. Arm 아키텍처 관점에서 크래시는 메모리 어보트로 볼 수 있는데, 메모리 어보트로 발생하는 원인의 절반 이상이 메모리 매니지먼트와 관련돼 있습니다. 몇 가지 예를 들면 다음과 같습니다.

 PC 얼라이먼트, SP 얼라이먼트
 메모리 권한 속성 오류로 인한 페이지 폴트
 가상 주소 변환 오류

메모리 매니지먼트를 이루는 지식은 메모리 어보트로 유발된 다양한 문제를 잘 해결할 수 있는 기반 지식입니다.

마지막으로 성능을 최적화하기 위한 기반 지식도 메모리 매니지먼트입니다. L1, L2 캐시 설정, 캐시 정책, 변환 테이블 처리 방식과 같이 메모리 시스템을 설정하는 방식이 성능에 많은 영향을 끼칠 수 있습니다.  

 

1. 0xc13342c0 가상 주소가 물리주소로 변환되는 과정 확인

crash> p schedule 
schedule = $1 =
 {int (void)} 0xc13342c0 <schedule>




물리주소는 0x813342c0임

crash> vtop 0xc13342c0
VIRTUAL   PHYSICAL
c13342c0  813342c0

PAGE DIRECTORY: c0004000
  PGD: c000704c => 8131940e
  PMD: c000704c => 8131940e
 PAGE: 81300000  (1MB)


  PAGE    PHYSICAL   MAPPING    INDEX CNT FLAGS
c4926680  81334000         0         0  1 400 reserved


0xc13342c0 가상 주소에서 L1 Page Table Index는 c13임(0xc13342c0)

crash> p schedule
schedule = $1 =
 {int (void)} 0xc13342c0 <schedule>



TTBA(Translation Table Base Address)는 0x80004000임


0x80004000-- 0x80006FB0까지 0으로 채워짐


0xc13342c0 가상 주소에 대한 PageTable Dump를 보면 아래와 같음



L1 Page Table은 0x8000704C = 0x80004000 + (0xC13 * 4 = 0x304C)

0x8131940E(*0x8000704C) = 10000001001100011001010000001110

 


10000001001100011001010000001110 값에서 마지막 2비트가 10이므로 Section Entry임

앞의 12비트가 Base Address임 10000001001100011001010000001110, 0x813 = 100000010011


0x813(Physical Base Address) 에서 20-Bit Left Shift + 가상 주소의 Offset 값(0xc13342c0) = 0x813342c0

 

공식: 0x813(PA 변환된 정보) + 342c0 오프셋 =  0x813342c0


Page Descriptor 주소는 c4926680

crash> vtop 0xc13342c0
VIRTUAL   PHYSICAL
c13342c0  813342c0

PAGE DIRECTORY: c0004000
  PGD: c000704c => 8131940e
  PMD: c000704c => 8131940e
 PAGE: 81300000  (1MB)


  PAGE    PHYSICAL   MAPPING    INDEX CNT FLAGS
c4926680  81334000         0         0  1 400 reserved


0xc13342c0 주소에 해당하는 페이지 프레임 번호는 0x1334임

(struct page *) &mem_map[0x0] = 0xC4900000
  (struct page *) (struct page*)0xC4926680
 
  0xC4926680 - 0xC4900000 = 0x26680 / 0x20 = 0x1334

 



2. 0xBFED1000 가상 주소가 물리주소로 변환되는 과정 확인

물리주소는 0xc893a000임

 

crash> vtop BFED1000
VIRTUAL   PHYSICAL
bfed1000  c893a000

PAGE DIRECTORY: c0004000
  PGD: c0006ff8 => a70e6811
  PMD: c0006ff8 => a70e6811
  PTE: a70e6344 => c893a65f

 PAGE: c893a000

  PTE     PHYSICAL  FLAGS
c893a65f  c893a000  (PRESENT|DIRTY|YOUNG|WRITE)

  PAGE    PHYSICAL   MAPPING    INDEX CNT FLAGS
c5212740  c893a000  e1367001        97  1 40080068 uptodate,lru,active,swapbacked

 

아래 과정으로 L1 Page Table Entry에 접근

L1 Page Table entry: 0xBFE(0xBFED1000)
 

Page Table Address 0x80006FF8 = 0x80004000 + 0x2FF8(0xBFE*4)
A70E6811=*0x80006FF8

 

// 2bit가 01이니까 다음 레벨의 페이지 테이블의 베이스 주소를 가리킴 

10100111000011100110100000010001(0xA70E6811) 


10100111000011100110100000010001 [31:10] 비트만 살리고 나머지는 0으로 밀어버리면
10100111000011100110100000000000 --> A70E6800 (Next, 2 level 페이지 테이블)

 

0xA70E6B44 = 0xA70E6800 + 0x344 [where: 4*0xD1(0xBFED1000) ]

 

<관련 Arm 스펙 문서>

 

이어서 2 레벨 페이지 변환을 진행하자.

0xA70E6B44 = 0xA70E6800 + 0x344 [where: 4*0xD1(0xBFED1000) ]

 

PTE인 C893A45F을 2진수로 표기하면 아래와 같다.

1100 1000 1001 0011 1010 0100 0101 1111

bit[1:0]이 11이니 스몰 페이지(Small page)이다.

 

<스몰 페이지에 대한 Arm 스펙 문서는 아래와 같다.>


결국 아래와 같이 계산하여 물리 주소를 얻음

C893A45F(2 level PTE 값)
BFED1000(가상 주소의 offset)
C893A000 (C893A45F)

다른 주소로 테스트

crash> vtop BFED1111
VIRTUAL   PHYSICAL
bfed1111  c893a111

crash> vtop BFED1ddd
VIRTUAL   PHYSICAL
bfed1ddd  c893addd


T32로 다시 확인



User space 가상 주소에 대한 Page Address

crash> vtop BFED1000
VIRTUAL   PHYSICAL
bfed1000  c893a000

PAGE DIRECTORY: c0004000
  PGD: c0006ff8 => a70e6811
  PMD: c0006ff8 => a70e6811
  PTE: a70e6344 => c893a65f

 PAGE: c893a000

  PTE     PHYSICAL  FLAGS
c893a65f  c893a000  (PRESENT|DIRTY|YOUNG|WRITE)

  PAGE    PHYSICAL   MAPPING    INDEX CNT FLAGS
c5212740  c893a000  e1367001        97  1 40080068 uptodate,lru,active,swapbacked


Offset 계산
 

 (struct page_address_map *) &page_address_maps[0x0] = 0xC15F71C0 = page_address_maps[0] -> (
________address|________0________4________8________C_0123456789ABCDEF
   NSD:C15F7EB0| C557BBC0 BFECF000 C15F8478 C15F7C98 ..W.....x._..|_.
   NSD:C15F7EC0| C570CB80 BFED0000 C15F84F8 C15F5940 ..p......._.@Y_.
   NSD:C15F7ED0| C5212740 BFED1000 C15F85D8 C15F6000 @'!......._..`_.
   NSD:C15F7EE0| C54A4BC0 BFED2000 C15F7F58 C15F5B00 .KJ.. ..X._..[_.
   0xD10 = 0xC15F7ED0 - 0xC15F71C0

 



page_address_maps 전역변수는 0xBFE00000 - 0xBFFFF000 구간 가상 주소에 대한 페이지 정보를 포함

 

+ Recent posts