CS 로드맵 7편 — OS 아키텍처 입문: Unix, NT, XNU의 갈림길
- CS 로드맵 (0) — AI 시대, CS 지식은 왜 더 중요해졌는가
- CS 로드맵 (1) — 배열과 연결 리스트: 메모리의 지형을 읽다
- CS 로드맵 (2) — 스택, 큐, 덱: 제한이 만드는 강력한 추상화
- CS 로드맵 (3) — 해시 테이블: O(1) 조회의 조건과 한계
- CS 로드맵 (4) — 트리: 순서와 균형, O(log n)의 보장
- CS 로드맵 (5) — 그래프: 관계의 네트워크, 경로의 과학
- CS 로드맵 (6) — 메모리 관리: 스택과 힙, GC, 그리고 프레임을 잡아먹는 것들
- CS 로드맵 (외전) — 힙과 우선순위 큐: 부분 순서의 경제학
- CS 로드맵 7편 — OS 아키텍처 입문: Unix, NT, XNU의 갈림길
- CS 로드맵 8편 — 프로세스와 스레드: OS는 실행 단위를 어떻게 추상화하는가
- CS 로드맵 9편 — 스케줄링: OS는 누구에게 CPU를 줄까
- 세 운영체제의 차이는 "기술 선택"이 아니라 "역사적 경로 의존성"이다 — Unix, VMS, NeXTSTEP이라는 1970~80년대의 결정이 오늘의 Linux, Windows, macOS를 만들었다
- 커널 구조가 다르다 (Linux 모놀리식, Windows NT 하이브리드, macOS XNU는 Mach microkernel 위에 BSD를 얹은 이중 구조)
- macOS는 Grand Central Dispatch로 스레드 추상화를, Apple Silicon으로 P/E 이질 코어와 16KB 페이지를, Rosetta 2로 하드웨어 TSO 모드를 도입했다
- 실행 바이너리 포맷(ELF/PE/Mach-O)부터 다르기 때문에 게임 멀티플랫폼 빌드에서 크로스 컴파일이 복잡해진다
서론: 왜 OS부터 시작하는가
Stage 1에서는 자료구조와 메모리를 다뤘습니다. 배열과 연결 리스트, 해시 테이블, 트리와 그래프, 그리고 힙까지 — 모두 “데이터를 어떻게 정리할 것인가”의 이야기였습니다.
Stage 2의 질문은 조금 다릅니다.
“스레드 두 개가 같은 변수를 쓰면 왜 프로그램이 때때로만 죽는가?”
이 질문에 답하려면 프로그램이 어떻게 실행되는지, 누가 CPU를 나눠주는지, 메모리가 어떻게 보호되는지를 알아야 합니다. 그것이 운영체제(OS)의 역할입니다.
그런데 OS 공부를 시작하면 바로 이상한 벽에 부딪힙니다. 교과서에는 “프로세스는 PCB를 가진다” 같은 추상적인 설명이 나옵니다. 하지만 실제로 macOS에서 ps 명령어를 쳐보고, Windows에서 작업 관리자를 열어보면, 세 OS의 세계가 전혀 다르게 보입니다.
- Linux에서는 프로세스 생성이
fork()두 글자로 끝납니다 - Windows에서는
CreateProcess()에 12개의 매개변수가 들어갑니다 - macOS에서는 같은
fork()를 써도, 그 아래에는 Mach 커널이라는 완전히 다른 것이 있습니다
이 세 OS의 차이는 기술적 선택이 아니라 역사의 산물입니다. 1969년 Unix의 탄생, 1977년 Berkeley의 분기, 1989년 NeXTSTEP의 도박, 1993년 Windows NT의 설계 — 이 결정들이 오늘 우리가 Unity 게임을 빌드할 때 .exe가 나오는지 .app이 나오는지를 결정합니다.
Stage 2 첫 편은 본격적인 이론으로 들어가기 전에 지도를 그리는 작업입니다. 각 OS가 어떤 혈통에서 왔고, 왜 서로 다른 모습이 되었고, 게임 개발자에게 어떤 차이가 있는지를 훑습니다. 다음 편부터는 프로세스, 스레드, 스케줄링 같은 구체적인 주제로 파고들지만, 그때마다 “이 개념은 Linux에서 A고 Windows에서 B다”를 비교할 수 있으려면, 먼저 세 OS의 뼈대를 알아야 합니다.
특히 맥 유저이신 독자를 위해 macOS 특화 섹션을 자세히 다룹니다. XNU 커널의 독특한 이중 구조, Grand Central Dispatch의 설계 사상, Apple Silicon의 하드웨어 기믹까지 — 다른 OS 책에서는 주변부로 밀리는 이야기들이 여기서는 주인공입니다.
Part 1: 세 OS의 혈통 — 1969년의 결정이 2026년을 만들다
Unix의 탄생 (1969)
Ken Thompson (왼쪽)과 Dennis Ritchie (오른쪽), 1973년. Unix와 C 언어의 창시자들. 출처: Jargon File (Public Domain)
모든 이야기는 1969년 미국 뉴저지의 AT&T Bell Labs에서 시작합니다. Ken Thompson과 Dennis Ritchie는 GE-645라는 거대한 메인프레임에서 Multics라는 복잡한 OS 프로젝트에 참여하다 좌절을 겪습니다. Multics는 너무 야심 차고, 너무 느리고, 너무 복잡했습니다.
Thompson은 Bell Labs 구석에 방치된 PDP-7이라는 작은 컴퓨터에서 Multics의 불필요한 복잡성을 덜어낸 단순한 OS를 취미로 만들기 시작합니다. 이름은 “Multi-“ 대신 “Uni-“를 붙여 UNICS (Uniplexed Information and Computing Service). 나중에 이름이 Unix로 정착합니다.
Unix의 설계 원칙은 후대에 “Unix 철학”으로 불리게 됩니다:
- 한 가지 일만 잘해라 (Do one thing and do it well)
- 모든 것은 파일이다 (Everything is a file)
- 프로그램을 조합하라 (Pipe 기호
|로 출력을 다음 입력으로) - 텍스트가 범용 인터페이스다
1973년, Ritchie는 Unix를 C 언어로 다시 씁니다. 이것이 결정적이었습니다. 그 전까지 OS는 어셈블리어로만 작성됐기 때문에 다른 하드웨어로 이식할 수 없었는데, C로 작성된 Unix는 이식 가능한 OS의 시대를 열었습니다.
1970년대 후반, Unix의 소스 코드는 AT&T가 저렴한 라이선스로 대학에 배포합니다. 특히 UC Berkeley가 열정적으로 받아들였고, 학생들은 자기네가 Unix를 고쳐서 배포하기 시작합니다. 이것이 분기점입니다.
BSD 갈래: Berkeley의 학생들
1977년부터 Berkeley에서 배포한 Unix 파생판을 Berkeley Software Distribution (BSD)라 부릅니다. BSD는 기존 Unix에 없던 많은 기능을 추가했습니다:
- TCP/IP 네트워킹 스택 (1983, 인터넷의 기초)
- Berkeley Sockets API (지금도 네트워크 프로그래밍의 표준)
- 가상 메모리 개선
- Fast File System (FFS)
1980년대 중반까지 BSD는 Unix의 사실상 표준 중 하나로 자리잡습니다. 하지만 AT&T가 라이선스 소송을 걸면서 Berkeley는 오랜 법정 다툼에 시달렸고, 그 결과 AT&T 코드를 완전히 제거한 자유로운 BSD가 만들어집니다. 이것이 FreeBSD, NetBSD, OpenBSD의 뿌리입니다.
중요한 점: BSD는 완전히 오픈소스이고, 라이선스가 GPL(Linux)보다 훨씬 자유롭습니다. 이 때문에 나중에 Apple이 macOS의 기반으로 BSD를 선택하게 됩니다. GPL이라면 Apple이 자신의 수정 내용을 모두 공개해야 했을 테지만, BSD 라이선스는 그런 의무가 없었기 때문입니다.
NeXTSTEP → macOS: Steve Jobs의 귀환
NeXTcube (1990), Computer History Museum 소장. 이 컴퓨터에 실린 NeXTSTEP이 오늘의 macOS의 뿌리다. 사진: Michael Hicks, CC BY 2.0
1985년, Apple에서 쫓겨난 Steve Jobs는 NeXT라는 회사를 차립니다. NeXT의 목표는 “대학과 연구자를 위한 고급 워크스테이션”이었습니다. 그 컴퓨터에 실릴 OS가 NeXTSTEP (1989)입니다.
NeXTSTEP의 설계는 독특했습니다:
- 커널은 Mach microkernel (Carnegie Mellon University에서 개발)
- 그 위에 BSD Unix layer를 얹어 POSIX 호환성 제공
- 응용 프로그램 프레임워크는 Objective-C로 작성된 Cocoa (당시 이름은 AppKit)
당시 이 구조는 학계에서 유행하던 “microkernel이 미래다”라는 사상의 실천이었습니다. 하지만 NeXT 컴퓨터는 상업적으로 실패했고, 회사는 살아남기 위해 하드웨어를 포기하고 NeXTSTEP을 다른 하드웨어에 이식하는 방향으로 전환합니다 (1993~).
1996년, 놀라운 일이 일어납니다. Apple이 NeXT를 인수합니다. 당시 Apple은 Mac OS 9의 후계가 될 차세대 OS를 만들려던 “Copland” 프로젝트가 실패하면서 기반 기술이 없었습니다. Apple은 외부에서 OS를 사오기로 하고, BeOS와 NeXTSTEP을 놓고 고민하다 NeXTSTEP을 선택합니다. 금액은 약 4억 달러.
Steve Jobs는 NeXT와 함께 Apple로 돌아왔고, 1997년 임시 CEO로 복귀합니다. 그리고 NeXTSTEP이 macOS의 기반이 되었습니다.
- 1999: Mac OS X Server 1.0 (NeXTSTEP 기반)
- 2001: Mac OS X 10.0 Cheetah — 일반 사용자용
- 2007: iPhone OS (Mac OS X의 축소판)
- 2016: “Mac OS X”에서 “macOS“로 이름 변경
즉, 오늘 여러분의 MacBook에서 돌아가는 macOS의 커널은 1980년대 NeXT가 1990년대 Apple에 팔았고, 그 뿌리는 Carnegie Mellon University의 Mach 연구 프로젝트로 거슬러 올라갑니다. 30년 이상 된 설계가 아직 살아 있습니다.
Linux: 핀란드 대학생의 취미 프로젝트 (1991)
Linus Torvalds, LinuxCon Europe 2014. 23년 전 취미 프로젝트가 세계 인프라의 기반이 된 것을 회고 중. 사진: Krd, CC BY-SA 4.0
1991년, 핀란드 헬싱키 대학의 Linus Torvalds는 학교에서 OS 수업을 들으며 Andrew Tanenbaum이 교육용으로 만든 Minix를 사용하고 있었습니다. Minix는 훌륭한 교육용 OS였지만, 상업 라이선스로 사용이 제한됐고 Linus는 자기 집 386 PC에서 더 자유롭게 쓸 수 있는 것이 필요했습니다.
그래서 취미로 OS를 만들기 시작합니다. 8월 25일, comp.os.minix 뉴스그룹에 올린 메시지가 유명합니다:
“Hello everybody out there using minix — I’m doing a (free) operating system (just a hobby, won’t be big and professional like gnu)…”
“크지도, 전문적이지도 않을 것”이라던 그 취미 프로젝트가 30년 뒤 세계의 절대다수 서버, 스마트폰, 슈퍼컴퓨터에서 돌아가고 있습니다.
Linux는 처음부터 GPL 라이선스를 채택했고, 전 세계 개발자들이 기여할 수 있는 모델을 구축했습니다. 그리고 GNU 프로젝트의 유저랜드 도구(gcc, bash, coreutils 등)와 결합해 완전한 OS가 되었습니다 — 그래서 엄밀히는 GNU/Linux라고 부릅니다.
Linux의 결정적 특징 — 커널 구조 면에서:
- 모놀리식 커널: Unix 전통을 따라 커널에 모든 기능(파일 시스템, 네트워크, 드라이버, 메모리 관리)을 모아둠
- Tanenbaum이 “microkernel이 우월하다”고 비판하자 Linus가 맞받아친 1992년의 논쟁은 OS 역사에서 유명합니다
- 30년이 지난 지금 Linux는 부분적으로 모듈화된 모놀리식 커널로 진화했습니다 (커널 모듈 기능)
VMS → Windows NT: Dave Cutler의 복수
지금까지의 이야기는 모두 Unix 계열입니다. 그런데 Windows는 Unix와 전혀 다른 혈통입니다.
1970년대, Digital Equipment Corporation (DEC)은 미니컴퓨터 시장의 강자였습니다. 그들의 OS는 VMS (Virtual Memory System)로, 대형 서버를 위한 고안정성 OS였습니다. VMS의 수석 설계자가 Dave Cutler였습니다.
1988년, DEC에서 새 프로젝트가 무산되자 Dave Cutler는 팀을 이끌고 Microsoft로 이적합니다. Microsoft의 Bill Gates가 “OS/2의 뒤를 이을 차세대 32비트 OS를 만들어 달라”고 제안했기 때문입니다.
Cutler는 Windows NT (NT = New Technology)를 설계합니다. 내부적으로 VMS의 많은 아이디어를 가져왔습니다 — 심지어 VMS의 각 문자를 한 칸씩 밀면 WNT가 된다는 농담이 있을 정도입니다 (V→W, M→N, S→T).
Windows NT의 핵심 특징:
- 하이브리드 커널: microkernel처럼 서브시스템을 나눴지만, 성능을 위해 많은 부분을 커널 공간에 두었습니다
- POSIX 서브시스템, OS/2 서브시스템, Win32 서브시스템이 분리 — 이론상 다른 OS API를 동시에 지원
- 유니코드 우선: 설계 단계부터 Unicode를 전제 (UTF-16)
- 멀티 아키텍처 지원: x86, MIPS, Alpha, PowerPC (초기에는)
1993년 Windows NT 3.1이 출시됐고, 이후 NT 4.0, Windows 2000, XP, 7, 10, 11까지 모두 같은 NT 커널 계보를 따릅니다. 즉, 여러분이 Windows 11에서 Unity를 빌드할 때 돌아가는 커널의 뿌리는 DEC VMS (1977)로 이어집니다.
한편 Windows 95, 98, ME는 완전히 다른 혈통이었습니다 — MS-DOS 기반의 Windows 1.0~3.1 계보. Microsoft는 2001년 Windows XP에서 이 두 계보를 NT 쪽으로 통합하며 DOS 계보를 끝냅니다.
혈통 트리
지금까지의 이야기를 시각화하면 다음과 같습니다.
Part 2: 세 OS의 설계 철학
혈통이 다르면 철학도 달라집니다. 같은 문제 — “메모리 부족 시 어떻게 처리할까” — 에 대해 세 OS가 다르게 대응하는 이유가 여기 있습니다.
Linux: 개방성과 성능
Linux의 문화는 “해킹 가능성“에 최고 가치를 둡니다.
- 모든 것이 공개: 커널 소스 전체가 GPL로 공개되어 누구나 읽고 수정할 수 있습니다
- 파일 시스템을 통한 제어:
/proc,/sys파일 시스템으로 커널 상태를 파일처럼 읽고 쓸 수 있습니다- 예:
cat /proc/meminfo로 메모리 상태 확인,echo 3 > /proc/sys/vm/drop_caches로 캐시 비우기
- 예:
- 텍스트 우선: 설정 파일은 거의 대부분 텍스트. 바이너리 설정 DB(레지스트리)가 없습니다
- 성능 우선: 호환성보다 성능. 예를 들어 ABI 호환성은 보장하지만 커널 내부 API는 언제든 바뀔 수 있습니다
- 다양성 수용: 배포판(Ubuntu, Arch, Fedora, Alpine…)마다 다른 철학을 허용
단점: 파편화. “Linux”라고 뭉뚱그려 말하지만 Ubuntu와 Alpine은 서로 다른 OS에 가까울 정도입니다. 또한 데스크톱 UX는 상대적으로 약합니다.
Windows: 하위 호환성의 극한
Microsoft의 문화는 “고객이 이미 돈을 낸 프로그램이 10년 뒤에도 돌아가야 한다“입니다.
- 하위 호환성이 거의 신성불가침: Windows 95용 프로그램이 Windows 11에서도 대부분 실행됩니다
- 유명한 일화: Windows에는 특정 유명 게임(SimCity)의 버그를 우회하는 코드가 커널에 들어 있습니다. 게임이 해제된 메모리를 읽는 버그가 있었는데, Windows 95에서 Win NT로 넘어가며 그 메모리가 즉시 회수되자 SimCity가 크래시했고, Microsoft는 “SimCity가 실행 중이면 메모리 해제를 지연시키는 코드”를 Windows에 추가했습니다 (Raymond Chen 블로그 기록)
- 강력한 바이너리 API: Win32 API는 30년 동안 사실상 그대로. COM, .NET 같은 상위 레이어도 하위 호환을 유지
- 레지스트리: 시스템 전역 설정 DB. 텍스트 파일 대신 구조화된 키-값 저장소
- GUI 우선: 커맨드라인보다 GUI가 먼저 설계됨. PowerShell이 뒤늦게 등장
- 엔터프라이즈 중심: Active Directory, Group Policy 등 대규모 조직 관리 기능이 매우 강력
단점: 하위 호환성을 위한 코드가 누적되면서 커널이 무거워지고 보안 표면이 넓어집니다. 30년 전 API의 버그가 2025년에도 사라지지 않는 이유입니다.
macOS: 통제된 경험과 하드웨어 통합
Apple의 문화는 “하드웨어와 소프트웨어를 함께 설계한다“입니다.
- 수직 통합: Apple은 CPU(Apple Silicon), OS(macOS), GUI(Aqua), 응용 프레임워크(Cocoa), 개발 도구(Xcode)를 모두 직접 만듭니다
- 단일 공식 경로: Linux처럼 다양한 배포판이 없고, Windows처럼 여러 서브시스템이 공존하지도 않습니다. 한 가지 공식 방식만 지원
- 급진적 전환 수용: Apple은 과감하게 구버전을 버립니다
- PowerPC → Intel (2006, Rosetta 1로 전환)
- 32비트 → 64비트 (2019 macOS Catalina에서 32비트 앱 지원 완전 제거)
- Intel → Apple Silicon (2020, Rosetta 2로 전환)
- 사용자 경험 우선: 애니메이션, 폰트 렌더링, 색 관리 같은 것들이 OS 수준에서 일관됨
- 보안 통제: Gatekeeper, notarization, SIP 같은 계층적 보안 체계로 모든 앱을 Apple의 검증 하에 둠
단점: 자유도가 낮고, Apple이 지원을 끊으면 방법이 없습니다 (예: 7년 이상 된 Mac은 최신 macOS 설치 불가). 또한 Apple 생태계 밖과의 호환성은 부차적.
철학 비교표
| 기준 | Linux | Windows | macOS |
|---|---|---|---|
| 핵심 가치 | 개방성, 성능 | 호환성, 엔터프라이즈 | 통합, 경험 |
| 커널 수정 | 누구나 가능 | Microsoft만 | Apple만 |
| 바이너리 호환성 | 커널 ABI만 보장 | 30년 유지 | 대전환 시 Rosetta로 |
| 유저 인터페이스 | 선택지 많음 (GNOME, KDE…) | Windows Shell 고정 | Aqua 고정 |
| 설정 저장 | 텍스트 파일 | 레지스트리 | plist (XML/바이너리) |
| 패키지 관리 | 배포판별 (apt, dnf, pacman) | MSI/EXE/Store | App Store / Homebrew / dmg |
| 주요 사용처 | 서버, 임베디드, 개발자 | 기업, 게임, 일반 소비자 | 크리에이티브, 개발자, 일반 |
| 게임 | 열악 (Proton이 개선 중) | 최고 | 중간 (Metal + Apple Silicon) |
Part 3: 커널 구조 — 모놀리식, 마이크로, 하이브리드
OS의 심장은 커널입니다. 커널은 하드웨어와 응용 프로그램 사이에서 자원을 관리합니다. 그런데 커널을 어떻게 구성할 것인가는 1980년대 이후 OS 설계자들의 오랜 논쟁거리였습니다.
세 가지 구조
1. 모놀리식 커널 (Monolithic Kernel)
커널 전체가 하나의 큰 프로그램입니다. 파일 시스템, 네트워크 스택, 드라이버, 메모리 관리 등이 모두 같은 주소 공간에서 실행됩니다.
- 장점: 빠름. 커널 내부 호출이 일반 함수 호출
- 단점: 드라이버 하나 버그로 전체 커널 크래시, 커널이 거대해짐
- 대표: Linux, 전통적 Unix, FreeBSD
2. 마이크로커널 (Microkernel)
커널은 최소한의 기능만 가집니다 — 프로세스, 메모리, IPC(프로세스 간 통신). 파일 시스템, 드라이버 등은 사용자 공간의 서버 프로세스로 분리됩니다.
- 장점: 모듈화, 안정성, 보안
- 단점: IPC 비용으로 느림 (메시지 전달이 커널을 한 번 더 거침)
- 대표: 순수 Mach, MINIX 3, QNX, L4, seL4
3. 하이브리드 커널 (Hybrid Kernel)
마이크로커널의 모듈화를 추구하지만, 성능을 위해 많은 부분을 커널 공간에 둡니다.
- 장점: 두 구조의 타협
- 단점: “진짜 microkernel이 아니다”라는 비판
- 대표: Windows NT, macOS (XNU)
Linux — 모놀리식의 정점
Linux 커널은 거대합니다. 2024년 기준 소스 코드 3천만 줄 이상. 하지만 내부적으로는 모듈화되어 있어 드라이버나 파일 시스템을 커널 모듈로 로드/언로드할 수 있습니다.
1
2
3
4
5
6
7
8
# Linux에서 현재 로드된 모듈 보기
lsmod
# 모듈 로드
sudo modprobe nvidia
# 모듈 언로드
sudo rmmod nvidia
이 모듈들은 같은 커널 주소 공간에서 실행됩니다. 즉, 악성 모듈이나 버그 있는 드라이버는 전체 시스템을 무너뜨릴 수 있습니다. 그래서 Linux 커널 모듈은 서명 검증, SecureBoot 같은 보안 계층을 추가로 둡니다.
Windows NT — 하이브리드의 실례
Windows NT는 Executive라 불리는 커널 상위 계층과 Microkernel이라 불리는 하위 계층으로 나뉩니다. 하지만 “Microkernel”이라는 이름과 달리 실제로는 드라이버, 파일 시스템, 네트워크 스택이 모두 커널 공간에서 실행됩니다.
Windows NT의 계층:
특이한 점: Windows NT는 초기에 POSIX 서브시스템과 OS/2 서브시스템을 가졌습니다. 이론적으로 POSIX 프로그램이 Windows에서 수정 없이 돌 수 있었습니다. 하지만 실용성이 낮아 POSIX 서브시스템은 Windows 8에서 제거됐고, 대신 WSL (Windows Subsystem for Linux)이 완전히 다른 방식(Linux 커널 자체를 돌리는 VM)으로 구현됐습니다.
XNU — Mach + BSD의 이중 구조
macOS의 커널은 XNU라 불립니다 (“X is Not Unix”). XNU는 두 레이어로 구성됩니다:
- Mach 3.0 microkernel (하위): Carnegie Mellon 연구에서 온 것. 태스크, 스레드, 메시지 전달(Mach port), 가상 메모리 담당
- BSD layer (상위): FreeBSD에서 포팅된 Unix 구현. 프로세스 모델(POSIX), 네트워크 스택, 파일 시스템(HFS+/APFS)
- I/O Kit: 드라이버 프레임워크 (C++로 작성)
왜 이렇게 이상한 구조인가?
원래 NeXTSTEP은 “순수 microkernel = Mach” 위에 “서버 프로세스로서의 BSD”를 얹는 구조를 시도했습니다. 그런데 이 방식은 너무 느렸습니다. 파일을 읽는 것조차 유저 공간의 BSD 서버와 Mach 커널 사이의 IPC를 여러 번 거쳐야 했기 때문입니다.
그래서 타협했습니다: BSD 코드를 Mach와 같은 커널 공간에 이식. “Microkernel”이라는 건축 철학은 깨졌지만, 성능이 확보됐습니다. 이것이 지금의 XNU입니다 — 이론상 microkernel이지만 실제로는 하이브리드.
잠깐, 이건 짚고 넘어가자
“Microkernel이 이론적으로 좋다면서 왜 아무도 순수 microkernel을 안 쓰는가?”
답은 IPC 비용입니다. Microkernel에서는 파일을 읽으려면 다음과 같이 진행됩니다:
- 앱이 “파일 읽어줘” 메시지를 커널에 보냄
- 커널이 그 메시지를 파일 시스템 서버 프로세스에 전달
- 파일 시스템 서버가 디스크 드라이버 서버에 메시지를 보냄
- 디스크 드라이버가 실제로 디스크를 읽고 결과를 파일 시스템 서버에 돌려보냄
- 파일 시스템 서버가 앱에 결과를 돌려보냄
각 단계마다 컨텍스트 스위치 + 메시지 복사가 발생합니다. 1980~90년대 하드웨어에서는 이 비용이 감당 안 될 정도였습니다.
모놀리식 커널에서는 같은 작업이 함수 호출 한 번으로 끝납니다.
그래서 대부분의 실용 OS는 “microkernel 설계 사상은 받아들이되, 성능을 위해 타협”하는 하이브리드로 수렴했습니다. 순수 microkernel은 실시간 시스템(QNX), 보안 중요 시스템(seL4 — 수학적으로 검증된 커널)처럼 특수 분야에서만 살아남았습니다.
Part 4: 실행 바이너리 포맷 — 같은 C 코드, 다른 결과물
여러분이 C++로 작성된 Unity 게임을 빌드하면 세 OS에서 다른 바이너리가 나옵니다:
- Linux: ELF (Executable and Linkable Format)
- Windows: PE / PE32+ (Portable Executable)
- macOS: Mach-O
이 포맷들은 완전히 다릅니다. 단순한 확장자 차이가 아니라 파일 내부 구조가 다르기 때문에, 한 OS의 바이너리를 다른 OS에서 실행할 수 없습니다 (에뮬레이터를 쓰지 않는 한).
ELF — Linux의 표준 (1988~)
Executable and Linkable Format은 System V에서 도입된 포맷으로, 지금은 대부분의 Unix 계열(Linux, FreeBSD, Solaris)이 씁니다.
ELF 파일의 구조:
ELF 확인:
1
2
3
4
5
6
7
8
9
10
$ file /bin/ls
/bin/ls: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), ...
$ readelf -h /bin/ls
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Type: DYN (Position-Independent Executable file)
Machine: Advanced Micro Devices X86-64
PE — Windows의 계보 (1993~)
Portable Executable은 Windows NT에서 도입된 포맷입니다. Unix의 COFF(Common Object File Format)에서 파생했지만 Microsoft 고유의 확장이 많습니다.
PE 파일의 구조:
재미있는 점: PE 파일 맨 앞에는 여전히 DOS 호환용 “MZ” 매직 넘버가 있습니다 (MZ는 DOS의 개발자 Mark Zbikowski의 이니셜). 1993년에 설계된 포맷이 1981년 DOS 호환성 문자열을 지금도 들고 있습니다. 이게 Windows의 하위 호환성 문화를 보여주는 대표 사례입니다.
Mach-O — macOS의 포맷
Mach-O (Mach Object)는 Mach 커널과 함께 설계된 포맷입니다. NeXTSTEP에서 시작해 지금도 macOS/iOS가 씁니다.
Mach-O 파일의 구조:
Universal Binary (Fat Binary): 하나의 파일에 여러 아키텍처의 Mach-O를 모두 담을 수 있습니다.
이것이 “동일한 앱이 Intel Mac과 M1 Mac 양쪽에서 네이티브로 돌아가는” 구조입니다. 2006년 PowerPC→Intel 전환 때도, 2020년 Intel→Apple Silicon 전환 때도 같은 방식으로 이식이 이뤄졌습니다.
멀티플랫폼 빌드에서의 의미
Unity, Unreal 같은 엔진이 “한 번 만들면 여러 플랫폼에서 돈다”고 광고하지만, 실제로는 엔진이 내부적으로 세 포맷에 맞춰 다시 빌드합니다. 여러분이 Build Settings에서 플랫폼을 바꿀 때 엔진이 하는 일:
- Windows: MSVC 또는 clang-cl로 컴파일 → PE32+ 생성, Windows SDK 링크
- macOS: clang/xcode 툴체인 → Mach-O 생성, Cocoa 프레임워크 링크 (Universal Binary로 Intel+ARM 동시)
- Linux: gcc 또는 clang → ELF 생성, glibc 링크
같은 C++ 코드여도 최종 바이너리는 전혀 다른 파일입니다. 그래서 Windows에서 빌드한 .exe를 macOS로 가져다 둬도 안 돕니다.
또 하나의 함정: 게임 엔진은 동적 라이브러리를 많이 씁니다.
- Windows:
.dll - macOS:
.dylib또는.framework(번들 형태) - Linux:
.so
각 플랫폼별로 전부 따로 빌드해야 합니다. Unity 네이티브 플러그인이 Windows만 지원하는 경우가 흔한 이유입니다.
Part 5: macOS 특화 이야기 — Apple이 쌓아 올린 것들
이제 Mac 유저에게 특히 재미있을 섹션입니다. macOS가 다른 OS와 구별되는 Apple 고유의 시스템들을 깊이 살펴봅니다.
XNU 탄생의 뒷이야기
앞서 XNU가 Mach + BSD의 이중 구조라고 했지만, 그 과정에는 실패와 타협의 역사가 있습니다.
1단계 (1985~88) — 순수 Mach의 꿈 Carnegie Mellon University의 Mach 프로젝트는 “BSD Unix 기능을 microkernel로 재구현”하는 학술 실험이었습니다. Rashid 교수와 학생들이 Mach 2.0을 내놓았는데, 이것은 “Mach + BSD 서버”가 한 커널에 섞인 혼합 구조였습니다.
2단계 (1990) — Mach 3.0의 시도 Mach 3.0은 순수 microkernel을 지향해 BSD 코드를 완전히 유저 공간 서버로 분리했습니다. 이론적으로 완벽했지만 성능이 끔찍했습니다. OSF/1이라는 상업 OS가 Mach 3.0으로 나왔지만 시장에서 실패했습니다.
3단계 (1989~96) — NeXTSTEP의 실용적 선택 NeXT는 처음에 Mach 2.0을 기반으로 NeXTSTEP을 만들었습니다. 일부 BSD 기능을 Mach 커널에 직접 병합해 성능을 확보. 이것이 NeXTSTEP의 커널 기반입니다.
4단계 (2000~) — XNU Apple이 NeXTSTEP을 가져와 macOS로 만들 때, BSD 쪽을 FreeBSD 5.x에서 대대적으로 업데이트해 가져왔습니다. 이 결과가 XNU. 그래서 macOS에서 uname -a를 치면 “Darwin”이 나오는데, Darwin = XNU + BSD 유저랜드 = macOS의 오픈소스 부분입니다.
1
2
$ uname -a
Darwin MacBook.local 23.0.0 Darwin Kernel Version 23.0.0: ...
Apple은 Darwin을 오픈소스로 공개합니다. 여러분도 opensource.apple.com에서 XNU 소스를 받아 빌드할 수 있습니다.
Mach port — 모든 것의 뿌리
Mach microkernel의 핵심 추상화는 port (포트)입니다. Mach port는 Unix의 file descriptor와 비슷한 역할이지만, 훨씬 광범위합니다.
- 프로세스 간 통신: 메시지를 port로 주고받음
- 신호 처리: Unix 시그널이 Mach port 메시지로 변환됨
- IOKit 드라이버: 사용자 공간 앱이 드라이버와 port로 통신
- Bootstrap: 이름 서비스(
launchd가 제공)도 port 기반
왜 중요한가? macOS의 보안 모델과 IPC가 전부 port 위에 세워져 있습니다. 예를 들어 앱 샌드박스는 “이 앱은 이 특정 port만 쓸 수 있다”로 구현됩니다. iOS의 엄격한 앱 격리도 근본적으로 Mach port 기반입니다.
1
2
3
/* Mach port로 메시지 보내기 (극단 단순화) */
mach_port_t target_port = ...;
mach_msg_send(&msg_header);
개발자가 직접 쓸 일은 거의 없지만, 디버거(lldb)나 Xcode Instruments 같은 도구의 내부에서 동작합니다.
Grand Central Dispatch (2009)
2009년 macOS 10.6 Snow Leopard에서 Apple은 Grand Central Dispatch (GCD, libdispatch)를 도입했습니다. 이것은 멀티코어 시대에 대한 Apple의 답변이었습니다.
기존 스레드 모델의 문제:
1
2
3
4
/* 전통적 C/Unix 스타일 */
pthread_t thread;
pthread_create(&thread, NULL, worker_function, arg);
pthread_join(thread, NULL);
- 개발자가 스레드 개수, 수명, 동기화를 직접 관리
- CPU 코어 수를 모르면 과도하거나 부족하게 만듦
- 동기화 프리미티브 사용에 실수가 많음
GCD의 해결책: 스레드 대신 큐에 작업을 던진다.
1
2
3
4
5
6
7
8
/* Swift */
DispatchQueue.global(qos: .userInitiated).async {
/* 여기 있는 작업이 백그라운드에서 실행됨 */
let result = heavyComputation()
DispatchQueue.main.async {
updateUI(result)
}
}
OS가 CPU 코어 수, 시스템 부하 등을 고려해 스레드를 자동으로 생성/재사용합니다. 개발자는 “어떤 우선순위로 실행할까”만 지정합니다 (QoS: User Interactive, User Initiated, Utility, Background).
GCD는 오픈소스 libdispatch로 공개됐고, Swift on Linux에서도 사용됩니다. 즉, 다른 언어나 플랫폼에서도 GCD 스타일 프로그래밍이 가능합니다.
게임 개발 시각에서: Unity Job System은 GCD와 사상이 매우 비슷합니다 — “스레드가 아닌 작업을 스케줄러에 맡긴다”. Part 13에서 자세히 다룹니다.
launchd — systemd보다 먼저
2005년 macOS 10.4 Tiger에서 Apple은 launchd를 도입했습니다. 이것은 Unix의 전통적 init 시스템(SysVinit, cron, xinetd, inetd, atd 등 여러 데몬의 분산된 역할)을 하나의 프로세스로 통합한 것입니다.
launchd 이전의 Unix:
init(PID 1): 부팅 시스템 초기화cron: 주기적 작업atd: 일회성 예약 작업inetd: 네트워크 요청 시 데몬 시작- 각 데몬이 별도로 실행
launchd는 이걸 전부 합친 만능 데몬 관리자입니다:
- PID 1로 실행, 시스템 전체 프로세스 관리
- XML 기반 plist 파일로 서비스 정의
- 파일 접근, 네트워크 연결 등 온디맨드 실행 지원
- 실패 시 자동 재시작
역사적 의미: Linux의 systemd (2010년 Lennart Poettering)가 launchd에서 영감을 받았습니다. systemd가 도입됐을 때 Linux 커뮤니티에서 “Unix 철학에 어긋난다”는 비판이 컸는데, 이미 launchd가 5년 먼저 같은 접근을 했고 macOS에서 별 문제 없이 돌고 있었습니다.
launchctl 명령으로 관리:
1
2
3
4
5
6
7
8
# 실행 중인 서비스 목록
launchctl list
# 특정 서비스 시작
launchctl load ~/Library/LaunchAgents/com.example.myservice.plist
# 중지
launchctl unload ~/Library/LaunchAgents/com.example.myservice.plist
Apple Silicon — P/E 코어의 이질 구조
2020년 Apple은 자체 설계 CPU M1 (Apple Silicon)을 Mac에 도입했습니다. M1은 ARM64 기반이지만 일반 ARM 서버와 다른 독특한 구조를 가집니다.
P-코어 (Performance)와 E-코어 (Efficiency)
M1은 동일한 ARM ISA를 실행하는 두 종류의 코어를 가집니다:
- P-코어 “Firestorm”: 고성능, 고전력. 게임, 컴파일, 렌더링 등 무거운 작업
- E-코어 “Icestorm”: 저성능, 저전력. 백그라운드 작업, 시스템 데몬, 배터리 절약
| 스펙 | P-코어 | E-코어 |
|---|---|---|
| 클럭 | 3.2 GHz | 2.0 GHz |
| L1 캐시 | 192KB | 128KB |
| L2 캐시 | 공유 12MB | 공유 4MB |
| 전력 | ~15W | ~1W |
| 성능 비율 | ~100% | ~25% |
| M1 구성 | 4개 | 4개 |
macOS의 QoS 기반 스케줄링: 앞서 본 GCD의 QoS 클래스가 여기서 다시 등장합니다.
- User Interactive / User Initiated QoS → 주로 P-코어
- Utility QoS → 상황에 따라
- Background QoS → 주로 E-코어
개발자가 DispatchQueue.global(qos: .userInitiated)라고만 쓰면 OS가 어떤 코어에서 실행할지 결정합니다. 이것이 Apple의 “개발자가 하드웨어 세부를 몰라도 되는” 철학입니다.
16KB 페이지 크기
Apple Silicon의 또 다른 특이점은 페이지 크기가 16KB라는 것입니다. Linux/Windows 표준은 4KB.
- 장점: TLB(Translation Lookaside Buffer) miss가 줄어들고, 큰 메모리 앱의 성능이 향상됨
- 단점: 메모리 정렬 요구사항 변경. 4KB 페이지를 가정한 구형 앱이 깨질 수 있음
2020년 Apple Silicon 전환 초기에 Homebrew, Docker, 일부 바이너리 호환성 툴이 16KB 페이지 문제로 고생했습니다. 지금은 대부분 해결됐지만, Unity 네이티브 플러그인 개발 시 mprotect() 호출 같은 것에서 페이지 정렬에 주의해야 합니다.
Rosetta 2 — 에뮬레이터가 아니라 번역기
Apple Silicon 전환의 성공 요인 중 하나는 Rosetta 2입니다. 이것은 x86_64 Mach-O 바이너리를 ARM64에서 실행합니다. 놀라운 점: 네이티브 성능의 70~80%가 나옵니다.
Rosetta 2는 JIT 에뮬레이터가 아닙니다. 앱을 설치할 때(또는 첫 실행 시) x86 명령을 ARM으로 AOT(Ahead-of-Time) 번역해서 파일로 저장합니다. 이후 실행은 이미 번역된 ARM 바이너리를 돌리는 것이라 빠릅니다.
결정적 트릭 — 하드웨어 TSO 모드: 이 부분이 가장 흥미롭습니다.
x86은 강한 메모리 모델 (TSO, Total Store Order)을 갖습니다. 한 CPU가 쓴 값이 다른 CPU에 보이는 순서가 프로그래밍 순서와 거의 같습니다.
ARM은 약한 메모리 모델을 갖습니다. CPU가 성능을 위해 메모리 쓰기/읽기 순서를 마음대로 재배치할 수 있습니다. 프로그래머가 명시적으로 메모리 배리어를 넣어야 순서가 보장됩니다.
문제는 x86용으로 작성된 프로그램이 TSO를 암묵적으로 가정할 때 생깁니다. 이런 프로그램을 단순히 ARM 명령으로 번역하면, ARM의 재배치 때문에 race condition이 새로 생깁니다.
Apple의 해결: M1 CPU에 “TSO 모드” 하드웨어를 넣었습니다. Rosetta 2가 번역한 바이너리가 실행될 때, CPU에 “이 스레드는 TSO 모드로 돌려”라는 플래그를 세웁니다. 그러면 ARM CPU가 x86과 같은 강한 메모리 순서로 동작합니다.
💡 이 주제는 Part 12 (메모리 모델과 원자 연산)에서 다시 등장합니다. 지금은 “Apple이 하드웨어 레벨에서 호환성 트릭을 썼다”는 점만 기억하면 됩니다.
Rosetta 2의 한계:
- AVX-512 같은 최신 x86 확장 명령은 번역 안 됨
- 커널 확장(.kext)은 Rosetta로 못 돌림 — OS 자체는 네이티브여야 함
- JIT 컴파일러가 내장된 프로그램(크롬 V8 엔진 등)은 Rosetta + JIT의 이중 번역이라 느릴 수 있음
XNU 내부 구조
Apple Silicon 이질 코어 구조
잠깐, 이건 짚고 넘어가자
“Rosetta 2가 왜 빠른가? 에뮬레이터라며 네이티브 성능의 70%가 말이 되는가?”
세 가지 이유가 겹쳐서 그렇습니다.
- AOT 번역 (사전 번역): 에뮬레이터가 아닙니다. 앱을 설치하거나 처음 실행할 때 x86 바이너리를 ARM으로 완전히 번역해 캐시합니다. 그 뒤로는 네이티브 ARM을 실행할 뿐입니다.
- M1이 애초에 x86보다 훨씬 빠름: M1의 싱글 코어 성능이 같은 세대 Intel CPU보다 우수합니다. 70%로 떨어져도 절대 성능은 나쁘지 않습니다.
- 하드웨어 TSO 모드: x86의 메모리 모델을 ARM에 강제하기 위한 소프트웨어 에뮬레이션은 비쌉니다. Apple은 이걸 하드웨어에 넣어 공짜로 만들었습니다.
한계: 하드웨어 TSO 모드는 x86 바이너리가 실행될 때만 켜집니다. 네이티브 ARM 앱은 약한 메모리 모델을 그대로 씁니다.
Part 6: 세 OS 장단점 표
이제까지의 내용을 한 표로 정리합니다. 각 OS의 강점과 약점을 객관적으로 나열했습니다.
개발자 관점 종합 비교
| 영역 | Linux | Windows | macOS |
|---|---|---|---|
| 커널 소스 접근 | ✅ 완전 공개 | ❌ 비공개 | 🟡 Darwin만 공개 (GUI/Cocoa 비공개) |
| CLI 생태계 | ✅ 최고 (bash, coreutils 네이티브) | 🟡 PowerShell 훌륭, WSL 필요 | ✅ Unix 표준 도구 기본 탑재 |
| 패키지 관리 | ✅ apt/dnf/pacman | 🟡 winget/choco (후발) | 🟡 Homebrew (비공식) |
| 가상화/컨테이너 | ✅ Docker 네이티브 | 🟡 WSL 2 / Hyper-V | 🟡 Docker Desktop (VM 경유) |
| 언어 지원 | ✅ 모든 언어 | ✅ 모든 언어 (특히 .NET) | ✅ 모든 언어 (Swift가 1급) |
| IDE | 🟡 VS Code, CLion | ✅ Visual Studio 최고 | ✅ Xcode, JetBrains |
| 문서화 | 🟡 분산됨, man 페이지 | ✅ MSDN 체계적 | ✅ Apple Developer 문서 |
| 커뮤니티 | ✅ 거대, 개방적 | 🟡 엔터프라이즈 중심 | 🟡 Apple 생태계 중심 |
게임 개발 관점
| 영역 | Linux | Windows | macOS |
|---|---|---|---|
| 주요 그래픽 API | Vulkan, OpenGL | DirectX 11/12, Vulkan | Metal (OpenGL/Vulkan 지원 종료 중) |
| 게임 엔진 지원 | 🟡 Unity/Unreal 빌드 타겟으로만 | ✅ 최고 (에디터 포함) | 🟡 Unity/Unreal 에디터 지원 개선 중 |
| 오디오 API | ALSA, PulseAudio, PipeWire | XAudio2, WASAPI | Core Audio |
| 디버거/프로파일러 | 🟡 GDB, Valgrind | ✅ Visual Studio | ✅ Instruments, Xcode |
| 스팀 게임 플레이 | 🟡 Proton (개선 중) | ✅ 네이티브 | 🟡 제한적 |
| VR/AR 지원 | 🟡 SteamVR | ✅ WMR, SteamVR | 🟡 Vision Pro 생태계 |
서버 운영 관점
| 영역 | Linux | Windows | macOS |
|---|---|---|---|
| 웹 서버 시장 점유율 | ~75% | ~20% | ~0% (서버용 아님) |
| 컨테이너 네이티브 | ✅ | 🟡 LCOW (리눅스 컨테이너를 WSL로) | ❌ |
| 저자원 운영 | ✅ 최소 수백 MB로 동작 | 🟡 수 GB 필요 | 🟡 서버 용도 거의 없음 |
| 라이선스 비용 | ✅ 무료 | 💰 유료 (Windows Server) | 🟡 Apple 하드웨어 강제 |
핵심 결론
- “가장 좋은 OS”는 없다 — 용도에 따라 다릅니다
- 서버/개발: Linux가 지배적
- 기업/게임 클라이언트: Windows가 지배적
- 크리에이티브 작업/일반 개인 개발: macOS가 강세
- 모든 OS가 서로의 강점을 빌려오는 중:
- Windows가 WSL로 Linux 호환
- macOS가 Homebrew로 Linux 도구 생태계 활용
- Linux가 데스크톱 UX 개선에 투자
Part 7: 보안과 샌드박스 — 짧게
OS마다 보안 모델이 다릅니다. 게임 개발 관점에서 관련 있는 부분만 간단히.
macOS — 다층 보안
SIP (System Integrity Protection): 시스템 파일 보호. /System, /bin 등은 root조차 수정 불가. 2015년 El Capitan에서 도입.
Gatekeeper: 서명되지 않은 앱 실행 차단. “Apple에서 신원이 확인되지 않은 개발자” 경고가 이것.
Notarization (공증): Apple에 바이너리를 제출해 악성코드 검증을 받아야 Gatekeeper 경고 없이 실행 가능. 2019년 의무화.
App Sandbox: Mac App Store 앱은 의무적으로 샌드박스. 일반 앱은 선택적. 파일 시스템, 네트워크, 카메라 등을 entitlement로 명시.
Hardened Runtime: JIT, 라이브러리 주입 등을 차단하는 추가 보안 계층.
개발자 관점: Mac용 상용 앱 배포 시 Apple Developer 계정($99/년)으로 서명 + 공증 필수. 게임 배포 시 중요.
Windows — UAC와 Defender
UAC (User Account Control): 관리자 권한이 필요한 작업에 사용자 확인. Vista 때 도입되며 악평 샀지만 지금은 필수 보안 모델.
Windows Defender: 기본 내장 안티바이러스. Windows 10 이후 거의 서드파티 AV가 불필요한 수준.
Code Signing: Authenticode 서명. EV 인증서는 SmartScreen 경고 없이 실행. 게임 배포 시 권장.
AppContainer: UWP 앱 격리. Mac App Sandbox와 유사한 개념이지만 사용 범위가 좁음.
Linux — 유연한 도구 모음
User/Group 권한: Unix 전통. rwx 비트, UID/GID.
Capabilities: root의 권한을 쪼개서 부여 (CAP_NET_BIND_SERVICE, CAP_SYS_ADMIN 등).
SELinux / AppArmor: Mandatory Access Control. 더 세밀한 정책 강제.
cgroups + namespaces: Docker의 기반 기술. 프로세스 그룹에 자원 제한과 격리.
seccomp: 시스템 콜 필터링. 특정 시스템 콜만 허용하도록 샌드박스.
게임 개발 시 AppImage, Flatpak, Snap 같은 배포 포맷은 내부적으로 이 기술들을 사용합니다.
Part 8: 게임 개발자 관점에서
이제 게임 개발자 입장에서 세 OS의 차이가 어떻게 드러나는지 살펴봅니다.
플랫폼별 게임 개발 고려사항
1. Unity 에디터
- Windows: 풀 기능, 권장 플랫폼
- macOS: 지원 양호, Apple Silicon 네이티브 빌드 가능
- Linux: 제한적 지원 (공식 Editor 있음, 플러그인 호환성 떨어짐)
2. Unreal Engine 에디터
- Windows: 풀 기능, 기본 지원
- macOS: 지원되지만 일부 기능 제한 (Vulkan 지원 등)
- Linux: 공식 지원 있음, 에디터도 빌드 가능
3. 그래픽 API 선택
- 크로스플랫폼이 목표라면 Vulkan + DirectX 12 추상화
- Apple 플랫폼 타겟이면 Metal 고려 (Apple이 OpenGL/Vulkan 지원을 중단 중)
- Unity/Unreal 같은 엔진이 이 추상화를 제공하지만, 네이티브 최적화 시 직접 관여 필요
4. 크래시 핸들러
- Windows: SEH (Structured Exception Handling),
SetUnhandledExceptionFilter - Linux/macOS: POSIX 시그널 (
SIGSEGV,SIGABRT),signal()또는sigaction() - 두 접근이 달라서 크로스플랫폼 크래시 리포터(Sentry, Crashlytics)가 복잡해짐
5. 파일 경로
- Windows:
C:\Users\name\AppData\..., 역슬래시 - macOS:
/Users/name/Library/Application Support/..., 슬래시 - Linux:
/home/name/.local/share/...(XDG 표준), 슬래시 - 엔진의
Application.persistentDataPath같은 추상화를 쓰되, 직접 다룰 때 주의
6. 스레드 우선순위
- Windows:
SetThreadPriority, 7단계 (IDLE~TIME_CRITICAL) - macOS: QoS 클래스 (4단계) + pthread 우선순위
- Linux: nice 값 (-20~19) + pthread SCHED_FIFO/RR
- 게임에서 오디오 스레드 같은 것에 높은 우선순위를 줘야 할 때 API가 달라짐
크로스 플랫폼 엔진의 추상화
엔진은 OS 차이를 숨기기 위한 레이어를 가집니다. Unreal Engine의 경우:
1
2
3
4
5
6
7
8
9
10
11
/* UE의 플랫폼 추상화 (개념적 예시) */
#if PLATFORM_WINDOWS
#include "Windows/WindowsPlatformFile.h"
typedef FWindowsPlatformFile FPlatformFile;
#elif PLATFORM_MAC
#include "Apple/ApplePlatformFile.h"
typedef FApplePlatformFile FPlatformFile;
#elif PLATFORM_LINUX
#include "Unix/UnixPlatformFile.h"
typedef FUnixPlatformFile FPlatformFile;
#endif
이 때문에 엔진 개발자(엔진을 수정하는 프로그래머)는 세 OS의 API를 모두 이해해야 합니다. 게임 프로그래머(엔진을 소비하는 입장)는 FPlatformFile 같은 추상 레이어만 보면 됩니다.
툴체인 호환성
| 도구 | Linux | Windows | macOS |
|---|---|---|---|
| 주 컴파일러 | gcc/clang | MSVC/clang-cl | clang |
| 표준 라이브러리 | glibc/libstdc++/libc++ | MSVC STL | libc++ |
| 링커 | ld/lld | link.exe/lld-link | ld64 |
| 디버거 | gdb, lldb | Visual Studio, WinDbg | lldb, Xcode |
| 프로파일러 | perf, Tracy | Visual Studio Profiler, PIX | Instruments |
| CI/CD 가능성 | ✅ 최고 | ✅ GitHub Actions Windows Runner | 🟡 Mac Runner는 유료/제한 |
Apple의 함정: iOS/macOS 앱을 빌드하려면 Xcode가 필요하고, Xcode는 macOS에서만 돕니다. 즉, Apple 플랫폼 타겟 게임을 만들려면 반드시 Mac 빌드 머신이 있어야 합니다. CI/CD에서 Mac Runner가 비싼 이유입니다.
플랫폼 별 디버깅 경험 비교
Windows (Visual Studio)
- 최고 수준의 IDE + 디버거 통합
- Edit and Continue, 조건부 중단점, Data Breakpoint 모두 매끄러움
- PIX로 GPU 프로파일링
macOS (Xcode + Instruments)
- Instruments는 세계 최고 수준의 프로파일러 중 하나 (System Trace, Time Profiler, Allocations)
- Apple Silicon의 P/E 코어 타임라인을 시각화해줌
- Metal Frame Debugger
Linux (gdb/lldb + Tracy)
- 커맨드라인 도구가 주. VS Code가 UX를 많이 개선
- Valgrind (Memcheck)은 강력하지만 느림
- Tracy Profiler는 크로스플랫폼 최고 옵션 중 하나
정리
이 편에서 다룬 것을 한 페이지로 요약합니다.
혈통:
- Unix (1969) → BSD (1977) → NeXTSTEP (1989) → macOS (2001)
- Unix → Minix → Linux (1991)
- VMS (1977) + Dave Cutler → Windows NT (1993)
설계 철학:
- Linux: 개방성 + 성능
- Windows: 하위 호환성
- macOS: 수직 통합 + 경험
커널 구조:
- Linux: 모놀리식
- Windows NT: 하이브리드
- macOS XNU: Mach microkernel + BSD layer (이중 구조)
바이너리 포맷:
- Linux: ELF
- Windows: PE (1981년 DOS 호환 MZ 헤더 유지)
- macOS: Mach-O (Universal Binary로 다중 아키텍처)
macOS 특유의 것들:
- XNU: 이론은 microkernel, 실제는 하이브리드
- Mach port: macOS IPC와 보안의 뿌리
- Grand Central Dispatch (2009): “스레드 대신 큐” 추상화
- launchd (2005): systemd의 5년 전 원형
- Apple Silicon: P/E 이질 코어 + 16KB 페이지
- Rosetta 2: AOT 번역 + 하드웨어 TSO 모드
게임 개발 시 기억할 점:
- 실행 바이너리 포맷이 다르기 때문에 멀티플랫폼 빌드는 각 OS별 빌드
- 크래시 핸들러, 스레드 우선순위, 파일 경로 등 사소해 보이는 것도 API가 다름
- 엔진 추상화 레이어를 믿되, 성능이 중요한 부분은 플랫폼별 최적화 필요
- Apple 플랫폼 타겟이면 반드시 Mac 빌드 머신 필요
다음 편부터는 이 지도를 바탕으로 구체적인 이론으로 들어갑니다. Part 8은 프로세스와 스레드 — PCB/TCB 구조, fork()와 CreateProcess()의 실제 차이, 스레드 매핑 모델, 컨텍스트 스위칭 비용까지 게임 엔진의 실행 모델과 연결해 살펴봅니다.
References
교재
- Silberschatz, Galvin, Gagne — Operating System Concepts, 10th ed., Wiley, 2018 — OS 표준 교재, 3장(Processes), 4장(Threads) 참조
- Tanenbaum, Bos — Modern Operating Systems, 4th ed., Pearson, 2014 — microkernel vs monolithic 논쟁의 원전
- Bovet, Cesati — Understanding the Linux Kernel, 3rd ed., O’Reilly, 2005 — Linux 커널 내부
- Russinovich, Solomon, Ionescu — Windows Internals, 7th ed., Microsoft Press, 2017 — NT 커널 상세
- Singh — Mac OS X Internals: A Systems Approach, Addison-Wesley, 2006 — XNU, Mach, BSD layer
- Levin — *OS Internals: Volume I - User Mode and Volume II - Kernel Mode, Technologeeks, 2019 — macOS/iOS 내부의 가장 상세한 현대 저술
- Gregory — Game Engine Architecture, 3rd ed., CRC Press, 2018 — 게임 엔진에서의 OS 활용
논문/연구 문서
- Accetta, Baron, Bolosky, Golub, Rashid, Tevanian, Young — “Mach: A New Kernel Foundation for UNIX Development”, USENIX Summer 1986 — Mach 최초 설명 논문
- Young, Tevanian, Rashid, Golub, Eppinger, Chew, Bolosky, Black, Baron — “The Duality of Memory and Communication in the Implementation of a Multiprocessor Operating System”, SOSP 1987
- Rashid, Baron, Forin, Golub, Jones, Julin, Orr, Sanzi — “Mach: A Foundation for Open Systems”, Workshop on Workstation Operating Systems, 1989
- Bershad, Anderson, Lazowska, Levy — “Lightweight Remote Procedure Call”, SOSP 1989 — microkernel IPC 최적화
- Anderson, Bershad, Lazowska, Levy — “Scheduler Activations: Effective Kernel Support for the User-Level Management of Parallelism”, SOSP 1991 — M:N 스레드 모델
공식 문서 / 소스
- Apple Open Source — opensource.apple.com — XNU, Darwin 소스
- Apple Developer — Dispatch Queues and Concurrency — developer.apple.com/documentation/dispatch
- Apple Developer — About the Rosetta Translation Environment — developer.apple.com
- Linux Kernel Documentation — kernel.org/doc
- Microsoft Docs — Windows Kernel-Mode Architecture — learn.microsoft.com
- FreeBSD Architecture Handbook — docs.freebsd.org/en/books/arch-handbook/
블로그/기사
- Raymond Chen — The Old New Thing — Windows 하위 호환성 일화 (SimCity 사례 포함)
- Howard Oakley — The Eclectic Light Company — macOS 내부 동작 해설
- Hector Martin (marcan) — Apple Silicon 역공학 — Asahi Linux 프로젝트
- Dougall Johnson — “M1 Memory and Performance” 시리즈 — Apple Silicon 하드웨어 분석
- Linus Torvalds — comp.os.minix “Hello everybody” post (1991-08-25)
- Linus vs. Tanenbaum debate (1992) — microkernel 논쟁 아카이브
도구
file,readelf,objdump(Linux) — ELF 분석dumpbin,PEview(Windows) — PE 분석otool,nm,lipo(macOS) — Mach-O 분석launchctl,ps,top— 세 OS 공통 관찰 도구- Instruments (macOS) — Apple 공식 프로파일러
이미지 출처
- Ken Thompson & Dennis Ritchie (1973) — Jargon File, Public Domain — Wikimedia Commons
- Linus Torvalds at LinuxCon Europe 2014 — photo by Krd, CC BY-SA 4.0 — Wikimedia Commons
- NeXTcube (1990) at Computer History Museum — photo by Michael Hicks, CC BY 2.0 — Wikimedia Commons
