File.read(fileDesc, buf, len);
Socket.send(socket, buf, len);
위 코드는 디스크의 데이터를 네트워크를 통해 전달하는 코드입니다. 구체적인 내부 동작원리는 다음과 같습니다.
- 운영체제가 디스크상의 파일을 읽어 데이터를 커널 공간(kernel space)의 읽기 버퍼(read buffer)에 저장합니다.
- 애플리케이션은 읽기 버퍼에 저장된 데이터를 사용자 공간(use space) 버퍼(application buffer)로 읽어 들입니다.
- 애플리케이션은 사용자 공간 버퍼로 읽어 들인 데이터를 소켓 버퍼에 씁니다.
- 운영체제는 소켓 버퍼의 데이터를 NIC(Network Interface Card) 버퍼에 복사합니다. 해당 데이터는 네트워크를 통해 전송됩니다.
디스크상의 파일을 읽어 네트워크를 통해 전송하는 과정까지 총 몇 번의 데이터 복사가 일어났을까요? "디스크 -> 읽기 버퍼 -> 사용자 공간 -> 소켓 버퍼 -> NIC 버퍼"를 거치며 총 4번의 데이터 복사가 수행됐습니다. 그리고 사용자 공간에 위치한 애플리케이션이 운영체제와 상호작용하기 위해 시스템 콜을 여러 번 호출해야 합니다. 이 과정을 단순화하고 더 효율적으로 수행하는 방법은 없을까요? 이를 가능케 하는 시스템콜이 바로 sendfile 시스템콜입니다.
sendfile
Linux manual page의 sendfile 설명은 아래와 같습니다.
sendfile - transfer data between file descriptors
즉, 파일 디스크립터 간에 데이터를 전달할 수 있도록 하는 시스템콜입니다. sendfile 시스템콜을 사용하면 사용자 공간의 개입 없이도 파일 디스크립터 간 데이터를 복사할 수 있습니다. 즉, 위에서 살펴본 2번과 3번(read, write 시스템 콜을 활용)을 생략할 수 있습니다.
참고로 이와 유사한 기능을 제공하는 Java API도 존재합니다. java.nio.channels.FileChannel의 transferTo() 메서드는 해당 channel에서 쓰기 가능한 byte channel로 애플리케이션의 간섭 없이 데이터를 복사하는 기능을 제공합니다.
Kafka's zero-copy
Kafka는 zero-copy 기법을 활용해 consumer의 읽기 요청에 대한 처리 속도를 향상합니다. 이 기법은 kafka 문서에서 자세히 설명하고 있습니다. 다수의 consumer는 kafka topic의 데이터를 요청하여 조회할 수 있습니다. 이때 kafka는 zero-copy를 활용하여 최적화를 수행합니다. 이 기법은 consumer가 데이터를 요청할 때마다 데이터를 사용자 공간(user space)에 복사하지 않습니다. 대신 sendfile를 활용해 읽기 버퍼에 데이터를 저장하고, 데이터를 사용자 공간으로 복사하지 않고 전달함으로써 consumer가 토픽의 메시지를 소비하는 속도를 사실상 네트워크의 한계 지점까지 높일 수 있습니다.
요약
이번 포스팅을 통해 sendfile 시스템콜이 무엇인지와 kafka에서는 sendfile 시스템콜을 활용해 어떻게 I/O 요청을 어떻게 효율적으로 처리하는지 살펴봤습니다. sendfile은 사용자 공간에 데이터가 복사되는 과정을 생략하고, 시스템 콜이 호출되는 횟수를 줄임으로써 file descriptor 간의 데이터 이동 성능을 높입니다.
Reference
https://man7.org/linux/man-pages/man2/sendfile.2.html
https://man7.org/linux/man-pages/man2/copy_file_range.2.html
'Operating System' 카테고리의 다른 글
[OS] TCP 송수신 Deep Dive (0) | 2023.10.20 |
---|