Tech </> tech.log
CHUL CHUL
· - ·
OS

[OS Theory 3] Memory Virtualization 1 (작성중)

운영체제의 핵심 역할중 하나인 가상화. 그중에서도 Memory의 가상화를 알아보자 (작성중)

이전 글에서 CPU 가상화를 다뤘다면, 이번 글에서는 가상화의 두 번째 축인 메모리 가상화를 다룬다. 주소 공간이라는 추상화가 왜 필요한지, 그리고 이를 구현하기 위한 기법들이 어떻게 진화해왔는지 살펴본다.


주소 공간의 탄생

초창기 컴퓨터에서는 프로그램 하나가 물리 메모리 전체를 독점했다.

멀티프로그래밍이 도입되면서 여러 프로세스가 메모리를 공유해야 했는데, 물리 주소를 직접 사용하면 한 프로세스가 다른 프로세스의 메모리를 읽거나 덮어쓸 수 있다. 이를 방지하기 위해 도입된 추상화가 **주소 공간(address space)**이다.

각 프로세스는 자신만의 가상 주소 공간을 가진다. 이 공간은 전형적으로 주소 0번에 코드(code) 영역이 놓이고, 그 위로 힙(heap)이 위쪽으로 성장하며, 주소 공간의 최상단에서 스택(stack)이 아래쪽으로 성장한다.

힙과 스택 사이의 미사용 영역은 실제로 물리 메모리를 차지하지 않는다 — 이것이 가상화의 핵심이다.

OS의 메모리 가상화가 달성해야 하는 세 가지 목표가 있다.

투명성(Transparency): 프로세스는 자신이 물리 메모리를 직접 사용하고 있다고 믿어야 한다. 가상화 메커니즘은 프로그램에 보이지 않아야 한다.

효율성(Efficiency): 시간과 공간 모두에서 효율적이어야 한다. 주소 변환이 프로그램을 크게 느리게 만들면 안 되고, 변환에 필요한 자료구조가 메모리를 과도하게 차지하면 안 된다.

보호(Protection): 각 프로세스는 다른 프로세스나 OS의 메모리를 접근할 수 없어야 한다. 주소 공간 격리가 보장되어야 한다.


메모리 API

C 언어에서 메모리 할당은 두 가지 유형으로 나뉜다.

스택 메모리: 함수 내 지역 변수가 자동으로 할당/해제된다. 컴파일러가 관리하므로 프로그래머가 신경 쓸 것이 없다.

void func() {
    int x;              // 스택에 자동 할당
    // ...
}                       // 함수 종료 시 자동 해제

힙 메모리: malloc()으로 할당하고 free()로 해제한다. 프로그래머의 책임이다. 여기서 수많은 버그가 탄생한다.

int *x = (int *)malloc(sizeof(int));  // 힙에 4바이트 할당
*x = 42;
free(x);    // 반드시 해제해야 함

흔한 실수들을 짚어보자. **메모리 누수(memory leak)**는 free()를 잊는 것이다.

단기 실행 프로그램에서는 프로세스 종료 시 OS가 전부 회수하므로 큰 문제가 안 되지만, 장기 실행 서버에서는 치명적이다.

해제 후 사용(use-after-free)은 free() 이후 포인터를 참조하는 것으로, 미정의 동작(undefined behavior)을 유발한다. **이중 해제(double free)**는 같은 메모리를 두 번 free()하는 것으로, 힙 자료구조를 망가뜨린다.

잘못된 크기 할당malloc(strlen(s)) 처럼 null 종단 문자를 빠뜨리는 등의 실수다.

malloc()은 라이브러리 함수이지 시스템 콜이 아니다.

내부적으로 brk/sbrk 시스템 콜(또는 큰 할당의 경우 mmap)을 사용하여 OS에 힙 확장을 요청한다.

free()도 마찬가지로 라이브러리가 관리하는 자유 리스트(free list)를 조작하며, 반드시 OS에 메모리를 즉시 반환하는 것은 아니다.


주소 변환의 진화

프로세스가 생성하는 모든 메모리 주소는 **가상 주소(virtual address)**다. 하드웨어(MMU)와 OS가 협력하여 이를 **물리 주소(physical address)**로 변환한다. 이 변환 메커니즘은 역사적으로 세 단계를 거쳐 진화했다.

Base & Bounds (동적 재배치)

가장 단순한 기법이다. MMU에 base 레지스터bounds(또는 limit) 레지스터 두 개를 둔다.

physical address = virtual address + base
if (virtual address >= bounds) → fault

프로세스의 전체 주소 공간이 물리 메모리의 연속된 영역에 통째로 올라간다. 장점은 단순하고 빠르다는 것이다 — 변환이 덧셈 하나와 비교 하나다. 하지만 치명적인 단점이 있다: 내부 단편화(internal fragmentation). 힙과 스택 사이의 사용되지 않는 거대한 빈 공간까지 물리 메모리를 차지한다.

..작성중입니다.

,right:'