지금까지 Arm 아키텍처에서 정의된 노멀 메모리 타입과 디바이스 메모리 타입을 알아봤습니다. 이어서 메모리 맵을 보면서 노멀 메모리 타입과 디바이스 메모리 타입의 특징을 자세히 알아봅시다.

[정보] 메모리 맵이란?

실전 프로젝트를 진행하면 메모리 맵이란 용어를 자주 듣습니다. 메모리 맵은 무엇일까요? 프로그램에 의해 실행되는 프로세스 입장에서 바라 본 메모리 레이아웃입니다. 지도를 보면 일정한 규칙(시/도)에 따라 구획이 나눠져 있듯이 메모리 영역을 속성별로 분류한 일종의 메모리 지도가 메모리 맵입니다. 


메모리 맵의 구조도 알아보기

메모리 맵은 데이터 영역, 코드와 같은 속성으로 마킹된 다양한 영역으로 구성돼 있습니다. 다음 그림을 보면서 메모리 맵에 대해 더 자세히 알아봅시다.

 
출처: Learn the architecture - ARMv8-A memory systems https://developer.arm.com/documentation/100941/latest/

그림 17.1 메모리 맵의 구조도 

그림 17.1을 보면 가장 왼쪽 열에는 메모리 구간별 주소를, 가운데에는 영역 이름, 그리고 가장 오른쪽에는 메모리 속성을 확인할 수 있습니다.

메모리 맵의 가장 윗부분에 있는 Peripheral부터 봅시다. Peripheral로 분류되는 메모리 영역은 디바이스 메모리 타입으로 분류됩니다. 이 영역에 있는 데이터는 오로지 읽고/쓰기만 가능하며 직접 실행할 수는 없습니다. Peripheral은 프로그래머들이 작성한 코드가 로딩될 수 없는 영역이기 때문입니다. 

그다음으로 가운데에 있는 Kernel Code를 봅시다. Kernel Code에 있는 데이터는 실행만 가능하며 읽고/쓰기는 불가능합니다. 운영체제 커널 코드가 로딩된 영역이므로 코드를 읽거나 쓰면 코드 내용이 바뀌므로 읽고/쓰기는 불가능하도록 설정합니다. 

이어서 바로 위에 있는 Kernel Data를 봅시다. Kernel Data에 있는 데이터는 오로지 읽고/쓰기만 가능하며 직접 실행할 수는 없습니다.  

여기서 주의깊게 봐야 할 점이 있습니다. Kernel Data와 Kernel Code의 공통적인 속성은 Cacheable, Privileged입니다. 둘 다 캐시에 의해 처리되며 Privileged 권한으로 접근됩니다. 또는 Privileged는 Armv8 기준으로 EL1 이상의 권한으로 실행되는 동작 권한을 뜻합니다.

마지막으로 가장 아랫부분에 있는 App Data와 App Code의 영역을 알아봅시다. Kernel Data와 Kernel Code 영역이 지닌 속성과 대부분 같지만 Unprivileged라는 속성이 다릅니다. Unprivileged(비특권)는 가장 낮은 실행 권한을 뜻하며, Unprivileged 권한으로 실행되는 코드는 Privileged 권한이 부여된 코드나 데이터에 직접 접근할 수 없습니다.

메모리 맵을 구성하는 주요 영역

앞에서 든 예시와 같이 메모리 맵은 메모리 주소별 영역의 이름과 각 속성 정보로 구성돼 있습니다. 그림에서 설명한 내용 이외에 메모리 맵은 다음과 같은 속성 정보로 구성돼 있습니다.

 메모리 페리페럴
 메모리 영역의 코드와 데이터
 운영체제와 유저 애플리케이션에 포함되는 리소스

그림 17.1을 보면 다양한 메모리 영역이 존재하나 Arm 아키텍처 관점에서 보면 메모리 타입은 크게 두 가지로 분류됩니다.

 디바이스 메모리: Peripheral  
 노멀 메모리: Kernel Code, Kernel Data, App Code, App Data 
 
Kernel Code, Kernel Data, App Code, App Data 주소 영역은 노멀 메모리로 관리하며 페리페럴은 디바이스 메모리(메모리 맵드 I/O)로 처리됩니다.

메모리 맵에 존재하는 노멀 메모리 타입과 디바이스 메모리 타입을 구분하는 가장 큰 차이점은 무엇일까요? 바로 명령어나 메모리를 리오더링하는 동작입니다. 노멀 메모리 타입으로 분류된 명령어나 데이터를 Arm 코어가 실행하면 내부에서 다양한 방식으로 최적화 작업을 수행합니다. 대표적으로 메모리 접근 순서(메모리 리오더링)나 명령어의 실행 순서를 바꿉니다. 소프트웨어 개발자가 작성한 대부분의 코드는 노멀 메모리 타입으로 분류되며, 명령어와 데이터는 이 방식으로 처리됩니다. 

디바이스 메모리를 처리하는 방식

Arm 프로세서가 디바이스 메모리를 처리하는 방식은 노멀 메모리를 처리하는 방식과 다릅니다. 디바이스 메모리로 분류된 영역의 데이터를 Arm 코어가 보면 메모리 리오더링와 같은 기법을 적용해 실행하지 않습니다. 디바이스 메모리 타입은 메모리 맵드 I/O 방식이며, 대부분 페리페럴은 디바이스 메모리 타입으로 관리됩니다. 메모리 맵드 I/O로 정의된 주소에 접근하는 한 가지 예를 들까요?

 0xD000_0000 주소 접근: 페리페럴에 인터럽트 활성화
 0xD000_0004 주소 접근: 페리페럴에 인터럽트를 잘 받았다는 ACK(Acknowledge)를 전달
 0xD000_0008 주소 접근: 페리페럴의 인터럽트를 비활성화

위 목록은 메모리 맵드 I/O 기반 물리 주소와 특징을 나타냅니다. 각각 드라이버에서 0xD000_0000, 0xD000_0004, 0xD000_0008 주소에 접근해 페리페럴을 제어할 수 있습니다.

0xD000_0000 주소와 0xD000_0008 주소에 순차적으로 접근해 페리페럴을 제어하려는 루틴이 있는데, 이를 Arm 코어가 노멀 메모리 타입으로 처리하면 주소에 접근하는 순서를 바꿔서 실행할 수 있습니다. 예를 들어 페리페럴에서 인터럽트를 받으면 인터럽트에 대한 ACK를 보내고 페리페럴의 인터럽트를 비활성화하는 명령어를 작성했는데, 명령어의 순서가 바뀌면 오동작할 수 있습니다. 즉, 디바이스 메모리 타입으로 분류되는 영역이 노멀 메모리 타입처럼 처리되면 이처럼 예상치 못한 사이트 이펙트가 유발될 수도 있습니다.

+ Recent posts