CS Insights

리눅스 비동기 I/O의 진화 패러다임: io_uring과 커널/유저 스페이스 공유 링 버퍼 매커니즘

리눅스 비동기 I/O의 진화 패러다임: io_uring과 커널/유저 스페이스 공유 링 버퍼 매커니즘
막대한 양의 이미지와 비디오 파일을 동시에 백업해야 하는 스토리지 서버 최적화 업무를 부여받았을 때, SSD의 스펙상 IOP(초당 입출력 연산)는 30만에 달하는데도 리눅스 서버 커널에서는 5만을 간신히 넘기며 목이 막히는 답답한 체증을 겪었습니다. 리눅스의 AIO(Asynchronous I/O)나 epoll 기반의 멀티플렉싱 API 등은 수많은 혁신을 거듭해 왔지만 뼈아픈 한계가 존재했습니다. 파일 읽기/쓰기 시스템 콜(System Call)을 호출할 때마다 프로그램은 어쩔 수 없이 유저 모드와 커널 모드 사이를 진자 운동처럼 널뛰는 컨텍스트 스위칭 비용을 지불해야 했습니다. 1만 번의 소켓 읽기 요청은 1만 번의 모드 전환 트랜잭션을 야기하며 CPU를 쓰레기로 만들어버립니다. 이 벽을 부수기 위해 탄생한 리눅스 커널 5.1의 총아가 바로 io_uring 모델입니다. io_uring의 설계 미학은 극단적인 미니멀리즘과 쉐어드 메모리(Shared Memory)에 있습니다. 유저 스페이스와 커널 스페이스 사이에 두 개의 거대한 원형 링(SQ: Submission Queue, CQ: Completion Queue)을 뚫어놓고 서로 주소 공간을 메모리 맵핑(mmap)으로 공유해버립니다. 유저 프로그램은 더 이상 낡은 시스템 콜 함수를 일일이 부르며 대기하지 않습니다. 그냥 자기가 읽고 싶은 파일 디스크립터나 버퍼 명령을 SQ 링 버퍼에 마치 편지를 쓰듯 무더기로 밀어 넣고 돌아서버립니다. 그러면 커널 내부에 대기하던 커널 스레드는 유저 어플리케이션을 단 한 번도 귀찮게 깨우거나 중단시키지 않고 SQ를 조용히 가져가 백그라운드 디스크나 네트워크 컨트롤러에서 DMA를 통해 데이터를 퍼 올립니다. 작업이 끝나면 완성된 결과물 주소를 CQ 링 버퍼에 다시 던져둡니다. 결과적으로 어플리케이션과 커널은 단 한 번의 시스템 콜 컨텍스트 스위칭도 없이, 각자의 링 버퍼만 빙글빙글 돌며 비동기 생산자-소비자 패턴만 무한 수행하게 됩니다. 디스크 처리량이 이론상 물리적 한계 수치에 도달하며 수직 상승하던 그란 모니터 그래프는 인프라 생태계가 또 한 번 대격변의 시즌에 돌입했음을 알려준 환희의 순간이었습니다.

Related Posts