io_uring은 리눅스 5.1에서 도입된 비동기 I/O 인터페이스로, 시스템 콜 오버헤드를 극적으로 줄입니다. 기존 epoll, AIO의 한계를 해결하며 고성능 서버의 새로운 표준이 되고 있습니다.


기존 I/O 모델의 한계

모델한계
Blocking I/O스레드 하나가 하나의 I/O에 묶임
Non-blocking + epoll이벤트 알림은 비동기지만 실제 I/O는 동기적 시스템 콜
POSIX AIOAPI가 불편하고 내부적으로 스레드 풀 사용
Linux AIODirect I/O만 지원, 버퍼 I/O 불가

io_uring 구조

공통 문제: 매번 시스템 콜을 호출해야 함 → 유저-커널 전환 비용


io_uring의 핵심 아이디어

두 개의 링 버퍼 를 유저-커널 공간에서 공유하여, 시스템 콜 없이 I/O를 요청하고 완료를 확인합니다.

PLAINTEXT
유저 공간              공유 메모리              커널 공간
┌──────────┐     ┌────────────────┐     ┌──────────┐
│          │     │ SQ (제출 큐)     │     │          │
│ 애플리케이션│────→│  요청1, 요청2... │────→│ 커널이    │
│          │     │               │     │ 처리      │
│          │     ├────────────────┤     │          │
│          │←────│ CQ (완료 큐)    │←────│ 결과를    │
│          │     │  결과1, 결과2... │     │ 넣음     │
└──────────┘     └────────────────┘     └──────────┘

시스템 콜 없이 SQ에 요청을 넣고, CQ에서 결과를 읽음!

두 개의 링 버퍼

  • SQ (Submission Queue): 애플리케이션이 I/O 요청을 넣는 큐
  • CQ (Completion Queue): 커널이 완료된 결과를 넣는 큐

두 큐 모두 mmap으로 유저-커널 공유 → 데이터 복사 없음


동작 흐름

PLAINTEXT
1. io_uring_setup(): 링 버퍼 초기화 (한 번만)

2. 요청 제출:
   SQE(Submission Queue Entry)를 SQ에 추가
   → 시스템 콜 불필요 (메모리 쓰기만)

3. 커널에 알림 (선택적):
   io_uring_enter() 또는 SQPOLL 모드에서는 자동

4. 완료 확인:
   CQ에서 CQE(Completion Queue Entry) 읽기
   → 시스템 콜 불필요 (메모리 읽기만)

SQPOLL 모드

커널 스레드가 SQ를 지속적으로 폴링합니다. 시스템 콜이 완전히 제거 됩니다.

PLAINTEXT
일반 모드:     앱 → SQ 쓰기 → io_uring_enter() → 커널 처리
SQPOLL 모드:   앱 → SQ 쓰기 → 커널 스레드가 자동 감지 → 처리
                          시스템 콜 제로!

코드 예시

C
#include <liburing.h>

struct io_uring ring;

// 초기화
io_uring_queue_init(256, &ring, 0);  // 큐 깊이 256

// 읽기 요청 제출
struct io_uring_sqe *sqe = io_uring_get_sqe(&ring);
io_uring_prep_read(sqe, fd, buf, buf_len, offset);
io_uring_sqe_set_data(sqe, user_data);  // 사용자 데이터 연결
io_uring_submit(&ring);

// 완료 대기
struct io_uring_cqe *cqe;
io_uring_wait_cqe(&ring, &cqe);
int result = cqe->res;  // 읽은 바이트 수
io_uring_cqe_seen(&ring, cqe);

// 정리
io_uring_queue_exit(&ring);

io_uring vs epoll

항목epollio_uring
I/O 제출시스템 콜 (read/write)링 버퍼에 쓰기
완료 확인시스템 콜 (epoll_wait)링 버퍼에서 읽기
시스템 콜 수많음최소 (SQPOLL이면 0)
지원 I/O 유형소켓 중심파일, 소켓, 타이머 등 모두
배치 처리제한적여러 I/O를 한 번에 제출

활용 사례

  • 고성능 스토리지: RocksDB, ScyllaDB 등이 io_uring 도입으로 I/O 처리량 향상
  • 웹 서버: 차세대 웹 서버들이 io_uring 기반으로 전환 중
  • 데이터베이스: PostgreSQL이 io_uring 지원 작업 진행

핵심 포인트

  • io_uring이 빠른 이유: 공유 링 버퍼로 시스템 콜 오버헤드 제거, 배치 처리
  • epoll과의 차이: epoll은 이벤트 알림만 비동기, 실제 I/O는 동기 시스템 콜 / io_uring은 I/O 제출도 비동기
  • SQPOLL 모드: 커널 스레드 폴링으로 시스템 콜 완전 제거 → CPU를 소비하지만 최저 지연

정리

io_uring은 리눅스 비동기 I/O의 게임 체인저입니다. 공유 링 버퍼로 시스템 콜 오버헤드를 제거하고, 배치 처리와 SQPOLL로 극한의 성능을 제공합니다. 고성능 서버 개발자라면 반드시 알아야 할 기술입니다.

댓글 로딩 중...