한양과학기술고등학교

에듀테크 프로젝트 대시보드

제로카피 (Zero-Copy)

제로카피 (Zero-Copy)

디스크의 파일을 네트워크로 그대로 흘려보낼 때, CPU가 데이터를 몇 번이나 베껴 쓰는가 — read()+write()부터 sendfile(), DMA 스캐터-개더까지.

🎯 한 줄 요약

"파일을 소켓으로 보낸다"는 단순한 일에도 전통적 방식은 데이터를 커널 버퍼 ↔ 유저 버퍼 사이로 CPU가 직접 2번 복사하고, read/write 호출마다 유저↔커널 모드를 오간다. 제로카피는 유저 공간을 건너뛰어 이 CPU 복사와 모드 전환을 없앤다. 대용량 파일 전송 서버(nginx, Kafka, 동영상 스트리밍)의 핵심 최적화.

1. 데이터가 지나는 5개의 정거장

파일 하나를 클라이언트에게 전송할 때 데이터는 아래 다섯 구간을 통과한다. 주황색은 커널 공간, 하늘색은 유저 공간, 회색은 하드웨어(DMA 엔진이 담당)이다.

DMA 복사 (CPU 안 씀 — 하드웨어가 처리) CPU 복사 (CPU가 바이트를 직접 옮김 — 낭비)

2. 인터랙티브 데모 — 4가지 전송 방식 비교

하드웨어💽디스크
(파일)
커널📥Read Buffer
(page cache)
유저📦App Buffer
커널📤Socket Buffer
하드웨어🌐NIC
(랜카드)
64K
유저 모드현재: 유저 모드커널 모드
0CPU 복사
0DMA 복사
0컨텍스트 스위치

3. 왜 CPU 복사가 아깝나?

DMA(Direct Memory Access) 엔진은 CPU 없이 장치 ↔ 메모리 사이 데이터를 옮긴다. 그동안 CPU는 다른 일을 할 수 있다. 반면 커널 버퍼 → 유저 버퍼 복사는 CPU가 memcpy로 한 바이트씩 옮기는 순수 오버헤드다. 1GB 파일이면 그 큰 메모리를 두 번 왕복시키느라 CPU 사이클과 메모리 대역폭, 캐시가 통째로 낭비된다. 게다가 read()/write() 시스템 콜마다 유저→커널→유저로 모드를 전환(레지스터 저장·복원)해야 한다.

4. 단계별로 뜯어보기

① read() + write() — 복사 4번, 컨텍스트 스위치 4번

// 가장 흔한 코드. 그러나 데이터가 유저 공간을 왕복한다 read(fileFd, buf, len); // ① DMA: 디스크→Read Buffer ② CPU: Read Buffer→App Buffer write(sockFd, buf, len); // ③ CPU: App Buffer→Socket Buffer ④ DMA: Socket Buffer→NIC

복사 4회(DMA 2 + CPU 2), 모드 전환 4회. App Buffer는 데이터를 그냥 통과시키기만 할 뿐 아무 일도 안 한다.

② mmap() + write() — CPU 복사 1번으로

파일을 mmap으로 프로세스 주소 공간에 매핑하면, 유저 버퍼가 곧 커널 Read Buffer를 가리킨다. 디스크→App 복사가 사라져 CPU 복사가 1번(Read Buffer→Socket Buffer)으로 준다. 대신 매핑 비용이 있고, 전송 중 다른 프로세스가 파일을 자르면 SIGBUS가 날 수 있다.

③ sendfile() — 유저 공간을 완전히 건너뛴다

sendfile(sockFd, fileFd, &offset, len); // 커널 안에서 Read Buffer → Socket Buffer 로 바로 넘김. 유저 공간 경유 없음

시스템 콜 1번 → 컨텍스트 스위치 2번. 복사는 3회(DMA 2 + 커널 내부 CPU 1). Java의 FileChannel.transferTo()가 내부적으로 이걸 쓴다.

④ sendfile() + DMA 스캐터-개더 — 진짜 제로카피 (CPU 복사 0)

NIC가 scatter-gather DMA를 지원하면, 커널은 Socket Buffer에 데이터를 복사하는 대신 "Read Buffer의 어디부터 몇 바이트"라는 디스크립터(위치·길이)만 적어둔다. 그러면 NIC의 DMA 엔진이 Read Buffer에서 직접 데이터를 긁어모아(gather) 전송한다. CPU 복사 0회, 복사는 DMA 2회뿐. 이것이 리눅스 2.4+의 진짜 zero-copy다.

5. 방식별 비교

방식CPU 복사DMA 복사컨텍스트 스위치유저 공간 경유
read() + write()224O
mmap() + write()124매핑
sendfile()122X
sendfile() + SG-DMA022X

6. 실제로 어디에 쓰이나

참고: Linux man 2 sendfile, man 2 splice · IBM Developer "Efficient data transfer through zero copy".