서버가 파일을 전송할 때 커널 내부에서는 무슨 일이 일어날까? I/O 성능의 핵심인 Zero-copy와 TCP 속도를 결정짓는 Sliding Window 메커니즘을 파헤칩니다.
우리가 흔히 말하는 웹 서버(Web Server) 는 OS 입장에서는 하나의 프로세스(Process) 에 불과합니다. 그리고 이 프로세스가 소켓(Socket)을 통해 데이터를 내보내는 행위는, 결국 파일(File)을 읽고 쓰는 I/O 작업의 연장선입니다.
서버의 디스크에 잠들어 있는 파일이 랜선을 타고 나가기까지, 커널 내부에서 벌어지는 '데이터의 이동' 과 'TCP의 기다림' 을 단계별로 분석해 봅니다.
서버가 클라이언트에게 파일을 전송하려면, 하드디스크(HDD/SSD)에 있는 데이터를 메모리로 읽어와야 합니다. 이 과정에서 데이터는 커널 영역과 유저 영역을 넘나들며 여러 번 복사됩니다.
일반적인 read()와 write() 시스템 콜을 사용할 때 발생하는 과정입니다.
하드웨어 드라이버가 디스크의 데이터를 읽어 커널 영역의 PageCache로 복사합니다. (CPU 개입 없음)
커널은 PageCache에 있는 데이터를 유저 영역의 애플리케이션 버퍼로 복사합니다. * 문제점: 이 과정에서 **컨텍스트 스위칭(Context Switching)**이 발생하여 CPU 자원을 소모합니다.
애플리케이션이 send()를 호출하면, 유저 버퍼의 데이터를 다시 **커널 영역의
소켓 버퍼(Socket Buffer)**로 복사합니다.
소켓 버퍼의 데이터가 네트워크 인터페이스 카드(NIC)로 전달되어 전송됩니다.
sendfile)만약 읽어온 파일 내용을 애플리케이션이 가공(압축, 암호화 등)할 필요 없이 그대로 보낸다면? 유저 영역을 갔다 오는 것은 낭비입니다.
리눅스의 sendfile() 같은 시스템 콜을 사용하면, 데이터가 PageCache에서
곧바로 소켓 버퍼로 이동(혹은 참조)합니다. * 효과: 불필요한 데이터 복사와
컨텍스트 스위칭이 사라져, CPU 부하가 줄고 전송 속도가 획기적으로 향상됩니다.
Nginx, Kafka 같은 고성능 서버들이 이 방식을 채택합니다.
소켓 버퍼에 안착한 데이터는 네트워크 선로를 타기 위해 잘게 쪼개지고 포장됩니다.
TCP가 UDP보다 느린 이유는 **"확실하게 보내기 위해 기다리기 때문"**입니다. 이 기다림의 미학을 **슬라이딩 윈도우(Sliding Window)**라고 합니다.
서버는 데이터를 무작정 보내지 않습니다. **수신측(Client)이 받아들일 수 있는 양(Window Size)**만큼만 보냅니다.
네트워크 회선은 1Gbps인데 실제 속도는 10Mbps도 안 나오는 경우가 있습니다. 범인은 의외로 **수신측(Client)**일 수 있습니다.
TCP는 OS가 관리하지만, 최종적으로 그 데이터를 가져가는 건 **애플리케이션(Process)**입니다.
네트워크 지연이 발생할 때, 무조건 서버나 회선 탓을 하기 전에 **수신측 시스템의 부하(CPU, Disk I/O)**를 점검해야 합니다. 수신측이 데이터를 빨리빨리 소화해주지 못하면, 송신측은 아무리 성능이 좋아도 기다릴 수밖에 없는 것이 TCP의 운명입니다.