SQPOLL은 불안형 여친이다
by
gg582 · 2026-09-19 01:51:54 · 65 views
I/O Uring은 대체 뭐야?
C언어, 리눅스를 기반으로 개발할 때, 비동기를 다루다 보면 초짜 때부터 바로 나오는 녀석이 있다.
io_uring이다. 솔직히 개발도 어렵지만 이름도 꽤 마음에 들지 않는다.
그야 Urine이랑 어감이 비슷해서 더럽잖아...
각설하고, io_uring의 기본 모드인 인터럽트 주도 모드(Interrupt-driven Mode)를 먼저 알아 보고, 본론인 SQPOLL 모드와 비교해 보자.
기본 모드/인터럽트 주도 모드
Judo 모드? 좋지, 유도부는 멋있지
유저 스페이스는 먼저 커널한테 이걸 시킨다.
야, 대충
io_uring_setup()호출해서 링버퍼 만들어라.
커널은 시킨 대로 유저 공간과 커널 공간에 두 놈을 만든다.
SQ는 앱->커널로 보내는 큐다. SQE는 SQ에 작업을 제출하기 위한 양식이다. CQ는 커널->앱으로 I/O 결과를 가져 온다.
쉬운 글이니 헛소리를 좀 하겠지만 기본적으로 얘는 SSD 등이 데이터 처리를 끝내면 커널에 '나 끝났소, 이 놈 보낼 테니 끝나면 알려 주이소' 하고 우선순위에 따라 밥도 먹고 커피도 마시다가 커널에 하드웨어 인터럽트를 보낸다. 커널은 알려주기 전까지 '알아서 연락 오겠지' 라면서 눈치만 보고 있는 거다. 그니까 이렇게 알려 주는게 io_uring_enter()란 거다.
신호가 왔네? 이제 얘가 오나 보자.
들어오고 나면 커널은 적당히 밥 먹고 일 좀 하다가 '보냈나?'하고 작업을 열어 본다. 그러니까 기본적으로 '현대 시스템은 충분히 빨라서 사실상 처리 순위가 밀려 봐야 그게 그거야!'라고 하는 게 프레임워크 개발할 때는 위험할 수 있으니, 할 거 좀 하는 오버헤드가 작지는 않다.
안 그러면 내가 이런 걸 블로그로 안 썼지. CWIST 프레임워크가 쏘아 올린 공이라니까.
열어 본 작업을 수취인한테 줘야 할 거 아니냐? 그럼 얘는 HW 컨트롤러나 커널 내 작업자 스레드인 io_wqe한테 전달되어야 할 거다.
웹 프레임워크면 바로 HW로 꽂힐 일은 없지. 아마도.
작업이 안에서 돌아가는 동안에, 시스템은 다음 작업을 링 버퍼에 또 꽂을 수 있다.
그럼 알아서 이 놈 처리하고 바로 커널에서 뽑아가겠지..냐고?
뭔 알아서 뽑는다는거야? io_uring_enter()는 syscall이 아니냐?
결국 io_uring_enter()라는 콜로 인터럽트가 발생하는 지점까지는 동기식이다.
하지만 작업이 올 때까지 폴링하는 것보단 CPU도 덜 타고 균형이 좋다.
자기 생활이 있는 안정형 여친.
SQPOLL이라는게 대체 뭔데 '불안형 여친'이라고 하는건데?
얘는 쉽게 말하자면 스레드님이 졸 때만 깨우는 거다.
IORING_SETUP_SQPOLL 플래그를 설정하고 io_uring_setup()을 불러 주면 이 링만 감시하는 커널 스레드 io_uring-sq를 생성한다. IORING_SETUP_SQ_AFF 하면 스레드 자체도 특정 코어에 매어 버릴 수 있다. 그러면 작업이 이 코어 저 코어로 옮겨가거나 하지 않으니까 꼬리 지연도 좋고 성능도 좋겠지...쉽게 말하자면 앞이 알려 줘야만 일하는 전산실 동료, 뒤는 이런 비유려냐...
오빠가 커피 사러 가는 시간까지 지켜보겠다는 불안형 여친
io_uring-sq양이냐...?
솔직히 집착 성향은 남녀 없이 피곤하다. 안 겪어 봤으면 말을 말라고...
뭐 어쨌건 io_uring-sq양은 오빠만을 바라본다고 하고, IORING_SETUP_SQ_AFF까지 하면 살짝 스토커 성향도 생긴다. CPU는 타 들어간다.
조금 무섭다. 리누스 저런 사람이었어?
어쨌건, 얘는 여친 양이 잘 때만 io_uring_enter가 필요하고, 아닐 때는 SQ 요구사항에 맞게 요청 양식 SQE를 작성해서 큐에 넣는다.
tail 포인터는 전진했고, 큐에 작업은 들어갔다. 여친 양이 알아서 '오빠 지금 어디야 내 마음은 타들어가는데 어디 있어?' 해 줄 거다.
크아아아악
커널 스레드는 알아서 폴링해서 정보를 가져 오고, SQE를 낚아 채서 인출한 후 head포인터를 전진시켜서 큐를 비워 준다. 가져온 작업은 또 하드웨어 드라이버나 io_wqe 스레드에 작업을 던져 져서 처리 대기열에 들어간다.
정보를 가져 온 여친 양은 남친 군을 갈구기 위한 건덕지를 미친 듯이 수집하면서, 들어오자마자 하나씩 작업한다.
가불기 초식 ON
처리가 끝난 작업은 완료 보고 양식대로 CQ에 채워지고, tail은 또 전진한다. 앱은 주기적으로 CQ의 head와 tail을 확인하며 두 포인터 주소가 이전과 달라지기를 기다린다.
주소가 달라졌다는 것은 움직여서 도착했다는 것입니다. 그것이 링 버퍼니까(끄덕)
펀쿨섹좌냐.
처리가 끝난 작업부터 하여, 가불기 3초식....아니 카톡을 여러 번 보낼 준비를 한다. 그럼 미안하다고 하든, 성질을 내든 하겠지?
그리고 이 때, 여친이 잘 때도 연락하는 불안형 남친이 더해지면 환장의 콜라보가 생긴다.
시스템적 사고로 말하면 초고성능 앱이다. 즉 스레드가 잘 때 io_uring_enter()하면 화들짝 놀라 '오빠 이 늦은 시간에 지금 뭐하는거야 어떤 여자랑 있어' 한다는 소리다.
자는 여자까지 깨우다니 분명 최악인데, 연락은 빨랐으니 좋았쓰.
여기서 밍기적거리다 확인하는 프로세스는 없었다. 비즈니스 로직은 최대한 지연 없이 처리될 것이다. 고성능 앱에 적합하다.
좀 웃기는 요약
성능보단 CPU를 덜 태우고 싶으면 안정형 여친, 죽어도 성능이면 불안형 여친인가. 한 마디로 그거잖아.
고성능 시스템은 불안형 연인이냐. 그럼 개발자는 불안형 남친/불안형 여친이라야 심리를 잘 알아서 고성능 개발을 한다는 거냐?
농담이지만 구조를 보면 꽤 그럴듯하다.