1부. OS 개발을 위한 힘찬 첫걸음
3장. 64비트 프로세서의 이모저모
운영 모드
운영 모드 사이의 관계와 운영 모드의 전환
- 처음 전원이 켜지거나 리셋되면 프로세서는 리얼 모드로 진입
- 리얼 모드에서 전환할 수 있는 모드는 공식적으로 보호 모드가 유일
- 보호 모드에서는 가상 8086 모드, IA-32e 모드, 리얼 모드로 전환 가능
- 시스템 관리 모드는 모든 모드에서 진입 가능
보호 모드 레지스터
IA-32e 모드 레지스터
범용 레지스터 (General Purpose Register)
범용 레지스터 : 계산, 메모리 어드레스 지정, 임시 저장 공간 등의 목적으로 사용
64비트 범용 레지스터는 하위 32비트, 16비트, 8비트 크기로 구분하여 접근할 수 있고, 레지스터에 접두사(Prefix)나 접미사(Postfix)를 붙여 접근하는 크기를 표시한다.
64비트 레지스터 : R 접두사
- 32비트 레지스터 : E 접두사나 D접미사
- 16비트 레지스터 : 접두사를 붙이지 않거나 W 접미사
- 상위 8비트 : 끝자리를 H로 변경
하위 8비트 : 끝자리를 L로 변경하거나 L 접미사
운영 모드에 따라 접두사를 결합하는 방법에는 일정한 규칙이 있으며 사용한 접두사에 따라 명령어가 처리하는 오퍼랜드나 어드레스의 크기가 달라진다.
표를 보면 IA-32e 모드의 기본 오퍼랜드 크기는 32비트, 기본 어드레스 크기는 64비트로 설계되었다. 기본 오퍼랜드 크기가 32비트로 설계되어 64비트 어드레스를 표현할 수 없으므로 RIP 상대 어드레스라는 어드레스 계산 방식이 도입되었다. RIP 레지스터는 현재 수행 중인 명령의 어드레스를 가리키는 레지스터이다. 기본 어드레스 크기가 32비트이므로 RIP 레지스터의 값에 상위 2G와 하위 2G 범위까지만 표현할 수 있다. 범위를 벗어나는 어드레스에 접근하는 경우를 대비하여 분기명령어(jmp)에 예외를 두었다. 무조건 분기 명령어의 오퍼랜드 크기는 기본 64비트로 하여 전체 어드레스 범위에서 이동이 가능하도록 하였다.
세그먼트 레지스터 (Segment Register)
세그먼트 레지스터
- (리얼 모드) 고정된 크기의 어드레스 영역을 지정하는 역할
- (보호 모드, IA-32e 모드) 접근 권한(Privilege Level), 세그먼트의 시작 어드레스와 크기 등을 지정하는데 사용
세그먼트 레지스터의 역할은 주소 공간을 목적에 따라 구분하는 것이다. 주소 공간을 구분하는 방법은 메모리 관리 기법(세그먼테이션, 페이징)과 깊은 관계가 있다. 세그먼트 레지스터를 통해 주소 공간을 구분하는 방식이 세그먼테이션이다.
컨트롤 레지스터 (Control Register)
컨트롤 레지스터 : 운영 모드를 변경하고, 현재 운영 중인 모드의 특정 기능을 제어하는 레지스터
CR0, CR4, CR8 레지스터에서는 64비트 중 상위 32비트를 0으로 설정
- CR2 레지스터의 경우는 64비트 영역을 모두 사용 가능
- CR3 레지스터는 비트 40부터 비트 51까지 모두 0으로 설정
메모리 관리 기법
- 세그먼테이션(Segmentation)
- 전체 영역을 원하는 크기로 나누어 관리하는 방식
- 세그먼트 레지스터에 세그먼트의 주소 혹은 디스크립터(Descriptor)라고 불리는 자료구조의 위치를 설정
- 페이징(Paging)
- 일정한 단위로 잘라진 조각을 모아 원하는 크기로 관리하는 방식
- 컨트롤 레지스터 중에 CR3 레지스터에 페이지 디렉터리라고 불리는 자료구조의 물리 주소를 설정
리얼 모드의 메모리 관리 방식
리얼 모드는 최대 1MB까지 주소 공간을 사용하여 세그먼테이션만 지원한다. 세그먼트의 크기는 64K로 고정이고, 세그먼트의 시작 어드레스는 세그먼트 레지스터에 직접 설정한다. 세그먼트의 시작 어드레스는 기준 어드레스(Base Address)로 사용된다.
리얼 모드의 세그먼테이션은 세그먼트의 기준 주소(세그먼트 레지스터 값에 16을 곱한 값)에 범용 레지스터 값을 더하는 방식이다.
보호 모드의 메모리 관리 방식
보호 모드는 세그먼테이션과 페이징을 모두 지원한다. 보호 모드의 세그먼테이션은 세그먼트 레지스터(세그먼트 셀렉터)에 디스크립터 자료구조의 위치(Offset)를 설정하는 방식으로 바뀌었다.
세그먼트 디스크립터는 세그먼트에 대한 정보를 나타내는 디스크립터이다. 세그먼트 디스크립터에는 세그먼트의 시작 어드레스와 크기, 권한(Privilege), 타입(Type) 등의 정보가 있다.
특권레벨(DPL, Descriptor Privilege Level)은 해당 세그먼트에 접근하기 위한 최소한의 권한으로 특권 레벨은 0~3 사이의 값을 가진다. 숫자가 작을수록 궈ㄴ한이 높고, 세그먼트에 접근하려면 현재 수행 중인 특권 레벨(CPL, Current Privilege Level)이 적어도 디스크립터에 설정된 권한과 같거나 높아야(작은 숫자여야) 한다. (CPL ≤ DPL)
세그먼트 디스크립터는 메모리상에 위치하는 자료구조의 일종으로 GDT(Global Descriptor Table)라고 불리는 곳에 모여있다. GDT의 위치와 관련된 레지스터는 GDTR(Global Descriptor Table Register)이며, 16비트 GDT 크기 필드와 32비트 기준 주소 필드로 구성된 자료구조의 물리주소를 넘겨 받는다.
세그먼트 디스크립터의 기준 주소에 범용 레지스터의 오프셋을 더하면 선형 주소가 나온다. 프로세서는 실제 메모리에 접근할 때 선형 주소를 기반으로 물리 주소를 계산한다. 보호 모드에서는 세그먼트의 크기를 지정할 수 있다.
다음은 세그먼테이션과 페이징을 통해 논리 주소를 물리 주소로 변환하는 과정이다.
페이징은 물리 메모리를 페이지(Page)라고 불리는 일정한 크기로 나누고, 선형 주소와 물리 주소를 나눠 놓은 페이지를 연결하는 방식이다. 페이징을 사용하면 물리 메모리 크기보다 더 큰 영역의 선형 주소도 물리 페이지만 연결하면 사용이 가능하므로, 주소 공간을 넓게 사용할 수 있는 장점이 있다.
- 같은 물리 페이지를 여러 선형 주소에 연결함으로써 응용 프로그램끼리 공유하는 메모리를 손쉽게 처리함
응용 프로그램마다 독립적인 주소를 보장하고 싶다면 페이징 자료구조를 따로따로 생성하고 물리 메모리에 중복되지 않게 연결함
페이징은 페이지 크기에 따라 물리 메모리를 4KB 크기로 나누고 선형 주소를 3단계로 구분하는 방식과 물리 메모리를 4MB 크기로 나누고 선형 주소를 2단계롤 구분하는 방식이 있다. 두 페이징의 기본 원리는 같으므로 3단계 페이징에 대해 살펴보도록 하겠다. 3단계 페이징은 선형 주소를 디렉터리, 테이블, 오프셋 세 부분으로 나누며 물리 메모리를 4KB 페이지로 나누어 관리하는 방식이다.
CR3 레지스터에 설정된 어드레스로 페이지 디렉터리의 시작 주소를 찾는다.
- 페이지 디렉터리의 시작 주고에 선형 주소의 디렉터리 오프셋을 이용해서 해당 디렉터리 엔트리를 찾는다(디렉터리 엔트리에 설정된 값이 페이지 테이블의 시작 주소).
- 페이지 테이블의 시작 주소에 선형 주소의 테이블 오프셋을 이용해서 해당 페이지 테이블 엔트리를 찾는다(페이지 테이블 엔트리에 설정된 값이 4KB 페이지의 시작 주소).
페이지의 시작 주소에 선형 주소의 페이지 오프셋 값을 더해 실제 물리 주소로 변환한다.
IA-32e 모드의 메모리 관리 방식
IA-32e 모드는 두 가지 서브 모드, 호환 모드와 64비트 모드가 있다. 호환 모드는 보호 모드와 동작이 같으므로 64비트 모드에 대해서만 살펴보도록 한다.
- 세그먼트 디스크립터에 설정된 기준 주소와 크기에 관계없이 모든 세그먼트가 기준 주소는 0, 크기는 64비트 전체로 설정된다.
IA-32e 모드는 호환 모드와 64비트 모드의 두 가지 서브 모드를 지원하므로 이를 구분하고자 코드 세그먼트 디스크립터에 L 필드(비트21)가 추가된다.