Direct Memory Access
여태까지 컴퓨터는 직접 메모리에 대해 접근하지 않고 가상 메모리를 사용하는 것으로 배웠는데, 만약 직접 메모리 접근이 있다면?
- DMA(Direct Memory Access)란?
- 왜 DMA가 필요한가?
- 기본 구성 요소
- 동작 흐름 (x86 PC 예시)
- 전송 모드
- 현대 시스템에서의 DMA 변화
- 장점과 단점 요약
- 실무 예시
- 운영체제 관점
DMA(Direct Memory Access)란?
CPU를 거치지 않고 I/O 장치가 메인 메모리와 직접 데이터 블록을 주고받게 해 주는 하드웨어 메커니즘입니다. OS입장에서 보면 “장치 ↔ 메모리 복사”라는 반복적이고 대용량인 일을 CPU로부터 떼어 내어 병렬성과 CPU 자유도를 확보하는 핵심 최적화 기법이다.
왜 DMA가 필요한가?
| 전통적 PIO(Programmed I/O) | DMA가 주는 이점 |
|---|---|
| CPU가 I/O 레지스터를 통해 바이트 단위로 데이터를 옮기면 레이턴시·오버헤드가 큼 | 한 번에 수십~수천 바이트 블록을 메모리로 직행 ⇒ CPU cycles 절약 |
| 빠른 장치(SSD, 10 GbE NIC 등)는 CPU 속도보다 I/O 대역폭이 큼 → CPU가 “목”이 됨 | DMA 엔진이 버스 마스터로 동작해 메모리 버스를 직접 차단·사용 |
| 멀티코어 시대에 CPU는 계산에 집중하고 싶음 | CPU는 전송 시작/종료만 관리 → I/O wait 시간 줄고, 캐시 miss도 감소 |
- 덕분에 대역폭·지연시간 모두 절감, 특히 디스크 I/O, 네트워크 카드, 오디오 스트리밍처럼 폭이 넓고 반복적인 전송에 필수적이다
기본 구성 요소
DMA 컨트롤러(DMAC)
전용 칩(고전 ISA-DMA) 또는 주변장치 내부 DMA 엔진(PCIe, NVMe 컨트롤러)
소스/목적지 물리 주소, 전송 길이, 모드, 제어 플래그 등을 저장하는 레지스터(메모리 맵 레지스터) 보유
여러 채널이 있으면 동시에 여러 장치 지원
시스템 버스
CPU ↔ 메모리 ↔ DMA 컨트롤러가 공유
CPU와 DMAC가 동시에 쓰지 않도록 버스 중재(bus arbitration) 를 수행
메모리 버스
- DMAC가 버스 마스터(master) 권한을 요청(Bus Request, BR) → 메모리와 직접 주고받음
주변장치(Device)
- 디스크, NIC, GPU, 오디오 코드 … 데이터가 실제로 존재/필요한 곳
CPU & OS
- 전송 파라미터를 DMAC 레지스터에 써 주고, 완료 인터럽트(DMA done)를 처리
I/O 장치(버스 마스터 가능 장치)
- PCIe NIC, NVMe SSD, GPU 등은 자체 DMA 엔진을 내장해 버스 마스터 가 되기도 한다
DMA 레지스터 SRC, DST, LEN
| 레지스터 | 역할 | 일반적 크기 | 세부 사항 |
|---|---|---|---|
| SRC (Source Address) | 읽어 올 원본 주소. 장치 → RAM 전송이면 장치 쪽 FIFO 주소, RAM → 장치 전송이면 RAM의 시작 주소 | 32 bit(SoC) 또는 64 bit(서버) | * 버스트·비버스트 여부, 버스트 길이에 따라 자동 증가(inc) 여부 선택 플래그 존재 * Scatter-Gather 모드에선 “다음 표(list) 항목” 주소로 해석되기도 함 |
| DST (Destination Address) | 쓰기 대상 주소 | 32/64 bit | * 자동 증가/고정 선택 가능 (예: 오디오 DAC 같은 스트리밍 장치는 고정 주소, 메모리 버퍼는 증가) |
| LEN (Length / Count) | 총 바이트(혹은 워드) 수 | 16 bit(65 kB) ~ 32 bit(4 GB) 이상 | * 전송이 끝날 때마다 LEN–; 0 → 인터럽트 발생 * 어떤 컨트롤러는 LEN 대신 COUNT×DATA_WIDTH 형태 사용 |
요약 : SRC·DST는 “어디서 → 어디로”를, LEN은 “얼마나”를 알려주는 좌표 + 거리 정보라고 생각하면 된다
동작 흐름 (x86 PC 예시)
| 단계 | 신호선 & 동작 |
|---|---|
| ① CPU 설정 | DMAC 레지스터에 <소스, 목적지, 길이, 모드> 기록 후 Bus Request(BR) 활성화 |
| ② 버스 획득 | CPU가 Bus Grant(BG)로 응답 → CPU 파이프라인이 버스를 놓고 다른 연산(레지스터 계산 등) 수행 |
| ③ 데이터 전송 | DMAC가 메모리 ↔ 장치 간 버스 싸이클 생성 |
| ④ 완료 인터럽트 | 전송 길이가 0이 되면 DMAC가 CPU에 DMA 완료 인터럽트. CPU는 이후 버스 재획득 |
중요 : DMAC가 버스를 잡은 동안 메모리 전체 버스 주도권을 갖기 때문에 CPU는 RAM을 전혀 접근하지 못한다. 개별 “주소 권한”이 아닌 “버스 소유권”의 문제다. 이 때문에 짝수 클럭마다 한 워드만 빼앗고 바로 돌려주는 Cycle Stealing 모드가 등장했다
동작 흐름 더 자세히

1 단계 - CPU까 준비만 하고 손을 뗀다
전송 파라미터 설정
SRC, DST, LEN, MODE을 레지스터에 값을 써 넣습니다
마지막으로 Enable 플래그를 1로 두면 DMAC가 “이제 내가 처리할게!”하고 대기
버스 요청(BR 신호)
- DMAC는 버스 중재기(arbiter)에게 “버스 좀 빌려주세요” 라고 손을 듦
2 단계 - 버스 소유권 교환
CPU → DMAC ‘Bus Grant(BG)’
CPU가 진행 중인 버스 싸이클을 마치면 주소·데이터 라인을 tristate해 두고 계산 같은 비-버스 작업에 집중함
tristate란, 여러개의 마스터(CPU, DMAC, GPU…)가 하나의 전선을 공유하고, 한 쪽이 0V, 다른쪽이 동시에 Vcc를 내보내면 단락(short) 위험을 방지하고자 버스를 넘겨받은 쪽만 드라이버를 켜고 나머지는 Hi-Z로 물러나야 함
버스가 필요 없는 작업이란, 파이프라인이 레지스터와 내부 캐시 만으로 실행 가능한 명령들
DMAC가 버스 장악
이 순간부터 RAM과 I/O 장치 사이를 오가는 모든 신호는 DMAC가 생성
CPU는 RAM을 못 만지기 때문에 캐시 miss가 나도 대기해야 함
3 단계 - 실제 데이터 이동
전송 모드에 따른 사이클
Burst/Block : LEN만큼 연속으로 → 최고속, 대신 CPU 길게 정지
Cycle-Stealing : 한 사이클씩 “훔치고” 바로 돌려줌 → 실시간 오디어/비디오
Scatter-Gather : 메모리 리스트 따라 다중 블록 자동 전송 → NVMe·NIC
주소·카운터 자동 증가
- DMAC 내부 카운터가 0이 될 때까지 SRC++, DST++, LEN–
4 단계 - 전송 완료 알림
DMA 완료 인터럽트
LEN==0 → DMAC가 IRQ를 날리고 버스 요청을 내림
CPU는 인터럽트 핸들러에서 데이터 후처리(예: 패킷 파싱, 디스크 블록 체크섬) 수행
버스 복귀
- Arbiter가 다시 CPU에 ‘Bus Grant’ → 평상시 메모리 접근 재개
전송 모드
| 모드 | 특징 | 사용 예시 |
|---|---|---|
| Single-word/Cycle-Stealing | 버스 싸이클 1 개만 “훔친” 뒤 즉시 CPU에 반환 → CPU 지연 최소 | 실시간 오디오, 비디오 스트림 |
| Block/Burst | 전체 블록(혹은 버스트) 한 번에 전송 → 가장 빠르지만 CPU 길게 정지 | SATA, SDIO, 대부분의 PCIe 장치 |
| Demand/Scatter-Gather | 장치가 필요할 때마다, 혹은 메모리 리스트 기반 다중 블록 이동 | 고성능 NIC, NVMe SSD, GPU VRAM 업로드 |
현대 시스템에서의 DMA 변화
PCIe Bus mastering - 주변장치가 메인 메모리를 완전히 “빌려” 직접 읽고/쓴다
IOMMU(DMA-Remapping) - 가상화·보안 목적. 장치가 접근 가능한 주소를 OS가 테이블로 전환 → 버퍼 오염·DMA 공격 방어
Cache coherency 문제 - CPU 캐시에 남은 더러운(line dirty) 데이터 vs DMA가 본 메모리 불일치. 해결 : dma_sync_*() - (Linux)·Cache flush, non-cacheable region, snoopint 버스.
Zero-copy - 네트워크 스택이나 GPU ↔ CPU 사이에서 “복사 없는” 파이프라인 구축 (DPDK, RDMA, CUDA cudaMemcpyAsync 등)
- 운영체제 버퍼를 추가로 “복사(copy)”하지 않고, 장치 ↔ 응용이 같은 메모리 페이지를 공유하도록 하는 기법. CPU가 불필요한 memcpy()를 안 하므로 지연과 캐시오염↓
RDMA(Remode DMA) - NIC가 원격 호스트의 메모리에까지 DMA 쓰기/읽기를 수행. 커널 네트워크 스택을 우회하여 μs 단위 지연시간을 제공한다. 고속 HPC, 데이터베이스 복제에서 필수
장점과 단점 요약
장점
- CPU 사용률 감소, 전력 효율 ↑
- 대역폭 활용 극대화(PCIe Gen4 x4 ≈ 8 GB/s 급)
- 실시간 스트리밍 지원(오디오 스터터링 방지 등)
단점
- 하드웨어·드라이버 복잡도 증가
- 버스 우선순위 조정 실패 시 CPU 지연 가능
- 캐시 일관성, 보안("DMA 공격") 이슈 처리 필요
- IOMMU로 보완
실무 예시
| 장치 | DMA 활용 |
|---|---|
| SSD/NVMe | 플래시 컨트롤러가 OS 버퍼를 읽어 와 NAND로 쓰기, 반대 방향 읽기 |
| 10 GbE NIC | 패킷을 커널 버퍼나 XSP ring으로 직접 놓고, 완료 시 인터럽트 |
| GPU | 대용량 텍스처/버퍼를 PCIe로 복사하거나, VRAM↔CPU RAM pinned transfer |
| Audio Codec | PCM 버퍼를 주기적으로 DMA → DAC, 실시간 재생 |
| USB 컨트롤러 | 호스트 메모리의 전송 링(Transfer Ring)을 DMA 로 순회 |
운영체제 관점
Linux : dma_map_single(), dma_alloc_coherent(), struct dma_async_tx_descriptor, DMAengine 프레임워크
Windows : WdfDmaTransaction*, KeFlushIoBuffers(), Scatter/Gather 목록 지원
RTOS(FreeRTOS, Zephyr) : MCU마다 별도의 DMAMUX & HAP API 제공