본문으로 건너뛰기

© 2026 Molayo

GeekNews헤드라인2026. 06. 22. 06:42

Linux의 epoll과 io_uring 비교

요약

Linux의 네트워크 I/O 성능 최적화를 위해 epoll과 io_uring을 비교 분석합니다. CPU 고정, 소켓 옵션 활용, io_uring의 성능 이점과 보안 이슈 및 커널 지원 현황을 다룹니다.

핵심 포인트

  • io_uring은 epoll 대비 약 20% 높은 처리량을 보일 수 있음
  • 성능 극대화를 위해 CPU 고정 및 소켓 정렬 기술이 중요함
  • io_uring은 보안 취약점 이슈로 인해 사용 시 주의가 필요함
  • 최신 커널에서는 cBPF 지원을 통해 io_uring 보안 문제를 완화 중임

GitHub 저장소 https://github.com/sibexico/TinyGate를 아주 잠깐 봤는데, CPU 고정은 아직 안 쓰는 것 같음
스레드와 리슨 소켓을 CPU에 고정하고, sockopt SO_INCOMING_CPU를 쓰면 성능을 조금 더 끌어낼 수 있음
나가는 소켓까지 CPU 정렬하면 꽤 큰 향상이 있을 텐데, 알기로는 이를 위한 좋은 API가 없음. Linux에는 호환 NIC용 트래픽 조향/흐름 조향 API가 있고, NIC가 쓰는 해시가 무엇인지 알면—아마 Toeplitz일 가능성이 큼—백엔드로 가는 소스 포트를 잘 골라 해시가 맞게 만들 수 있음
목표는 프록시가 CPU 간 통신 없이 패킷을 처리하게 만드는 것임

저장소의 v0와 v1은 거의 처음부터 다시 쓴 완전히 다른 구현이고, 지금은 세 번째 구현을 작업 중이며 아마 마지막일 것 같음. 아키텍처 선택도 완전히 달라졌음

참고로 splice(2)가 구현되어 있어서 uring으로 sendfile 비슷한 방식을 쓸 수 있음. sendfile만큼 쓰기 편하진 않지만 거의 비슷하게 동작할 것임

DPDK로 만들면 훨씬 복잡해지겠지만, 성능에서는 nginx를 압도할 기회가 생김
FPGA에서 돌리게 만들면 더 복잡해짐
교훈은 성능을 위해서는 추상화를 뜨거운 칼로 버터 자르듯 뚫고 지나가는 태도가 필요하지만, 그만큼 모든 것이 더 어려워진다는 것임. 소켓과 연결당 스레드 방식은 네트워크가 CPU에 비해 매우 느리던 시절에는 좋은 접근이었고, 오늘날에도 여전히 가장 단순한 접근인 경우가 많음

최근 Asio를 직접 만든 epoll 이벤트 루프로 바꿨더니 RPS가 약 16% 좋아졌음. 적당한 규모의 SQL 서버에서 나온 결과라, 잘 포장된 라이브러리를 쓸 때는 조심해야 함

데이터베이스 서버에서 Asio의 epoll 백엔드를 io_uring으로 바꿨더니 CPU 사용률이 확 올라갔음. 사용 방식과 이벤트 코드에 어떻게 통합했는지에 따라 달라질 가능성이 큼

Boost는 너무 불편함. 빌드하고 쓰기 까다로운 거대한 동적 라이브러리들임. 이미 CMake를 쓰고 있었는데도, Boost를 설치해서 발견 가능하게 만드는 과정이 매우 성가셨음. 다만 Mac에서 겪은 일임

2050년쯤 되면 Linux에서 소켓을 폴링하는 방법이 20가지쯤 있을 것 같음

맞음, io_uring 안에서도 그렇다. 더 빠르게 가려고 io_uring 단발 방식이 나오고, 그다음엔 다중 발사 방식까지 생김

맞음, io_uring은 epoll보다 확실히 빠름. 내 경우에는 io_uring이 초당 요청 수 기준으로 약 20% 빨랐던 것 같음
문제는 커널에서 명시적으로 켜야 하고, 보안 이유로 거의 모든 곳에서 비활성화되어 있다는 점임. 커널과 사용자 공간 사이에 직접 메모리 공유가 있는 것 같은데, 꽤 꺼림칙함. 최근 io_uring을 노린 익스플로잇도 여러 번 있었음
그래서 Go처럼 가능한 한 최고 성능을 노리는 엔지니어링 프로젝트도 io_uring을 합리적인 기본값으로 깊게 넣지는 않음. 위험을 감수하고 싶다면 좋아하는 언어에서 직접 돌릴 수는 있음. 더 빠르지만 대가는 잠재적인 익스플로잇 가능성임

비활성화되는 주된 이유는 이제 해결됨. 최신 RC에 cBPF 지원이 들어가서, 전부 끄는 대신 실행 가능한 작업을 제한할 수 있게 됨

경우에 따라 다름. epoll이 아니라 poll로 만든 내 POSIX 방식 io_uring 에뮬레이션이 io_uring보다 빨랐던 적도 있음. 다만 큰 무복사 버퍼에서는 io_uring이 최고임 io_uring은 비동기 I/O가 아니어도 유용함. 예를 들어 mkdir 후 그 디렉터리를 여는 식의 연산 체인을 단일 원자적 작업처럼 구현할 수 있음
네트워킹에서 초당 패킷 수를 최대화하려 하면 커널 한계[1]에 매우 빨리 부딪히고, 결국 GSO/GRO 같은 기능을 활용하거나 네트워크 스택을 완전히 우회해야 함
1: https://github.com/axboe/liburing/discussions/1346

RHEL 9와 10은 이제 기본적으로 io_uring을 완전히 지원함. 아주 최근 일이지만, 이로써 많은 기업 Linux 설치 환경이 포함됨. Gemini는 Ubuntu와 SuSE도 지원한다고 “말했지만”, 이를 증명할 링크는 주지 않았음 https://access.redhat.com/solutions/4723221
Go도 지원을 재검토해야 함. 한번 해볼 만함

Go 같은 프로젝트라면 런타임 시작 시 한 번만 io_uring 기능 감지를 하는 선택지도 있지 않을까? 익스플로잇은 io_uring을 쓰기로 한 프로그램만의 문제가 아니라 전체 OS의 문제 아닌가?

모든 종류의 폴링 모드 네트워킹은—RDMA, DPDK, io_uring—결국 메모리 격리를 사용자가 책임져야 하는 성격이 강함
다만 io_uring의 경우 링이 커널 안에 있어서 사용자가 할 수 있는 게 많지 않음
LLM 덕분에 앞으로 나아질 거라 기대하지만, 해결하기 어려운 문제임. 커널 자체에서 처리하기도 매우 어렵고, 사람들도 이를 튜닝하는 법을 제대로 이해하지 못하는 경우가 많음

AI 자동 생성 콘텐츠

본 콘텐츠는 RSS: GeekNews (한국어)의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0