세그멘테이션 (Segmentation)
핵심 인사이트 (3줄 요약)
- 본질: 세그멘테이션(Segmentation)은 프로그램을 기계가 편한 4KB의 고정 크기(페이징)로 무식하게 자르지 않고, 코드(Code), 데이터(Data), 힙(Heap), 스택(Stack), 함수 등 '인간이 이해할 수 있는 논리적 의미 단위(가변 크기)'로 예쁘게 잘라서 메모리에 비연속적으로 흩뿌리는 기법이다.
- 가치: 찢어진 덩어리 자체가 하나의 완결된 의미(예: 배열 전체, 메인 함수 통째로)를 가지기 때문에, 읽기 전용(Read-Only) 코드를 타 프로세스와 100% 안전하게 공유(Sharing)하거나, 실행 방지(NX) 등의 보안 권한(Protection)을 부여하기가 페이징보다 압도적으로 유리하고 직관적이다.
- 융합: 하지만 잘라진 덩어리의 크기가 수 KB에서 수십 MB까지 제각각이므로 메모리 반환 시 필연적으로 **외부 단편화(External Fragmentation)**라는 불치병을 부활시켰고, 이로 인해 현대 OS 커널에서는 단독으로 쓰이지 않고 페이징 아키텍처 밑단에 융합(Paged Segmentation)되어 보안 권한을 담당하는 조연으로 물러났다.
Ⅰ. 개요 및 필요성 (Context & Necessity)
-
개념: 세그멘테이션은 사용자(개발자)의 관점을 그대로 메모리 분할에 적용한 비연속 할당 아키텍처다. 논리 주소는
<세그먼트 번호(s), 오프셋(d)>의 2차원 투플로 구성되며, 각 세그먼트(조각)는 자신만의 독립적인 크기(Limit)와 물리 메모리 상의 시작 주소(Base)를 가진다. -
필요성: 페이징은 메모리 낭비(외부 단편화)를 없애는 기계적 효율의 끝판왕이었지만, 프로그램의 뇌(코드)와 팔다리(데이터)를 무자비하게 4KB 전기톱으로 썰어버렸다. 이 때문에 1개의 배열 데이터가 3개의 페이지에 걸쳐 찢어지는 끔찍한 사태가 발생했다. 만약 이 배열에 쓰기 금지(Read Only) 락을 걸고 싶다면 3개의 페이지 테이블에 일일이 락을 걸어야 했고, 자칫 다른 데이터가 섞여 들어간 페이지면 오류가 났다. "아니, 그냥 배열 하나(세그먼트)를 통째로 잘라서 거기에 락 하나만 깔끔하게 걸면 안 돼?"라는 개발자들의 아우성이 세그멘테이션을 탄생시켰다.
-
💡 비유: 페이징이 소고기를 부위 상관없이 무조건 400g씩 깍둑썰기해서 담는 기계식 정육점이라면, 세그멘테이션은 갈비살은 갈비살대로, 등심은 등심대로 부위의 모양과 크기에 맞춰 의미 있게 도려내어 정갈하게 담아주는 고급 정육점 셰프와 같다.
-
등장 배경 및 아키텍처 갈림길:
- 연속 할당의 붕괴: 통째로 올리는 건 외부 단편화 때문에 불가능하다. 비연속으로 찢긴 찢어야 한다.
- 어떻게 자를 것인가의 이념 대립:
- 페이징 진영: 무조건 똑같이 4KB로 자르자! 관리(단편화 0%)가 최우선이다! (기계 중심)
- 세그먼트 진영: 찢더라도 코드/데이터 등 의미 단위로 찢자! 보안/공유/관리가 편해야 한다! (인간 중심)
┌──────────────────────────────────────────────────────────────────────┐
│ 페이징과 세그멘테이션의 '프로그램 찢기' 철학 비교 │
├──────────────────────────────────────────────────────────────────────┤
│ │
│ [ 원본 프로그램 A ] │
│ ┌───────────────┐ ┌────────┐ ┌────────┐ │
│ │ Main 함수 (7KB)│ │ 전역 변수│ │ Stack │ │
│ └───────────────┘ └────────┘ └────────┘ │
│ │
│ ▶ 페이징(Paging)의 도끼질: (의미 무시, 4KB 고정 절단) │
│ [Main 앞 4KB] [Main 뒤 3KB + 전역변수 1KB 섞임!] [나머지] │
│ ⚠ 문제: 가운데 조각(페이지)에 '읽기 전용' 락을 걸면, 섞여 들어간 │
│ 전역 변수(데이터)마저 글을 못 쓰게 되어 프로그램이 터짐! │
│ │
│ ▶ 세그멘테이션(Segmentation)의 정밀한 칼질: (가변 크기 유지) │
│ [Main 세그먼트 (7KB 통째로)] [전역변수 세그먼트] [Stack 세그먼트] │
│ ✅ 장점: Main 세그먼트 전체에 깔끔하게 '실행/읽기' 락 하나만 걸면 끝!│
│ ⚠ 치명적 단점: 잘라낸 조각 크기(7K, 2K)가 제각각이라 램에 넣을 때 │
│ 다시 끔찍한 외부 단편화가 발생함. │
└──────────────────────────────────────────────────────────────────────┘
[다이어그램 해설] 세그멘테이션은 컴파일러가 만들어낸 심볼(기호) 테이블을 그대로 하드웨어 메모리 매핑에 투영한다. 개발자가 int arr[100]을 선언하면 이게 그냥 하나의 세그먼트가 된다. 프로그램의 구조와 메모리의 물리적 보호 단위가 완벽하게 1:1로 일치하므로 디버깅, 공유, 샌드박싱 등 보안 측면에서 예술적인 우아함을 자랑한다.
- 📢 섹션 요약 비유: 이삿짐을 쌀 때, 그릇과 옷을 무조건 5kg짜리 박스(페이징)에 무게만 맞춰 섞어 담으면 트럭에 싣기는 편하지만 새집에서 물건 찾다 화가 납니다. 반면, 그릇은 그릇 통에, 옷은 옷통(세그멘테이션)에 종류별로 담으면 정리는 완벽하지만 트럭에 싣을 때 박스 크기가 다 달라 빈 공간(외부 단편화)이 생기는 것과 같습니다.
Ⅱ. 아키텍처 및 핵심 원리 (Deep Dive)
하드웨어 주소 번역 아키텍처 (STBR & STLR)
세그멘테이션도 비연속 할당이므로, CPU 주소를 물리 주소로 번역해 줄 세그먼트 테이블(Segment Table) 장부가 필요하다.
CPU는 <세그먼트 번호(s), 오프셋(d)>을 내뿜는다.
┌──────────────────────────────────────────────────────────────────────┐
│ 세그멘테이션의 논리 -> 물리 주소 번역 회로 │
├──────────────────────────────────────────────────────────────────────┤
│ │
│ [ CPU 요청 ] 논리 주소 < s(세그먼트 2), d(오프셋 500) > │
│ │ │
│ ▼ 1단계: 세그먼트 테이블(장부) 조회 │
│ ┌──────┬────────────────┬─────────────────┐ │
│ │ Seg │ Limit (크기) │ Base (물리 시작점)│ │
│ ├──────┼────────────────┼─────────────────┤ │
│ │ 0 │ 1000 │ 1400 │ │
│ │ 1 │ 400 │ 6300 │ │
│ │ 2 │ 600 │ 4300 │ ◀─ 매핑 정보 획득! │
│ └──────┴────────────────┴─────────────────┘ │
│ │ │
│ ▼ 2단계: 크기 방어선 (Limit Check) │
│ ┌───────────────────────────┐ │
│ │ 요청한 오프셋(d=500)이 Limit(600) 보다 작은가? │
│ └─────────┬─────────────────┘ │
│ │ (Yes! 통과) ※ 만약 700을 불렀다면 즉시 SegFault 트랩! │
│ ▼ │
│ ▼ 3단계: 물리 주소 덧셈 연산 │
│ ┌───────────────────────────┐ │
│ │ Base(4300) + d(500) = 물리 주소 4800 도출 완료! │
│ └───────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────┘
[다이어그램 해설] 페이징 번역과 비슷해 보이지만 결정적인 차이가 두 군데 있다.
- Limit 검사의 부활: 페이징은 프레임 크기(4KB)가 고정이므로 오프셋이 무조건 4KB 이하라는 게 수학적으로 보장되지만, 세그먼트는 조각 크기가 600바이트일 수도 1MB일 수도 있다. 따라서 장부에 적힌 고유의 Limit(크기) 값을 반드시 비교하는 하드웨어 회로가 동반된다. (이게 어긋날 때 나는 에러가 프로그래머의 주적 Segmentation Fault 다.)
- 단순 덧셈(+ 오프셋): 페이징은 조각 크기가 일정해 그냥 비트 결합(Bypass)을 하면 되지만, 세그먼트는 램 빈 곳 아무 데나(Base 주소가 제각각) 박혀있으므로 반드시 거대한 가산기(Adder) 회로를 통해 Base와 Offset을 덧셈 연산해야 물리 주소가 나온다.
세그멘테이션의 외부 단편화 재림
세그멘테이션의 눈부신 우아함은 결국 물리 메모리 할당(Allocation) 단계에서 처참히 박살 났다.
-
메모리에 10MB(메인함수), 5MB(데이터), 20MB(스택)짜리 세그먼트들이 막 들어갔다 나갔다 한다.
-
어? 크기가 제각각인 조각들이 이빨 빠진 듯 나갔다 들어왔다 한다?
-
맞다. 연속 메모리 할당 시절(가변 분할 방식) 시스템을 마비시켰던 '외부 단편화(External Fragmentation)' 지옥이 비연속 할당인 세그멘테이션에서 다시 100% 똑같이 부활해버렸다.
-
남은 램 총합은 30MB인데 빈 구멍이 10MB씩 3군데 쪼개져 있으면 20MB짜리 세그먼트를 적재할 수 없어 에러가 난다. 이를 합치려면 시스템을 멈추고 **압축(Compaction)**을 또 해야 한다. 진화의 퇴보다.
-
📢 섹션 요약 비유: 박스 크기가 다 다른(가변) 화물들을 테트리스 하듯 창고에 빈틈없이 쌓으려 하니 결국 중간중간 쓸모없는 빈 공간(외부 단편화)이 생겨나서, 다시 지게차(압축)를 부를 수밖에 없는 운명에 처한 것입니다.
Ⅲ. 융합 비교 및 다각도 분석
비교 1: 페이징(Paging) vs 세그멘테이션(Segmentation) 최종 요약
이 두 비연속 할당 기법의 대결은 컴퓨터 아키텍처 역사상 가장 유명한 트레이드오프다.
| 관점 | 페이징 (Paging) | 세그멘테이션 (Segmentation) |
|---|---|---|
| 설계 철학 | 물리적, 기계적, 수학적 편의성 | 논리적, 인간적, 의미론적 직관성 |
| 조각 크기 | 무조건 고정 크기 (예: 4KB) | 무작위 가변 크기 (1KB ~ 수십 MB) |
| 단편화 발생 | 내부 단편화 (무시할 수준) | 외부 단편화 (치명적 병목, 압축 필요) |
| 공유 (Sharing) | 의미 없는 조각들이라 섞이면 공유 까다로움 | 코드/데이터가 완벽히 분리되어 공유 압도적 유리 |
| 보안 (Protection) | 조각 안에 이물질이 섞여 락 걸기 까다로움 | 세그먼트 하나에 깔끔하게 R/W/X 비트 제어 완벽 |
| 주소 변환 회로 | Limit 검사 불필요, 오프셋 덧셈 불필요 (초고속) | Limit 비교 연산 및 오프셋 덧셈 회로 필수 (무거움) |
Paged Segmentation (페이징 기반 세그멘테이션) 의 융합
70년대 인텔(Intel x86) 엔지니어들은 고민했다. "페이징의 외부 단편화 0% 마법과, 세그멘테이션의 완벽한 보안/공유 마법을 둘 다 훔칠 순 없을까?" 이 탐욕이 낳은 돌연변이 아키텍처가 **세그먼트를 다시 페이지로 찢어버리는 '페이징 기반 세그멘테이션'**이다.
- 개발자의
Main 함수(7MB)를 하나의 세그먼트로 논리적으로 잘라 세그먼트 테이블에 등록하여 R/X 보안 락을 깐깐하게 건다. (세그멘테이션의 장점 흡수) - 근데 이 7MB 덩어리를 램에 그대로 꽂으면 외부 단편화가 터지니까, 이 세그먼트의 배를 갈라 다시 무식하게 4KB 단위의 페이지로 수천 개 찢어버린다.
- 그리고 램 안의 빈 프레임 아무 데나 흩뿌린 뒤, 이 찢어진 조각들을 관리하는 페이지 테이블을 세그먼트별로 하나씩 달아준다. (페이징의 장점 흡수)
- 결과: 주소 변환을 위해 세그먼트 테이블을 1번 읽고 -> 페이지 테이블을 또 1번 읽어야 하는 지옥의 오버헤드가 발생하지만, 공유/보안 완벽 + 외부 단편화 제로라는 궁극의 메모리 관리가 탄생했다. MULTICS와 Intel 80386이 채택한 방식이다.
- 📢 섹션 요약 비유: 고급 셰프가 소고기를 부위별로 의미 있게 해체(세그멘테이션)하여 꼬리표를 붙인 다음, 포장할 때는 빈 공간 없이 꽉꽉 채우기 위해 그 부위를 다시 다짐육(페이징)으로 갈아서 규격 박스에 담아버리는 극한의 효율화 전술입니다.
Ⅳ. 실무 적용 및 기술사적 판단 (Strategy & Decision)
실무 시나리오: 현대 리눅스(Linux)의 세그멘테이션 폐기 선언
이론적으로 아름다웠던 '페이징 기반 세그멘테이션'은 결국 현대 서버 시장(Linux 커널)에서 완전히 용도 폐기(Bypass) 되었다.
- 문제 봉착: x86 칩셋의 하드웨어는 세그멘테이션을 강제로 거치도록 회로가 납땜 되어 있었다. 리눅스는 인텔 칩 위에서 돌기 위해 어쩔 수 없이 이 회로를 거쳐야 했다.
- 리눅스의 우회 해킹 (Flat Memory Model):
- 리눅스의 창시자 리누스 토발즈와 엔지니어들은 복잡한 세그먼트 장부를 관리하는 것을 극도로 혐오했다. 다른 RISC(ARM 등) CPU들은 아예 세그멘테이션 하드웨어가 없어서 이식성(Portability)이 떨어졌기 때문이다.
- 그래서 리눅스는 커널 코딩 시, x86의 필수 세그먼트 장부(GDT/LDT)에 모든 세그먼트의 크기(Limit)를 4GB(전체 메모리)로 똑같이 맞춰버리고, 시작 주소(Base)를 모두 0번지로 덮어씌워 버렸다.
- 이렇게 되면 세그멘테이션 번역을 거쳐도
가상 주소 + 0 = 가상 주소가 되어, 세그멘테이션 회로가 사실상 무력화(투명인간)된다. (이를 Flat Memory Model 이라 함)
- 현재의 표준: 결국 오늘날 여러분의 PC, 스마트폰, 클라우드 서버는 100% '페이징(Paging)' 단독 구조로만 램을 관리하며, 세그멘테이션은 x86 칩 구석의 과거 유산(Legacy)으로 숨만 쉬고 있다.
프로그래머의 단어: Segmentation Fault (SegFault)
리눅스 커널이 세그멘테이션 기능을 껐음에도 불구하고, C언어에서 포인터를 잘못 찌르면 에러 메시지로 "Page Fault"가 아니라 여전히 **"Segmentation fault (core dumped)"**가 뜬다. 이는 초창기 유닉스 시절 한계 레지스터(Limit)를 뚫고 불법 메모리에 접근했을 때 뱉던 에러의 이름이 수십 년간 관습적으로 고착화된 언어적 화석이다.
- 📢 섹션 요약 비유: 톨게이트(세그멘테이션 하드웨어)가 길막을 해서 짜증 났던 버스 기사(리눅스 커널)가, 톨게이트를 철거할 순 없으니 모든 차량에 '하이패스 프리패스 무정차 단말기(Flat Model)'를 강제로 달아줘서 톨게이트의 존재 자체를 무의미하게 만들어버린 유쾌한 반항입니다.
Ⅴ. 기대효과 및 결론 (Future & Standard)
정량/정성 기대효과
| 구분 | 내용 |
|---|---|
| 프로그램 모듈화 매핑 | 코드를 기계 단위가 아닌 심볼(함수, 배열) 단위로 독립적 스와핑/재배치하게 하여 프로그래머 친화적 환경 제공 |
| 메모리 보안/공유 직관성 | 읽기 전용 코드(Text) 영역과 쓰기 가능(Data) 영역의 락(Lock)을 섞임 없이 완벽하게 분리 제어 |
| 세그먼트 폴트의 기원 | 할당되지 않은 허공을 찌르는 불법 메모리 접근을 즉각 방어하는 현대 디버깅/보안 트랩의 시발점 |
결론 및 미래 전망
세그멘테이션 (Segmentation)은 인간의 의미(Semantic)를 물리적 하드웨어 램의 구조에 직관적으로 투영하려 했던 눈물겨운 낭만주의 아키텍처다. 배열은 배열답게, 함수는 함수답게 통째로 메모리에 담고 싶었지만, '외부 단편화'라는 무자비한 기계적 현실의 벽에 부딪혀 결국 4KB라는 획일적 컨베이어 벨트(페이징)에 왕좌를 내어주고 말았다. 현대 범용 OS에서는 페이징의 보조 수단으로 전락하거나 아예 비활성화(Flat Model)되어 자취를 감추었지만, 그 철학이 남긴 '논리적 단위의 보안 통제(R/W/X)'와 '동적 라이브러리 독립적 공유'의 개념은 오늘날 소프트웨어 공학의 기둥으로 영원히 살아남았다.
- 📢 섹션 요약 비유: 사람 몸을 스캔할 때 뼈, 근육, 내장이라는 의미 단위로 나눠서 정밀 분석(세그멘테이션)하려 했으나 계산이 너무 복잡해져서, 그냥 몸 전체를 무식하게 4mm짜리 픽셀 모자이크(페이징 MRI)로 쪼개어 기계가 분석하게 놔둔 컴퓨터 공학 발전의 역설입니다.
📌 관련 개념 맵 (Knowledge Graph)
- 페이징 (Paging) | 세그멘테이션을 죽이고 최후의 승자가 된, 인간의 의미를 무시하고 메모리를 4KB로 무식하게 썰어버리는 대세 기법
- 외부 단편화 (External Fragmentation) | 조각 크기가 가변적인 세그멘테이션이 물리 램에 올려질 때 피할 수 없이 발생하는 치명적인 빈 공간 조각들
- Paged Segmentation | 외부 단편화를 없애기 위해, 세그먼트로 우아하게 자른 뒤 그 배를 갈라 다시 4KB 페이지로 난도질하는 융합 아키텍처
- Flat Memory Model (플랫 모델) | 리눅스 커널이 세그멘테이션의 복잡함을 피하고자 모든 세그먼트 한계를 전체 메모리로 덮어씌워버린 꼼수
- 세그먼트 테이블 (Segment Table) | 세그먼트 번호를 물리 주소로 맵핑하며, 보안 비트와 함께 반드시 크기(Limit) 값을 가지고 있는 번역 장부
👶 어린이를 위한 3줄 비유 설명
- 세그멘테이션이 무엇인가요? 사과, 바나나, 수박을 도시락에 쌀 때, 크기가 다 다름에도 불구하고(가변 크기) 억지로 과일 모양 그대로 과일통에 따로따로 예쁘게 담는 방법이에요.
- 무엇이 좋은가요? "이 통은 바나나 전용이니까 섞지 마!" 하고 딱 의미를 정해서(보안, 공유) 관리하기가 너무 편해요.
- 그런데 왜 요새 안 쓰나요? 과일 모양 그대로 담으려다 보니 가방 안에 못 쓰는 빈틈(외부 단편화)이 너무 많이 생겨서, 요새는 그냥 다 똑같은 크기의 깍두기 모양(페이징)으로 다져서 꽉꽉 채워 넣는 방식을 더 좋아한답니다.