테마
Chapter 02. I/O 성능, DMA, 캐시 메모리
I/O 경로가 성능에 미치는 영향, DMA(Direct Memory Access)를 통한 최적화, 캐시 메모리(Cache Memory)의 계층 구조, 그리고 예측 실행(Speculative Execution)이 만들어낸 보안 취약점까지 살펴본다.
2.1 하드웨어 구조의 진화: 브릿지 칩셋
과거의 메인보드 구조
옛날 메인보드에는 두 개의 브릿지 칩셋이 존재했다.
| 칩셋 | 이름 | 담당 영역 | 속도 |
|---|---|---|---|
| 노스브릿지 (Northbridge) | MCH (Memory Controller Hub) | CPU ↔ RAM, 고성능 I/O (비디오카드, PCI Express) | 빠름 |
| 사우스브릿지 (Southbridge) | ICH (I/O Controller Hub) | USB, PCI, ISA, SATA, 오디오 등 | 상대적으로 느림 |
노스브릿지는 CPU와 가장 가까이에 위치하며, 속도가 중요한 장치들을 관장했다. 반면 사우스브릿지는 노스브릿지를 경유하여 CPU와 통신했고, 상대적으로 느린 주변 장치들을 담당했다.
핵심 포인트: "좋은 메인보드"란 곧 "좋은 브릿지 칩셋을 탑재한 메인보드"를 의미했다. 아무리 빠른 CPU를 장착해도, 칩셋이 병목이면 CPU 성능을 온전히 발휘할 수 없었다.
현재의 메인보드 구조
인텔(Intel)이 중요한 결정을 내렸다. RAM 컨트롤러를 CPU 내부에 직접 내장시킨 것이다. 이로써 노스브릿지의 핵심 기능이 CPU로 흡수되었다.
현재 구조에서는:
- CPU가 직접 처리: RAM, PCI Express (GPU 등), 핵심 고속 I/O
- 칩셋(PCH)이 처리: USB, SATA, 오디오, 네트워크 등 나머지 장치
| 항목 | 과거 | 현재 |
|---|---|---|
| 메모리 제어 | 노스브릿지가 담당 | CPU가 직접 처리 |
| GPU 연결 | 노스브릿지 경유 | CPU에서 직접 PCI Express 제공 |
| 느린 장치 관리 | 사우스브릿지 | PCH (Platform Controller Hub) |
| 병목 구간 | 노스브릿지 ↔ 사우스브릿지 간 버스 | CPU ↔ PCH 간 DMI 링크 |
| 칩셋 수 | 2개 (노스 + 사우스) | 1개 (PCH) |
왜 이렇게 바뀌었는가? CPU와 RAM 사이에 노스브릿지라는 중간 다리가 있으면, 아무리 CPU가 빨라도 그 다리의 대역폭이 한계가 된다. CPU가 RAM 컨트롤러를 직접 품으면 레이턴시(Latency)가 크게 줄어들고, 메모리 접근 속도가 비약적으로 향상된다.
2.2 I/O 성능과 DirectX의 탄생
일반 I/O 경로 (GDI 경로)
윈도우(Windows)에서 화면에 무언가를 그릴 때, 일반적인 I/O 경로는 다음과 같이 6단계를 거친다.
장점:
- OS가 **추상화된 API(GDI)**를 제공하므로, 개발자가 그래픽 카드의 종류를 신경쓰지 않아도 된다
- 어떤 GPU가 꽂혀 있든 동일한 코드로 화면에 그릴 수 있다
단점:
- 유저 모드 → 커널 모드 → 드라이버 → 장치 → 반환까지 단계가 너무 많다
- 각 단계마다 컨텍스트 스위칭(Context Switching) 과 데이터 복사가 발생
- 일반 문서 작업에는 충분하지만, 대량의 그래픽 연산에는 치명적인 성능 저하
게임의 문제
게임은 1초에 60프레임(60 FPS) 이상을 그려야 한다. 매 프레임마다 수백만 개의 폴리곤(Polygon)을 연산하고, 텍스처(Texture)를 입히고, 조명(Lighting)을 계산해야 한다.
이런 미친 듯한 연산량을 6단계 경로로 처리하면? 절대 실시간 렌더링이 불가능하다.
DirectX의 등장
마이크로소프트(Microsoft)가 해결책을 내놓았다. 바로 DirectX이다.
"Direct"라는 이름 자체가 핵심을 설명한다. 유저 모드에서 커널 모드로, 기존의 긴 경로를 건너뛰고 "직접(Direct)" 접근하는 예외적인 API인 것이다.
| 항목 | 일반 GDI 경로 | DirectX 경로 |
|---|---|---|
| 거치는 단계 | 6단계 | 4단계 (GDI 엔진 생략) |
| 커널 오버헤드 | GDI 엔진 통과 필수 | 최소화 |
| 실시간 렌더링 | 불가능 | 가능 |
| 대상 | 일반 UI, 문서 | 게임, 멀티미디어, 3D |
Windows Vista 이후의 변화
Windows Vista에서 큰 전환이 이루어졌다.
- GDI 엔진을 커널에서 제거 - 더 이상 전통적인 GDI 경로를 기본으로 사용하지 않음
- 모든 그래픽 처리를 DirectX 기반으로 전환 - 데스크탑 렌더링 자체가 DirectX로 수행
- 3D 가속 기능이 필수 - DirectX를 지원하지 않는 구형 그래픽 카드에서는 Aero 인터페이스 사용 불가
- 데스크탑 인터페이스가 미려해짐 - 반투명 효과(Aero Glass), 부드러운 애니메이션 등
정리: DirectX는 게임만을 위한 기술이 아니다. Windows 자체가 DirectX 위에서 동작하는 시대가 되었다. 화면에 보이는 모든 것이 GPU 가속을 받는다.
2.3 DMA (Direct Memory Access)
개념
DMA(Direct Memory Access) 란 CPU의 개입 없이, I/O 장치가 RAM에 직접 접근하여 데이터를 읽고 쓰는 기술이다.
왜 이것이 중요한가? CPU는 연산에 집중해야 하는데, 데이터를 이리저리 복사하는 "심부름"까지 맡으면 연산 효율이 크게 떨어지기 때문이다.
일반 I/O 방식 vs DMA 방식
일반 방식 (CPU가 모든 데이터 이동 담당)
문제점: CPU가 데이터를 한 바이트씩 읽어서, 레지스터에 넣고, 장치에 써넣는 과정을 직접 수행한다. 이 동안 CPU는 다른 일을 하지 못한다.
DMA 방식 (CPU 해방)
| 항목 | 일반 I/O | DMA |
|---|---|---|
| CPU 역할 | 데이터 복사 직접 수행 | 지시만 내리고 해방 |
| 데이터 경로 | RAM → CPU → 장치 | RAM → 장치 (직접) |
| CPU 활용률 | 낮음 (복사에 묶임) | 높음 (연산에 집중) |
| 대용량 전송 | 느림 | 빠름 |
| 인터럽트 | 바이트/워드마다 발생 가능 | 전체 전송 완료 시 1회 |
네트워크 카드(NIC)에서의 DMA
네트워크 I/O는 DMA의 효과가 가장 극적으로 드러나는 영역이다.
일반 네트워크 I/O 경로
소켓(Socket) → I/O 버퍼 → TCP/IP 스택(커널) → 커널 버퍼 → NIC각 단계마다 데이터 복사(Memory Copy) 가 발생한다. 복사 횟수가 많을수록 CPU 시간이 낭비된다.
DMA 지원 NIC의 동작
DMA를 지원하는 NIC은 훨씬 효율적으로 동작한다.
- 유저 모드 메모리를 잠금(Lock) - 해당 메모리 영역이 스왑아웃(Swap Out)되지 않도록 고정
- NIC이 잠긴 메모리에 직접 데이터 기록 - CPU를 거치지 않고 RAM에서 직접 읽거나 씀
- 전송 완료 후 인터럽트 1회 - CPU는 완료 통보만 받음
IOCP(I/O Completion Port)가 빠른 이유: Windows의 IOCP는 내부적으로 DMA와 결합하여 동작한다. 비동기 I/O 요청을 걸어두면, DMA가 데이터 전송을 처리하고, 완료 시 컴플리션 포트(Completion Port)에 통보한다. CPU는 전송 과정에 전혀 관여하지 않으므로, 수천~수만 개의 동시 네트워크 연결을 효율적으로 처리할 수 있다.
가상환경에서의 성능: East-West 트래픽
클라우드 환경에서 재미있는 현상이 있다. 같은 물리 호스트(Physical Host) 위의 VM(Virtual Machine)들 사이의 통신은 매우 빠르다는 것이다.
이 트래픽을 East-West 트래픽이라 부른다 (물리적 네트워크를 타고 나가는 North-South 트래픽과 구분).
왜 빠른가?
- 같은 물리 서버의 VM들은 같은 물리 RAM을 공유한다
- VM 0에서 VM 1로 데이터를 보내는 것은 실제로 **RAM 내의 메모리 카피(Memory Copy)**로 끝난다
- 물리적 NIC을 거칠 필요가 없다
- 네트워크 인터럽트도 불필요 → 오버헤드가 극도로 낮음
| 트래픽 유형 | 경로 | 속도 | 인터럽트 |
|---|---|---|---|
| North-South | VM → 하이퍼바이저 → 물리 NIC → 외부 네트워크 | 상대적으로 느림 | 필요 |
| East-West | VM → RAM(메모리 카피) → VM | 매우 빠름 | 불필요 |
실무 시사점: 마이크로서비스 아키텍처에서 서로 자주 통신하는 서비스들을 같은 물리 호스트에 배치하면, East-West 트래픽의 이점을 활용할 수 있다.
2.4 캐시 메모리 (Cache Memory)
속도의 격차
CPU와 RAM 사이에는 엄청난 속도 차이가 존재한다.
| 장치 | 접근 시간 | 비유 |
|---|---|---|
| CPU 레지스터 | ~0.3ns | 눈 깜짝할 새 |
| L1 캐시 | ~1ns | 옆 사람에게 말 걸기 |
| L2 캐시 | ~3-10ns | 같은 방 책장에서 찾기 |
| L3 캐시 | ~10-20ns | 옆 방에 다녀오기 |
| RAM (메인 메모리) | ~50-100ns | 도서관에 다녀오기 |
| SSD | ~100,000ns | 다른 도시에 다녀오기 |
| HDD | ~10,000,000ns | 해외 출장 |
CPU(~1ns)와 RAM(~50ns)의 속도 차이는 약 50배다. CPU가 RAM에서 데이터를 가져올 때마다 50배 느린 장치를 기다려야 한다면, CPU의 엄청난 처리 능력이 낭비된다.
캐시 메모리(Cache Memory) 는 이 속도 차이를 메우기 위한 완충 장치이다.
캐시의 계층 구조
| 캐시 레벨 | 소속 | 내용 | 크기 | 속도 |
|---|---|---|---|---|
| L1 (Level 1) | 코어마다 개별 보유 | 명령어 캐시와 데이터 캐시 분리 | 32-64KB | ~1ns |
| L2 (Level 2) | 코어마다 개별 보유 | 명령어 + 데이터 통합(Unified) | 256KB-1MB | ~3-10ns |
| L3 (Level 3) | 모든 코어가 공유 | 명령어 + 데이터 통합 | 8-64MB | ~10-20ns |
L1 캐시가 명령어와 데이터를 분리하는 이유: CPU 파이프라인(Pipeline)에서 **명령어 페치(Fetch)**와 **데이터 로드(Load)**가 동시에 일어난다. 같은 캐시를 공유하면 충돌이 발생하므로, 아예 물리적으로 분리하여 동시 접근이 가능하도록 한다.
CPU의 예측 실행 (Speculative Execution)
여기서부터가 정말 재미있다. CPU는 단순히 "요청받은 데이터를 캐시에서 찾는" 수준이 아니다. 미래에 필요할 데이터를 미리 예측해서 가져온다.
박사님과 조교 비유
이해하기 쉽게 비유해 보자.
- 박사님 = CPU (연구에 몰두하는 천재)
- 조교 = 캐시 컨트롤러 (박사님의 연구 패턴을 관찰하는 보좌관)
- 도서관 = RAM (자료가 잔뜩 있지만 다녀오려면 시간이 걸림)
- 박사 책상 위 = 캐시 (바로 손이 닿는 곳)
예측 실행의 핵심 원리
CPU는 프로그램 실행 패턴을 분석하여 미리 데이터를 프리페치(Prefetch) 한다.
예시: for문 반복 패턴
c
for (int i = 0; i < 1000; i++) {
sum += array[i]; // array[0], array[1], array[2], ...
}캐시 컨트롤러는 이 패턴을 감지한다:
array[0]에 접근했다- 다음에
array[1]에 접근했다 - 그 다음에
array[2]에 접근했다 - 패턴 감지: 순차적 접근! →
array[3],array[4], ... 를 미리 가져오자!
| 용어 | 의미 | 발생 시 |
|---|---|---|
| Cache Hit (캐시 적중) | 예측 성공! 캐시에서 바로 제공 | ~1ns로 즉시 제공 |
| Cache Miss (캐시 미스) | 예측 실패! RAM에서 가져와야 함 | ~50ns 대기 필요 |
현대 CPU의 캐시 적중률(Cache Hit Rate)은 약 90-95% 에 달한다. 10번 중 9번은 캐시에서 바로 데이터를 얻는다는 뜻이다.
PIM (Processing-in-Memory): 차세대 기술
캐시로도 해결하기 어려운 근본적인 문제가 있다. 데이터가 RAM에서 CPU로 이동하는 과정 자체가 병목이라는 것이다. 아무리 캐시가 커져도, 결국 RAM에서 캐시로 데이터를 옮겨야 한다.
PIM(Processing-in-Memory) 은 발상을 전환한다. 데이터를 CPU로 가져오는 대신, RAM 안에서 직접 전처리 연산을 수행하는 것이다.
라면왕 리(이철호)의 감자 비유
유명한 요리 만화 "라면왕 리"에 나오는 일화로 비유하면:
- 전통적 방식: 요리사(CPU)가 "감자 가져와!" → 조수(RAM)가 감자를 통째로 가져옴 → 요리사가 직접 껍질 깎고, 썰고, 다듬고, 요리
- PIM 방식: 요리사가 "감자 가져와, 근데 깍둑썰기로 미리 해와!" → 조수가 부엌에서 미리 전처리하여 깍둑썰기 된 감자만 전달 → 요리사는 바로 요리
| 항목 | 전통적 방식 | PIM |
|---|---|---|
| 연산 위치 | CPU에서 모든 연산 | RAM 내부에서 전처리 |
| 데이터 이동량 | 대량 (원본 데이터 전체) | 소량 (처리 결과만) |
| 대역폭 부담 | 높음 | 낮음 |
| 활용 분야 | 범용 | AI, 빅데이터, 과학 연산 |
PIM의 핵심 가치: "데이터를 연산기로 옮기지 말고, 연산기를 데이터 곁으로 보내자!" 삼성, SK하이닉스 등 메모리 반도체 기업들이 적극 연구 중인 차세대 기술이다.
2.5 CPU 보안 취약점: 멜트다운(Meltdown)과 스펙터(Spectre)
"시키지도 않은 일을 한다"
2.4절에서 CPU의 예측 실행(Speculative Execution)이 캐시 적중률을 90% 이상으로 끌어올린다고 했다. 하지만 이 "시키지도 않은 일을 미리 하는" 특성이 심각한 보안 구멍을 만들어냈다.
2018년에 발표된 멜트다운(Meltdown) 과 스펙터(Spectre) 취약점이 바로 그것이다.
공격 원리 (3단계)
1단계: 금지된 메모리 읽기 시도
유저 모드 프로세스가 커널 메모리 영역을 읽으려고 시도한다. 당연히 권한 오류(Segmentation Fault) 가 발생한다.
2단계: 하지만 CPU는 이미 실행했다
여기가 핵심이다. CPU는 권한 검사와 실제 연산을 동시에(Out-of-Order) 수행한다.
- 권한 검사 결과: "접근 불가!" → 예외(Exception) 발생
- 하지만 CPU의 예측 실행 유닛은 이미 해당 메모리를 읽고 연산을 수행했다
- 연산 결과는 레지스터에서는 사라지지만, 캐시에는 흔적이 남는다
3단계: 캐시의 흔적으로 값을 추론
공격자는 사이드채널 공격(Side-Channel Attack) 을 이용한다.
- 특정 메모리 주소에 접근하는 데 걸리는 시간을 측정
- 빠르면(~1ns) → 캐시에 있다 → 예측 실행 때 접근된 것
- 느리면(~50ns) → 캐시에 없다 → 접근되지 않은 것
- 이 타이밍 차이를 이용해 간접적으로 커널 메모리의 값을 한 비트씩 추론
가상환경에서의 위협
이 취약점은 가상환경(Virtualization) 에서 특히 위험하다.
하나의 물리 서버에 여러 VM이 동작하는 클라우드 환경을 생각해 보자:
- VM 0: 악성코드가 동작하는 공격자의 VM
- VM 1: 다른 고객의 데이터베이스 서버
- VM 2: 또 다른 고객의 웹서버 (SSL 인증서 보유)
공격자(VM 0)가 예측 실행을 악용하여 무작위로 물리 메모리를 읽으면:
- CPU의 예측 실행이 하이퍼바이저(Hypervisor) 메모리에 접근
- 하이퍼바이저 메모리에는 다른 VM들의 데이터가 매핑되어 있을 수 있음
- VM 2의 SSL 인증서, VM 1의 데이터베이스 비밀번호 등이 캐시에 유입
- 사이드채널로 해당 값을 추론 → 민감 데이터 탈취
"궁예의 관심법" 이라는 비유가 딱 맞다. "나의 CPU 인스트럭션(명령)으로 그대들의 커널 메모리를 읽는 것이니라!" 직접 읽지는 못하지만, 캐시에 남긴 흔적을 통해 간접적으로 읽어내는 기술이다.
나쁜 박사 비유
2.4절의 "박사님과 조교" 비유를 확장하면:
- 나쁜 박사(공격자) 가 조교에게 일부러 다른 박사의 논문을 가져오라고 시킨다
- 조교(캐시 컨트롤러)는 권한 확인 전에 이미 도서관에서 가져와 책상 위에 놓는다
- 곧 "권한 없음!"이라며 논문을 치우지만, 책상 위에 놓았다 치운 흔적(캐시 타이밍)이 남는다
- 나쁜 박사는 이 흔적을 관찰하여 다른 박사의 연구 내용을 파악한다
멜트다운 vs 스펙터 비교
| 항목 | 멜트다운 (Meltdown) | 스펙터 (Spectre) |
|---|---|---|
| CVE | CVE-2017-5754 | CVE-2017-5753, CVE-2017-5715 |
| 공격 대상 | 커널 메모리 읽기 | 다른 프로세스 메모리 읽기 |
| 원인 | Out-of-Order 실행 | 분기 예측(Branch Prediction) 오류 |
| 영향 CPU | 주로 인텔 | 인텔, AMD, ARM 모두 |
| 소프트웨어 패치 | KPTI (Kernel Page Table Isolation) | Retpoline, 마이크로코드 업데이트 |
| 성능 영향 | 패치 시 5-30% 성능 저하 | 패치 시 2-10% 성능 저하 |
현실적 영향: 이 취약점 패치 후 클라우드 서비스 제공자들의 서버 성능이 5-30%까지 저하되었다. 보안을 위해 성능을 희생해야 하는 뼈아픈 사례이다. CPU의 핵심 최적화 기법이 동시에 보안 구멍이라니, 하드웨어 설계의 딜레마를 잘 보여준다.
핵심 정리
| 개념 | 핵심 내용 | 왜 중요한가? |
|---|---|---|
| 브릿지 칩셋 진화 | 노스브릿지 기능이 CPU에 흡수, 현재는 PCH만 존재 | 메모리 접근 레이턴시 대폭 감소 |
| DirectX | GDI를 우회하여 GPU에 직접 접근하는 API | 실시간 그래픽 렌더링 가능, Windows 자체가 DirectX 기반 |
| DMA | CPU 개입 없이 I/O 장치가 RAM에 직접 접근 | CPU 해방 → 연산 효율 극대화, IOCP의 성능 비밀 |
| East-West 트래픽 | 같은 호스트 VM 간 통신은 RAM 카피로 처리 | 클라우드에서 서비스 배치 전략에 영향 |
| 캐시 메모리 | L1/L2(코어별) → L3(공유) 계층 구조 | CPU와 RAM의 50배 속도 차이를 완충 |
| 예측 실행 | CPU가 필요한 데이터를 미리 프리페치 | 캐시 적중률 90%+ 달성의 핵심 |
| PIM | RAM 내부에서 전처리 연산 수행 | 데이터 이동 병목 근본 해결, 차세대 기술 |
| 멜트다운/스펙터 | 예측 실행의 캐시 흔적을 이용한 사이드채널 공격 | 하드웨어 수준 보안 위협, 패치 시 성능 저하 |
다음 챕터 예고: Chapter 03에서는 프로세스(Process)와 스레드(Thread)의 개념, 프로세스 생성과 소멸, 그리고 컨텍스트 스위칭(Context Switching)에 대해 알아본다.