[OS] 기억장치 개요와 주기억장치 할당 기법 - 11

2026. 3. 27. 11:14운영체제(OS)

기억장치 개요와 주기억장치 할당 기법

기억장치의 종류와 계층구조, 그리고 주기억장치에 프로그램을 올리는 세 가지 기법(단일 분할 / 다중 분할)의 동작 원리와 장단점을 정리한다.

💡 핵심: CPU는 오직 주기억장치에 있는 데이터만 처리할 수 있다. 따라서 주기억장치를 어떻게 나누고 관리하느냐가 곧 시스템 성능과 직결된다.

1. 기억장치 종류와 계층구조

컴퓨터의 기억장치는 크게 주기억장치, 보조기억장치, 캐시기억장치, 가상기억장치로 나뉜다. 여기에 CPU 내부에 존재하는 임시 저장 공간인 레지스터1까지 포함하면 속도·용량·가격에 따른 계층이 만들어진다.

아래가 그 계층이다. 위로 올라갈수록 빠르고 비싸며 용량이 작고, 아래로 내려갈수록 느리고 저렴하며 용량이 크다.

가장 빠름
레지스터
캐시 메모리 L1
캐시 메모리 L2
캐시 메모리 L3
주기억장치 (RAM)
보조기억장치 (SSD / NVMe)
보조기억장치 (HDD)
가장 느림
보조기억장치 (Optical / Tape)

주기억장치와 보조기억장치의 역할

주기억장치는 저장 공간에 연속적인 주소가 할당되어 있으며, CPU가 처리하는 모든 것은 반드시 여기에 먼저 올라와야 한다. 용량과 저장 방식이 처리 속도에 직접 영향을 준다.

보조기억장치는 전원이 꺼져도 데이터가 유지되는 영구 저장 장치다. 프로그램을 실행하려면 보조기억장치에서 주기억장치로 옮기는 과정이 선행된다. CPU는 보조기억장치에 직접 접근하지 못한다.

명령어 처리 과정 (Instruction Cycle)

CPU가 명령어 하나를 실행하는 흐름은 다음과 같다. 주기억장치에서 명령어를 가져오고(fetch), 해석하고(decode), 피연산자를 다시 주기억장치에서 가져온 뒤(operand fetch), 연산 후 결과를 다시 주기억장치에 기록한다(write back).

명령어 인출
명령어 해독
피연산자 인출
명령어 실행
결과 저장

이 사이클이 반복될수록 주기억장치와 CPU 사이의 속도 차이가 병목이 된다. 캐시 메모리는 이 병목을 줄이기 위해 존재한다. Unity에서 NativeArray와 Job System을 쓰는 이유 중 하나도 GC 힙 대신 캐시 친화적인 연속 메모리 배치로 이 사이클을 빠르게 만들기 위해서다.

2. 주기억장치 할당 기법 분류

프로그램이 실행되려면 주기억장치에 올라와야 한다. 어디에, 어떤 크기로 올릴지 결정하는 것이 주기억장치 할당 기법이다. 크게 두 가지로 나뉜다.

구분 방식 세부 기법 특징
연속 할당 기법 프로그램을 통째로 연속된 공간에 적재 단일 분할 할당, 다중 분할 할당 구현 단순, 단편화 발생 가능
분산 할당 기법 프로그램을 나눠서 분산 적재 페이징, 세그멘테이션 가상기억장치 시스템에서 사용

이 장에서는 연속 할당 기법 — 단일 분할과 다중 분할 — 을 중점적으로 다룬다. 분산 할당은 가상기억장치 챕터에서 이어진다.

3. 단일 분할 할당 기법

단일 분할 할당은 이름 그대로 한 번에 한 사람의 프로그램만 주기억장치에 올린다. 단일 사용자 시스템(Single User System)을 전제로, 주기억장치를 운영체제 영역과 사용자 영역 두 개로만 나눈다.

주기억장치
운영체제 영역
사용자 영역 (프로그램 1개만)

운영체제 영역과 사용자 프로그램 영역의 경계를 구분하기 위해 경계 레지스터(Boundary Register)2를 사용한다. 운영체제는 필요 시 사용자 영역을 침범할 수 있지만, 사용자 프로그램은 운영체제 영역을 침범할 수 없다.

가장 단순한 구조라 초기 운영체제에서 주로 쓰였다. 하지만 프로그램 하나의 크기가 사용자 영역보다 작으면 나머지 공간이 낭비되고, 한 번에 한 프로그램만 실행할 수 있다는 한계가 있다. 이 한계를 보완하기 위해 오버레이 기법과 스와핑 기법이 나왔다.

오버레이(Overlay) 기법

오버레이는 주기억장치보다 큰 프로그램을 실행하기 위한 방법이다. 프로그램을 여러 조각으로 나눠 보조기억장치에 저장해 두고, 실행에 필요한 조각만 그때그때 주기억장치에 올린다. 동시에 모든 기능이 메모리에 있을 필요 없이, 당장 필요한 부분만 올려두고 실행하는 구역성(국부성) 기반의 발상이다.

보조기억장치
(조각-1, 2, 3 저장)
조각-1 적재
실행 중
조각-2로 덮어씀
(오버레이)
계속 실행

프로그램을 어떤 단위로 나눌지, 어느 시점에 어떤 조각을 올릴지는 프로그램 작성자가 직접 설계해야 한다. PC(Program Counter)가 각 조각의 진입점을 관리한다. 개발자가 메모리 구조까지 신경 써야 하는 부담이 크다는 점에서 현대에는 OS와 가상기억장치가 이 역할을 대신한다.

스와핑(Swapping) 기법

스와핑은 현재 실행 중인 프로그램과 실행이 필요한 다른 프로그램을 통째로 교체하는 방식이다. 주기억장치에 있는 프로그램을 보조기억장치로 내보내고(Swap Out), 실행할 프로그램을 주기억장치로 불러오는(Swap In) 두 단계로 이루어진다.

① Swap OUT
주기억장치의 조각-1 (실행 중)
→ 보조기억장치로 이동

공간 확보
② Swap IN
보조기억장치의 조각-3
→ 주기억장치로 이동

새 프로그램 실행

하나의 사용자 프로그램이 완료되기 전까지 여러 번의 교체가 수행될 수 있다. 스와핑은 이후 가상기억장치의 페이징 기법으로 발전한다. 현대 OS에서도 메모리가 부족할 때 스왑 영역(swap partition 또는 pagefile)으로 데이터를 내리는 형태로 여전히 쓰인다.

4. 다중 분할 할당 기법

단일 분할은 한 번에 하나의 프로그램만 올릴 수 있어 자원 낭비가 크다. 다중 분할 할당은 주기억장치를 여러 영역으로 나눠 동시에 여러 프로그램을 올리는 방식이다. 분할 크기를 고정하느냐 유동적으로 가져가느냐에 따라 두 가지로 나뉜다.

(1) 고정 분할 할당 (MFT · 정적 할당)

MFT(Multiple contiguous Fixed parTition Allocation)는 주기억장치의 사용자 영역을 미리 고정된 크기로 나눠 두는 방식이다. 준비상태 큐에 대기 중인 프로그램을 각 영역에 순서대로 할당해 실행한다.

문제는 각 분할 영역의 크기가 고정되어 있다 보니, 들어오는 프로그램과 크기가 맞지 않아 사용되지 못하는 공간이 생긴다. 이를 단편화(Fragmentation)3 현상이라 한다.

OS
P1 (9K)
낭비 1K
P2 (5K)
낭비 5K
외부단편
10K
MFT — 고정 분할
OS
P1 (9K)
P2 (5K)
P3 (20K)
→ 못 들어감
크기 불일치 예
내부 단편화
분할 영역 안에서 남는 공간.
프로그램이 영역보다 작을 때 발생.
외부 단편화
분할 영역 자체가 프로그램보다 작아
아예 올리지 못하는 공간.

MFT는 할당 방식에 따라 두 가지로 나뉜다. 절대 번역과 적재는 프로그램을 번역할 때 어셈블러나 컴파일러가 특정 분할 영역 위치를 지정하는 방식이고, 재배치 번역과 적재는 분할 영역을 미리 정하지 않고 프로그램이 실행될 때 하나의 준비상태 큐를 통해 순서대로 영역에 할당하는 방식이다. 현재는 사용하지 않는 기법이다. 실제로 IBM이 1960년대 OS/360에서 MFT를 도입했고, 1980년대 이후 가상기억장치 기반 시스템이 보편화되면서 완전히 대체됐다.

(2) 가변 분할 할당 (MVT · 동적 할당)

MVT(Multiple contiguous Variable parTition allocation)는 고정 분할의 단편화 문제를 줄이기 위해 나온 방식이다. 미리 주기억장치를 나눠 두지 않고, 프로그램이 적재될 때 필요한 크기만큼만 영역을 분할해 할당한다.

🕰 고정 분할(MFT) 시절
😤 운영체제
"30KB를 10KB씩 3개 영역으로 나눠뒀는데, P3 (20KB)는 들어갈 자리가 없네. 그냥 못 올린다."
🤔 "사전에 나눠두지 말고, 프로그램이 올 때마다 필요한 만큼만 잘라주면 안 되나?"
✅ 가변 분할(MVT)
😌 운영체제
"P1(9K) 적재 → P2(5K) 적재 → P1 종료 → 빈 공간 생김 → P3(15K) 적재. 고정 분할보다 훨씬 유연하다."

내부 단편화는 사라지지만, 프로그램들이 올라갔다 내려갔다를 반복하다 보면 사용 가능한 공간이 여러 군데 조각조각 흩어지는 외부 단편화는 여전히 발생한다. 이를 해결하는 방법이 집약(Compaction)이다.

집약(Compaction)

집약은 흩어진 빈 공간들을 한 곳으로 모아 큰 연속 공간을 만드는 작업이다. 빈 공간이 여기저기 쪼개져 있어 큰 프로그램을 올리지 못하는 상황을 해소한다.

⚠ 집약 전 — 외부 단편화
OS (고정)
P1 실행 중 (9K)
빈 공간 (5K)
P2 종료 후 빈 공간
P3 실행 중 (15K)
빈 공간 (1K)
✅ 집약 후 — 연속 빈 공간 확보
OS (고정)
P1 실행 중 (9K)
P3 실행 중 (15K)
빈 공간 (6K) ← 통합됨

단점은 집약하는 동안 시스템 전체가 멈춘다는 것이다. 프로그램과 데이터를 다른 주소로 옮기면서 해당 주소를 참조하는 값들도 모두 수정해야 하기 때문에, 집약에는 상당한 시간이 소모된다. Unity의 Boehm GC가 Stop-the-World 방식으로 힙을 정리하는 것과 같은 원리다. 메모리 압축(compaction)이 곧 GC pause의 근본 원인이다.

MFT vs MVT 비교

항목 고정 분할 (MFT) 가변 분할 (MVT)
분할 시점 사전에 고정 적재 시 동적 결정
내부 단편화 발생 없음
외부 단편화 발생 발생 (집약으로 해소)
프로세스 크기 제약 분할 크기에 묶임 상대적으로 유연
현재 사용 여부 사용 안 함 사용 안 함 (페이징으로 발전)

MVT도 결국 외부 단편화와 집약 비용이라는 문제를 완전히 해결하지 못한다. 이 문제를 근본적으로 해결하는 것이 분산 할당 기법인 페이징세그멘테이션이다. 페이징은 물리 메모리를 고정 크기의 프레임으로, 프로세스는 같은 크기의 페이지로 나눠 비연속 배치를 가능하게 해 외부 단편화를 없앤다. 현대 Linux·Windows·macOS는 모두 페이징 기반으로 동작한다. 세그멘테이션은 코드·데이터·스택 같은 논리 단위로 나누는 방식인데, x86-64 아키텍처에서는 레거시 호환 목적으로만 남아 있고 실질적 메모리 관리는 페이징이 전담한다. 두 기법은 가상기억장치 챕터에서 이어진다.

📎 용어 설명

  1. 레지스터(Register) — CPU 내부에 존재하는 가장 빠른 임시 저장 공간. 현재 처리 중인 명령어나 데이터를 잠시 보관한다. 용량이 극히 작고, 소프트웨어에서 직접 접근할 수 없다.
  2. 경계 레지스터(Boundary Register) — 운영체제 영역과 사용자 영역의 경계 주소를 저장하는 레지스터. 사용자 프로그램이 이 주소 이하의 영역(OS)을 침범하려 하면 트랩이 발생한다. Fence Register라고도 한다.
  3. 단편화(Fragmentation) — 메모리 공간이 작은 조각들로 나뉘어 실질적으로 사용하지 못하는 낭비 공간이 생기는 현상. 분할 영역 안에서 발생하면 내부 단편화, 분할 영역 사이에서 발생하면 외부 단편화라 한다.
운영체제 기억장치 주기억장치 단일분할 다중분할 MFT MVT 오버레이 스와핑 단편화 집약