[OS] 하드웨어 기반 메모리 주소 변환
하드웨어가 명령어 반입, 탑재, 저장 등의 가상 주소를 정보가 실제 존재하는 물리 주소로 변환하는 과정을 알아보자.
Apr 18, 2024
✅ 주소 공간
먼저, 주소 공간에 대한 내용을 살펴보자.
실행 중인 프로그램이 가정하는 메모리의 모습이다. 운영체제의 메모리 개념을 이해하는 것이 메모리를 어떻게 가상화할지를 이해하는 핵심이다.
→ 두 영역은 확장되거나 축소될 수 있기 때문에 힙과 스택의 확장 방향은 반대이다.
가상 메모리 시스템의 주요 목표는 투명성, 효율성, 보호이다.
투명성
- 운영체제는 실행 중인 프로그램이 가상 메모리의 존재를 인지하지 못하도록 가상 메모리 시스템을 구현해야 한다. 프로그램은 자신이 전용 물리 메모리를 소유한 것처럼 행동해야 한다.
효율성
- 운영체제는 가상화과 시간과 공간 측면에서 효율적이도록 해야 한다. 시간적으로 프로그램이 너무 느리게 실행되어서는 안 되고, 공간적으로는 가상화를 지원하기 위한 구조를 위해 너무 많은 메모리를 사용해서는 안 된다. 효율적인 가상화를 구현할 때, 운영체제는 TLB 등의 하드웨어 기능을 포함하여 여러 하드웨어의 지원을 받아야 한다.
보호
- 운영체제는 프로세스를 다른 프로세스로부터 보호해야 하고 운영체제 자신도 프로세스로부터 보호해야 한다. 프로세스가 탑재, 저장, 혹은 명령어 반입 등을 실행할 때 어떤 방법으로든 다른 프로세스나 운영체제의 메모리 내용에 접근하거나 영향을 줄 수 있어서는 안 된다.
✅ 메모리 관리 API
메모리 공간의 종류
스택
- 할당과 반환이 프로그래머를 위해 컴파일러에 의해 암묵적으로 이루어진다. 따라서 자동 메모리라고 불린다.
힙
- 오랫동안 값이 유지되어야 하는 변수를 위해 힙이 필요하다. 모든 할당과 반환이 프로그래머에 의해 명시적으로 처리된다. 따라서, 수많은 버그의 원인이 된다.
malloc() 함수
- 힙에 요청할 공간의 크기를 넘겨 주면, 성공했을 경우 새로 할당된 공간에 대한 포인터를 사용자에게 반환하고 실패했을 경우 NULL을 반환한다.
free() 함수
- 더 이상 사용하지 않는 힙 메모리를 해제하기 위해 프로그래머는 free()를 호출한다. 메모리 할당보다 할당된 메로리를 언제, 어떻게 해제하고 더욱이 해제 여부를 확인하는 것이 더 어렵다.
✅ 주소 변환
여기서 다룰 기법은 하드웨어 기반 주소 변환(hardware-based address transition)또는 짧게 주소 변환(address transition)이다. 주소 변환을 통해 하드웨어는 명령어 반입, 탑재, 저장 등의 가상 주소를 정보가 실제 존재하는 물리 주소로 변환한다.
정확한 변환을 위해 운영체제는 메모리의 빈 공간과 사용 중인 공간을 항상 알고 있어야 하고, 메모리 사용을 제어하고 관리한다.
이 모든 작업의 목표는, 프로그램이 자신의 전용 메모리를 소유하고 그 안에 자신의 코드와 데이터가 있다는 환상을 만드는 것이다.
일단, 몇 가지 가정이 필요하다.
- 사용자 주소 공간은 물리 메모리에 연속적으로 배치되어야 한다.
- 주소 공간은 물리 메모리 크기보다 작다.
- 각 주소 공간의 크기는 같다.
비현실적인 가정들이지만, 점차 완화하여 실제 메모리의 가상화를 이끌어 낼 것이다!
동적 재배치 (하드웨어 기반)
하드웨어 기반 주소 변환을 이해하기 위하여 베이스와 바운드라는 아이디어가 필요하다.
각 CPU마다 2개의 하드웨어 레지스터가 필요하다. 하나는 베이스(base) 레지스터라 불리고, 다른 하나는 바운드(bound) 레지스터 혹은 한계(limit) 레지스터라고 불린다.
베이스와 바운드 레지스터는 CPU 칩 상에 존재하는 하드웨어 구조이다!
베이스와 바운드 쌍으로 우리가 원하는 위치에 주소 공간을 배치할 수 있다. 배치와 동시에 프로세스가 오직 자신의 주소 공간에만 접근한다는 것을 보장한다.
위 식으로 물리 주소가 결정된다.
그럼 바운드는 어디에 쓰는걸까? 바운드 레지스터는 보호를 지원하기 위해 존재한다. 프로세서는 먼저 메모리 참조가 합법적인가를 확인하기 위해 가상 주소가 바운드 안에 있는지 확인한다. 프로세스가 바운드보다 큰 가상 주소 또는 음수인 가상 주소를 참조하면 CPU는 예외를 발생시키고 프로세스는 종료될 것이다.
바운드 레지스터는 두 가지 방식으로 정의될 수 있다.
- 가상 주소를 베이스 레지스터에 더하기 전에 먼저 바운드 레지스터와 비교한다. (가상 주소와 비교)
- 베이스 레지스터를 더하고 그 결과가 바운드 안에 있는지 검사한다. (물리 주소와 비교)
여기서는 1번 방식을 사용한다고 가정하자.
예제
주소 공간의 크기가 4KB인 프로세스가 물리 주소 16KB에 탑재되어 있다고 가정하자.
예시에서 볼 수 있듯이 물리 주소를 얻기 위해서 가상 주소를 바운드와 비교하고, 베이스를 더하기만 하면 된다. 가상 주소가 너무 크거나 음수일 경우에는 폴트를 일으키고 예외가 발생한다!
운영체제 이슈
동적 재배치 지원을 위해 하드웨어 뿐만 아니라, 운영체제도 새로운 이슈가 등장한다. 베이스와 바운드 방식의 가상 메모리 구현을 위해 운영체제가 반드시 개입되어야 하는 중요한 시점이 존재한다.
- 프로세스가 생성될 때 운영체제는 주소 공간이 저장될 메모리 공간을 찾아 조치를 취해야 한다.
→ 다행히도 각 주소 공간은 물리 메모리 크기보다 작고 크기가 일정하다 라는 가정하에 쉽게 처리할 수 있다.
→ 새로운 프로세스가 생성되면 운영체제는 빈 공간 리스트를 검색하여 공간을 할당한다.
- 프로세스가 종료할 때(정상적으로 종료될 때 또는 잘못된 행동을 하여 강제로 죽게될 때), 프로세스가 사용하던 메모리를 회수하여 다른 프로세스나 운영체제가 사용할 수 있게 해야 한다.
→ 운영체제는 종료한 프로세스의 메모리를 다시 빈 공간 리스트에 넣고 연관된 자료 구조를 모두 정리한다.
- 운영체제는 문맥 교환이 일어날 때마다 베이스와 바운드 쌍을 저장하고 복원해야 한다.
→ 운영체제가 실행 중인 프로세스를 중단시키기로 결정하면 메모리에 존재하는 프로세스 별 자료 구조 안에 베이스와 바운드 레지스터 값을 저장한다. 이 자료 구조는 PCB(process control block)이라고 불린다.
- 운영체제는 예외 핸들러 또는 호출될 함수를 제공해야 한다.
→ 운영체제는 부팅 시 특권 명령어를 사용하여 핸들러를 설치한다.
하지만, 메모리 변환은 운영체제의 개입 없이 하드웨어에서 처리된다. 운영제체는 프로세스가 잘못된 행동을 했을 때에만 개입한다.
✅ 요약
가상 메모리 시스템의 주요 목표는 투명성, 효율성, 보안이며, 이를 위해 운영체제는 프로그램이 가상 메모리의 존재를 인지하지 못하도록 구현해야 한다. 메모리 관리 API는 스택과 힙을 포함하며, 이들은 각각 컴파일러와 프로그래머에 의해 할당과 반환이 이루어진다.
하드웨어 기반 주소 변환은 가상 주소를 실제 존재하는 물리 주소로 변환한다. 이를 위해 베이스와 바운드라는 두 가지 레지스터가 필요하다. 운영체제는 프로세스 생성, 종료, 문맥 교환 등에서 개입해야 하며, 이 모든 작업의 목표는 프로그램이 자신의 전용 메모리를 소유하고 그 안에 자신의 코드와 데이터가 있다는 환상을 만드는 것이다.
베이스 바운드 방식으로 주소 변환을 하면, 프로세스의 모든 메모리 접근을 제어할 수 있고, 접근이 항상 주소 공간의 범위 내에서 이루어지도록 보장할 수 있다. 따라서 매우 효율적이고 보호 기능도 제공한다.
하지만!!! 스택과 힙을 모두 사용하지 않으면, 내부 단편화가 발생한다. 내부 단편화를 해결하기 위한 방식이 다음에 소개된다. 바로 세그멘테이션이다! 다음에 살펴보자!
Share article