lhywk 님의 블로그

[운영체제 OS] Memory Management 본문

Operating System

[운영체제 OS] Memory Management

lhywk 2026. 2. 1. 19:05

1. 메모리 관리의 개요 및 요구조건

1-1 메모리 관리의 정의

다중 프로그래밍 환경에서 한정된 주기억장치(RAM)를 여러 프로세스에 동적으로 할당하고 배치하는 운영체제의 기능이.

1-2 메모리 관리의 5가지 요구조건

  • 재배치 (Relocation): 프로세스가 메모리 내 어느 위치에 있더라도 정상 실행되도록 지원.
  • 보호 (Protection): 타 프로세스나 OS 영역 침범 방지.
  • 공유 (Sharing): 공통 코드/데이터를 여러 프로세스가 함께 사용하도록 허용.
  • 논리적 구성 (Logical Organization): 프로그램을 모듈/세그먼트 단위로 관리 및 맵핑.
  • 물리적 구성 (Physical Organization): RAM과 디스크 간의 데이터 이동(스와핑 등) 관리.

 

2. 연속 메모리 할당 기법

프로그램 전체가 하나의 커다란 공간에 연속적으로 할당되는 것이다.

2-1 고정 분할 (Fixed Partitioning)

메모리를 미리 고정된 크기로 나누어 두는 방식이다.

프로세스가 분할 영역보다 작으면, 남는 공간이 발생. 이를 내부 단편화라고 한다.

1. 균등 분할 (Equal-size Partitioning)

운영체제를 제외한 모든 메모리 파티션의 크기가 동일하다.

  • 장점: 관리가 매우 간단하다.
  • 단점: 심각한 내부 단편화를 유발. 예를 들어, 8MB 크기의 파티션에 1MB짜리 프로세스를 넣으면 7MB의 공간이 그대로 낭비. 또한, 파티션 크기(8MB)보다 조금이라도 큰 프로세스는 아예 실행될 수 없다.

2. 비균등 분할 (Unequal-size Partitioning)

메모리 파티션들의 크기가 서로 다르다.

  • 장점: 작은 프로세스는 작은 파티션에, 큰 프로세스는 큰 파티션에 할당하여 메모리 낭비를 줄일 수 있다.
  • 단점: 여전히 내부 단편화 문제가 발생. 예를 들어, 위 그림 (b)에서 9MB 크기의 프로세스는 12MB 파티션에 들어가야 하므로 3MB의 공간이 낭비. 또한, 파티션의 개수가 정해져 있어 동시에 실행할 수 있는 프로세스의 수가 제한된다.

고정 분할에서의 메모리 배정

  • 파티션별 큐 (Per-Partition Queue)각 파티션마다 별도의 대기 줄을 두는 방식이다.
  • 단일 큐 (Single Queue)하나의 공통된 대기 줄에 모든 프로세스를 세우는 방식. 파티션이 비면, OS는 대기 줄을 쭉 훑어보고 그 파티션에 들어갈 수 있는 가장 적합한 프로세스를 선택한다.

2-2 동적 분할 (Dynamic Partitioning)

프로세스가 필요한 만큼만 딱 맞춰서 메모리를 할당한다.

동적 분할의 결과

  1. (a) → (d) 프로세스 할당: 처음에는 텅 빈 메모리(a)에 프로세스 1, 2, 3이 순서대로 들어온다. 각 프로세스는 자신이 요청한 크기만큼만 정확히 할당. 이 덕분에 고정 분할의 문제였던 내부 단편화는 발생하지 않는다.
  2. (e) 프로세스 종료: 중간에 있던 프로세스 2가 종료되자, 그 자리가 14M 크기의 빈 공간으로 남음.
  3. (f) → (h) 단편화 발생: 새로운 프로세스 4(8M)가 들어오면서 14M 공간에 배치되고, 6M의 작은 조각이 남음(f). 이후 프로세스 1이 종료되고(g), 그 자리에 새 프로세스 2(14M)가 들어오면서 또 다른 6M 조각이 남음(h).

최종 상태 (h)를 보면, 비어있는 공간이 6M, 6M, 4M로 총 16M나 남아있다. 하지만 이 공간들이 모두 분리되어 흩어져 있기 때문에, 만약 10M 크기의 새로운 프로세스가 들어오려고 해도 할당해 줄 수 없는 문제가 발생한다.

이처럼 사용 가능한 메모리 총량은 충분하지만, 그것이 연속된 공간이 아니어서 할당할 수 없는 상태를 외부 단편화라고 한다.

 

해결책: 집약(Compaction)을 통해 조각들을 모아야 하지만 부하가 크다.

 

동적 분할의 4가지 배치 알고리즘

16Mbyte 블록의 배정 전과 후의 메모리 구성의 예

1. 최초 적합 (First-Fit)

  • 메모리의 처음부터 검색해서, 프로세스가 들어갈 수 있는 첫 번째 빈 공간에 바로 할당한다.

2. 최적 적합 (Best-Fit)

  • 모든 빈 공간을 검색해서, 프로세스가 들어가기에 충분한 공간 중 가장 크기가 딱 맞는(가장 작은) 곳에 할당한다.

3. 최악 적합 (Worst-Fit)

  • 모든 빈 공간을 검색해서, 프로세스를 넣을 수 있는 가장 큰 공간에 할당한다.

4. 순환 적합 (Next-Fit)

  • 최초 적합과 유사하지만, 검색을 메모리 처음이 아닌 이전에 마지막으로 할당했던 위치부터 시작한다.

 

3. 버디 시스템 (Buddy System)

고정 분할과 동적 분할의 절충안으로, 메모리 블록을 2^k 크기로 분할하여 관리한다.

  • 할당: 요청 크기에 맞는 최소 2^k 블록을 찾고, 없으면 상위 블록을 반으로 쪼개 할당한다.
  • 해제(병합): 메모리 반환 시 인접한 버디 블록이 비어있으면 다시 합쳐 큰 블록을 만든다.

1. 할당 (분할) 과정

  • A: 100K 요청
    • 100K보다 큰 최소의 2k 크기는 128K
    • 시스템은 1MB → 512K → 256K → 128K로 블록을 연속으로 분할하여 A에 할당
    • 결과: [A:128K] [128K] [256K] [512K]
  • B: 240K 요청
    • 240K보다 큰 최소의 2k 크기는 256K
    • 남아있던 256K 블록을 B에 할당
    • 결과: [A:128K] [128K] [B:256K] [512K]
  • C: 64K 요청
    • 64K는 2k 크기이므로 바로 64K를 할당
    • 남아있던 128K 블록을 64K 버디 두 개로 분할하여 하나를 C에 할당
    • 결과: [A:128K] [C:64K] [64K] [B:256K] [512K]
  • D: 256K 요청
    • 남아있던 512K를 256K 버디 두 개로 분할하여 하나를 D에 할당

2. 반환 (병합) 과정

메모리 반환 시에는 "나의 버디도 비어있는가?"를 확인하여, 비어있다면 합치는 병합 과정이 핵심

  • B 반환: 256K 공간이 비게 됨. B의 버디는 A, C 등이 차지하고 있으므로 병합은 일어나지 않는다.
  • A 반환: 128K 공간이 비게 됨. A의 버디는 C가 차지하고 있으므로 병합은 일어나지 않는다.
  • C 반환: 64K 공간이 비게 됨.
    • C의 버디(바로 옆 64K)도 비어있으므로, 둘을 합쳐 하나의 128K 블록으로 병합
  • E 반환: 128K 공간이 비게 됨
    • 1단계 병합: E의 버디(바로 옆 128K)도 비어있으므로, 둘을 합쳐 하나의 256K 블록으로 병합.
    • 2단계 병합: 새로 만들어진 256K 블록이 자신의 버디(B가 반환한 256K)를 확인하니 역시 비어있다. 따라서 둘을 합쳐 하나의 512K 블록으로 다시 병합.
  • D 반환: 256K 공간이 비게 됨
    • D의 버디(오른쪽 끝 256K)도 비어있으므로 512K 블록으로 병합.
    • 새로 만들어진 512K 블록이 자신의 버디(왼쪽의 512K)를 확인하니 역시 비어있다.
    • 최종적으로 두 512K 블록이 합쳐져 원래의 1MB 통 메모리로 복구.

 

4. 주소 변환 및 하드웨어 지원

4-1 주소의 종류

  • 논리 주소 (Logical Address): CPU가 생성하며 프로세스 관점에서 0번지부터 시작하는 주소이다.
  • 물리 주소 (Physical Address): 실제 메모리 칩에 접근하기 위한 주소이다.

4-2 MMU (Memory Management Unit)

실행 시간에 논리 주소를 물리 주소로 변환하는 하드웨어 장치이다.

  • 베이스 레지스터 (Base Register): 시작 물리 주소 저장.
  • 경계 레지스터 (Limit Register): 프로세스 크기 저장 (보호 기능).
  • 계산식: 물리 주소 = 베이스 레지스터 값 + 논리주소

 

5. 불연속 메모리 할당 기법

5-1. 페이징 (Paging)

  • 페이지 (Page): 프로세스(논리 메모리)를 나누는 고정된 크기의 조각.
  • 프레임 (Frame): 주기억장치(물리 메모리)를 나누는 조각으로, 페이지와 크기가 정확히 동일.
  • 페이지 테이블 (Page Table): 운영체제가 각 프로세스마다 관리하는 주소 변환용 테이블.

특징

  • 외부 단편화 없음: 빈 프레임 어디든 페이지를 넣을 수 있기 때문이다.
  • 내부 단편화 발생: 마지막 페이지가 꽉 차지 않을 때 발생한다.

  • (a) → (d) 프로세스 적재: 처음에는 메모리가 비어있어 프로세스 A, B, C가 순서대로 들어오면서 물리적으로 연속된 빈 프레임에 차례대로 할당.
  • (e) 프로세스 B 스왑 아웃 (Swap-out): 메모리 공간이 더 필요해지자, 운영체제는 중간에 있던 프로세스 B를 메모리에서 내보냄. 그 결과 프레임 4, 5, 6이 다시 사용 가능한 빈 공간이 됨.
  • (f) 프로세스 D 적재: 이제 5개의 페이지로 이루어진 프로세스 D가 들어올 차례.
    • 운영체제는 비어있는 프레임들을 찾아 D의 페이지들을 배치.
    • D의 처음 세 페이지(D.0, D.1, D.2)는 B가 비워준 4, 5, 6번 프레임에 들어감.
    • 나머지 두 페이지(D.3, D.4)는 메모리 뒷부분에 있던 다른 빈 프레임인 11, 12번에 배치.

이처럼 프로세스 D의 페이지들이 메모리 곳곳에 흩어져서(불연속적으로) 저장되는 것이 페이징의 가장 중요한 특징. 이 덕분에 외부 단편화가 발생하지 않는다.

운영체제는 이처럼 흩어져 있는 페이지들의 위치를 페이지 테이블을 통해 정확히 기억하고 있다.

주소 변환 과정

1단계: 논리 주소 분할

CPU가 16비트 논리 주소 0000010111011110를 생성하면, MMU는 이것을 두 부분으로 나눈다.

  • 페이지 번호 (p): 주소의 앞부분 6비트인 000001. 이 주소가 '어떤 페이지'에 속하는지를 나타낸다.
  • 오프셋 (d): 주소의 뒷부분 10비트인 0111011110. 해당 페이지 내에서 '얼마나 떨어져 있는지'를 나타내는 상대적인 위치이다.

2단계: 페이지 테이블 조회

MMU는 1단계에서 얻은 페이지 번호(p=1)를 인덱스(index)로 사용하여, 이 프로세스 전용 페이지 테이블의 1번 항목을 조회한다.

  • 페이지 테이블의 1번 항목에는 000110이라는 값이 저장되어 있다.
  • 이 값은 해당 논리 페이지가 물리 메모리의 6번 프레임에 저장되어 있다는 의미. 이 값을 프레임 번호(f)라고 함.

3단계: 물리 주소 생성

이제 MMU는 2단계에서 찾은 프레임 번호(f)와 1단계의 오프셋(d)을 합쳐 최종 물리 주소를 만듦.

  • 물리 주소 = 프레임 번호 (f) + 오프셋 (d)
  • 000110 (프레임 번호) + 0111011110 (오프셋) = 0001100111011110

5-2. 세그먼테이션 (Segmentation)

프로그램을 논리적인 단위(Main, Stack, Function 등)인 세그먼트(Segment)로 나누어 관리한다. 크기가 가변적이다.

세그먼테이션은 이처럼 논리적인 코드와 데이터 단위를 그대로 하나의 세그먼트로 정의하여 관리한다.

운영체제는 각 세그먼트의 정보를 세그먼트 테이블(Segment Table)에 저장하여 관리한다.

  • limit: 해당 세그먼트의 크기를 나타냄 (메모리 보호용)
  • base: 해당 세그먼트가 물리 메모리에 저장된 시작 주소를 나타냄 (주소 변환용)

세그먼테이션에서의 논리 주소는 <세그먼트 번호, 오프셋>으로 구성된다. 예를 들어, 논리 주소 <2, 100> (2번 세그먼트의 시작으로부터 100만큼 떨어진 위치)은 다음과 같이 변환된다.

  1. 세그먼트 테이블에서 2번 항목을 찾는다 (base=4300, limit=400)
  2. 보호 검사: 오프셋(100)이 limit(400)보다 작은지 확인 (100 < 400 이므로 유효)
  3. 물리 주소 계산: 물리 주소 = base + 오프셋 = 4300 + 100 = 4400

특징

  • 보호와 공유 용이: '코드'나 '데이터' 같은 의미 단위로 쪼개져 있어 권한 관리가 쉽다.
  • 외부 단편화 발생: 가변 크기이므로 동적 분할처럼 틈새 공간이 생길 수 있다.

주소 변환

1단계: 논리 주소 분할

MMU는 CPU가 생성한 16비트 논리 주소를 <세그먼트 번호(s), 오프셋(d)>으로 나눈다.

  • 세그먼트 번호 (s): 앞의 4비트 0001. '어떤 세그먼트'인지를 나타낸다.
  • 오프셋 (d): 뒤의 12비트 011111110000. 해당 세그먼트 시작점으로부터 '얼마나 떨어져 있는지'를 나타낸다.

2단계: 세그먼트 테이블 조회

MMU는 세그먼트 번호(s=1)를 인덱스로 사용하여 세그먼트 테이블의 1번 항목을 조회. 해당 항목에는 두 가지 정보가 있다.

  • 길이(limit): 세그먼트의 크기. 보호 검사에 사용됨 (오프셋이 길이보다 크면 메모리 접근 오류).
  • 시작 주소(base): 세그먼트의 물리 메모리 시작 주소.

3단계: 물리 주소 계산

MMU는 테이블에서 읽어온 시작 주소(base)와 논리 주소의 오프셋(d)을 더하여 최종 물리 주소를 계산한다.

물리 주소 = 시작 주소 (base) + 오프셋 (d)

이처럼 세그먼테이션은 논리적 단위에 기반한 유연한 메모리 보호 및 공유를 가능하게 하지만, 외부 단편화라는 문제점을 안고 있다.

 

출처

  • William Stallings, 운영체제 내부구조 및 설계원리

'Operating System' 카테고리의 다른 글

[운영체제 OS] Scheduling  (1) 2026.02.01
[운영체제 OS] Virtual Memory  (0) 2026.02.01
[운영체제 OS] Deadlock  (1) 2026.02.01
[운영체제 OS] Concurrency  (0) 2026.02.01
[운영체제 OS] Thread  (0) 2026.02.01