Linux I/O 성능 혁신 - io_uring

2019년에 소개된 io_uring은 Linux 커널을 위한 비동기 I/O 인터페이스입니다. 기존 인터페이스인 epoll/kqueue와 aio 대비하여 뛰어난 성능과 간결하고 직관적인 API 사용성을 제공합니다. io_uring은 비동기 I/O, 시스템 콜의 다중 배치 처리, 유연한 버퍼 관리 등을 통해 Linux I/O 모델의 중요한 발전 중 하나로 인정받고 있습니다.

주요 특징

동작 원리

io_uring은 사용자와 커널 간의 공유 메모리 내에 원형 큐 (queue rings)를 활용합니다. 제출큐 (Submission Queue)와 완료큐 (Completion Queue)로 구성된 이러한 큐를 통해 사용자는 여러 개의 I/O 작업을 스케줄링할 수 있습니다. 커널은 스케줄된 작업을 처리하고 그 결과를 완료큐에 반환하여 사용자가 결과를 처리할 수 있도록 합니다.

우수한 성능

io_uring은 사용자와 커널 간의 공유 메모리를 이용하여 Zero-Copy I/O를 지원합니다. 또한 사용자가 I/O 작업을 스케줄링할 때 필요한 모든 정보를 커널에 전달하고 커널 내에서 작업 완료를 관리함으로써 추가적인 사용자/커널 간 컨텍스트 스위칭(context switching)을 최소화합니다. 이로써 전통적인 동기 I/O에 비해 더 낮은 오버헤드를 가지게 됩니다.

간결한 인터페이스

기존의 epoll에서는 여러 개의 입출력 작업(multiplexing)을 위해 파일 디스크립터의 상태를 필터링해야 했습니다. 반면 io_uring은 더 간결한 사용 방식을 제공합니다. 사용자는 io_uring_enter() 함수를 호출하여 시스템 콜을 커널에 스케줄링하고 결과를 처리합니다. 이러한 방식은 Polling과 Interrupt-driven I/O를 모두 지원합니다.

epoll을 사용한 설정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
...

int epoll_fd = epoll_create1(0);
struct epoll_event event;
event.events = EPOLLIN; // 읽기 이벤트 설정
event.data.fd = sockfd; // 관심 있는 파일 디스크립터 설정

epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sockfd, &event);

struct epoll_event events[MAX_EVENTS];
int ready_fds = epoll_wait(epoll_fd, events, MAX_EVENTS, timeout);
for (int i = 0; i < ready_fds; i++) {
int fd = events[i].data.fd;
if (events[i].events & EPOLLIN) {
// 읽기 이벤트 처리
// ...
}
}

io_uring을 사용한 설정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <liburing.h>

...

struct io_uring ring;
io_uring_queue_init(QUEUE_DEPTH, &ring, 0);

struct io_uring_sqe *sqe = io_uring_get_sqe(&ring);
io_uring_prep_read(sqe, sockfd, buf, buflen, offset);
io_uring_submit(&ring);

struct io_uring_cqe *cqe;
int ret = io_uring_wait_cqe(&ring, &cqe);
if (ret >= 0) {
if (cqe->res >= 0) {
// 성공
} else {
// 에러
}
io_uring_cqe_seen(&ring, cqe);
}

생각

libuv에서도 io uring API를 사용할 수 있게 되었습니다. 현재는 파일 연산에 한정되어 있지만, io_uring을 지원하는 커널의 경우 파일 연산 처리량이 최대 8배까지 증가하는 것으로 확인되었습니다. 파일 연산이 많거나 파일과 네트워크 I/O 워크로드가 혼합된 시스템에서는 io_uring의 사용으로 비약적인 성능 향상이 가능할 것으로 보입니다. 이러한 잠재력을 고려하여 io_uring을 활용하는 것이 유용할 것입니다.

References

Share