블로킹 / 논블로킹 / 비동기 I/O (Blocking / Non-blocking / Asynchronous I/O)
핵심 인사이트 (3줄 요약)
- 본질: 이 세 가지 개념은 애플리케이션 프로세스가 OS에게 느린 I/O(디스크, 네트워크) 작업을 요청했을 때, **"결과가 나올 때까지 내(Process)가 멈춰서 기다릴 것인가?(제어권 반환 시점)"**와 **"결과가 나왔는지 내가 직접 챙길 것인가, 아니면 OS가 나를 찔러줄 것인가?(완료 통지 방식)"**를 결정하는 시스템 아키텍처의 패러다임이다.
- 가치: 1만 개의 동시 접속(C10K 문제)을 처리해야 하는 현대 웹 서버에서, 스레드 1만 개를 띄워놓고 전부 블로킹으로 멈춰두면 서버 메모리가 터져버린다. 이를 단 1개의 스레드로 수만 개 요청을 논블로킹/비동기로 쳐내는 이벤트 루프(Event Loop)의 탄생을 이끈 핵심 최적화 모델이다.
- 융합: 운영체제의 파일 디스크립터(fd) 상태 관리, 시스템 콜(System Call) 설계, 그리고 커널과 유저 공간 사이의 폴링(epoll/kqueue) 및 인터럽트 신호 체계가 정교하게 융합되어 Node.js와 Nginx의 폭발적 성능을 완성했다.
Ⅰ. 개요 및 필요성 (Context & Necessity)
-
개념:
- I/O 모델은 크게 두 가지 기준 축(제어권 반환, 결과 통보)의 조합으로 나뉜다.
- 블로킹 (Blocking):
read()함수를 호출하면, 하드웨어에서 데이터를 전부 퍼 올릴 때까지(수 밀리초~수 초) 함수가 리턴하지 않고 프로세스를 대기(Sleep) 상태로 기절시킨다. - 논블로킹 (Non-blocking):
read()를 호출했을 때 당장 데이터가 없으면, 프로세스를 기절시키지 않고 즉시-1(EAGAIN 에러)을 리턴하여 제어권을 돌려준다. 앱은 다른 일을 하다가 나중에 다시 와서 물어봐야 한다. - 비동기 (Asynchronous): 앱이
aio_read()를 던져두고 쿨하게 자기 할 일을 하러 떠난다. 데이터가 다 준비되면 OS가 백그라운드에서 앱 버퍼에 데이터를 채워놓고, 콜백(Callback) 함수나 시그널로 "다 됐어!"라고 알아서 알람을 울려준다.
-
필요성(문제의식):
- 네트워크 채팅 서버를 블로킹 방식으로 짰다고 가정하자. 1번 접속자가
recv()함수에 걸려 키보드를 안 치고 가만히 있으면, 서버 스레드가 기절해 버려서 2번 접속자는 접속조차 못 하고 화면이 멈춘다. - 이를 해결하려고 접속자마다 스레드를 1개씩 떼어주면(Thread-per-request), 접속자 10만 명일 때 스레드 문맥 교환(Context Switch) 오버헤드와 램 고갈로 서버가 터져버린다.
- 해결책: "기다리지 말고 바로 반환해라!(논블로킹) 다 되면 네가 나한테 찔러줘라!(비동기) 그러면 나(단일 스레드) 혼자서 10만 명을 다 상대할 수 있다."
- 네트워크 채팅 서버를 블로킹 방식으로 짰다고 가정하자. 1번 접속자가
-
💡 비유:
- 블로킹(Blocking): 커피숍에서 진동벨이 없어, 주문하고 카운터 앞에 멍하니 서서 내 커피가 나올 때까지 아무것도 못 하고 기다리는 진상 손님.
- 논블로킹(Non-blocking): 주문해 놓고 자리로 돌아가 책을 읽으면서, 1분마다 카운터에 가서 "제 커피 나왔어요?" 하고 물어보는(Polling) 부산스러운 손님.
- 비동기(Asynchronous): 배달 앱으로 주문해 놓고 잊어버린 채 집안일을 하다가, 라이더가 문 앞(Callback)에 커피를 두고 "띵동!" 벨을 누르면 그때 문만 열고 가져오는 최고의 살림꾼.
-
등장 배경:
- 초기 단순한 파일 I/O는 블로킹으로 충분했으나, 1990년대 인터넷의 폭발로 수많은 동시 소켓 연결(Socket Connection)을 처리해야 하면서
select,poll,epoll을 거쳐 현대의 진정한 비동기 I/O 프레임워크(io_uring,AIO)로 진화했다.
- 초기 단순한 파일 I/O는 블로킹으로 충분했으나, 1990년대 인터넷의 폭발로 수많은 동시 소켓 연결(Socket Connection)을 처리해야 하면서
┌─────────────────────────────────────────────────────────────┐
│ 블로킹 vs 논블로킹 vs 비동기 I/O의 시간 축 흐름 차이 │
├─────────────────────────────────────────────────────────────┤
│ │
│ [ 1. 블로킹 (Blocking I/O) ] - 동기적 │
│ App: 호출 ──▶ (기절 / Wait) ──────────────────▶ 데이터 처리 │
│ 커널: 디스크 헤드 이동 및 버퍼 복사 중... ───┘ │
│ │
│ [ 2. 논블로킹 (Non-blocking I/O) ] - 동기적 │
│ App: 호출 ─▶ EWOULDBLOCK 즉시반환 ─▶ 다른일 ─▶ 호출 ─▶ 처리 │
│ 커널: 아직 없음! 데이터 도착!┘ │
│ │
│ [ 3. 비동기 (Asynchronous I/O) ] - AIO │
│ App: 호출(버퍼주소 넘김) ─▶ 즉시반환 ─▶ 계속 딴 일 쭉~~~ (수거안함)│
│ 커널: 디스크 복사 중.. ─▶ App 버퍼에 채움 ─▶ ⚡ 콜백 발송 │
│ App: (딴 일 하던 중 콜백에 맞음) ───────▶ 즉시 데이터 사용 │
└─────────────────────────────────────────────────────────────┘
[다이어그램 해설] I/O 모델의 핵심 분기점은 "커널이 데이터를 퍼 올리는 무거운 물리적 시간(수십 ms) 동안, 앱(CPU)이 무엇을 하고 있는가?"에 있다. 블로킹은 까만 공백으로 완전히 CPU를 낭비(대기)한다. 논블로킹은 제어권을 바로 반환받아 딴짓을 할 수 있지만, 데이터가 다 됐는지 확인하기 위해 앱이 커널을 반복적으로 귀찮게 찌르는 폴링(Polling) 비용이 든다. 궁극체인 비동기는 앱이 확인할 필요조차 없이 커널이 다 떠먹여 주므로 CPU 효율의 정점을 찍는다.
- 📢 섹션 요약 비유: 빨래를 세탁기에 넣고(I/O 요청), 빨래가 끝날 때까지 세탁기 창문만 쳐다보고 서 있는 것(블로킹)과, 10분마다 화장실에 가보는 것(논블로킹), 알람 소리가 울리면 그때 꺼내는 것(비동기)의 차이로, 여러분의 퇴근 시간(시스템 처리량)을 결정짓는 가장 중요한 요소입니다.
Ⅱ. 아키텍처 및 핵심 원리 (Deep Dive)
동기(Synchronous) vs 비동기(Asynchronous)와 혼동 파훼
가장 헷갈리는 부분은 논블로킹과 비동기를 구분하는 것이다. 이 둘은 2x2 매트릭스로 이해해야 정확하다.
- 제어권 기준 (Blocking vs Non-blocking): 함수를 불렀을 때 당장 리턴(Return)해서 딴 일을 할 수 있는가?
- 타이밍 및 주도권 기준 (Sync vs Async): 데이터가 완성되었는지 챙기는 주체가 누구인가? (내가 직접 챙기면 Sync, OS가 챙겨서 콜백 주면 Async).
| 조합 분류 | 동작 시나리오 매커니즘 | 실무 적용 사례 (API) |
|---|---|---|
| Sync + Blocking | read() 함수 호출 시, 데이터가 다 메모리에 올 때까지 내가 멈춰서 기다리다 다 되면 진행. | 가장 일반적인 C언어 파일 I/O, 구형 소켓 통신 (recv) |
| Sync + Non-blocking | read()가 즉시 리턴됨. 하지만 데이터가 완성됐는지 확인하려면 내가 주기적으로 루프를 돌며 계속 read()를 때려봐야 함 (나의 주도적 확인). | 네트워크 소켓의 O_NONBLOCK 플래그 설정 후 while(read(...) == EAGAIN) |
| Async + Blocking | 논리적으로 모순이나, 비동기 호출을 해놓고 결국 결과가 필요해서 wait() 계열 함수로 강제 대기하는 기형적 구조. | Node.js에서 async 함수를 await로 억지로 동기화처럼 쓸 때의 체감 상태, Java Future.get() |
| Async + Non-blocking | 함수는 즉시 리턴되고, 데이터 완성 확인도 OS가 콜백이나 시그널로 찔러줌. 나는 100% 비즈니스 로직만 수행함. 궁극의 형태. | 리눅스 aio_read(), Windows IOCP, 최신 io_uring, Node.js의 콜백 체인 |
논블로킹 I/O 다중화 (I/O Multiplexing : select / poll / epoll)
"논블로킹으로 1만 명의 소켓을 열어놨는데, 도대체 누구한테 패킷이 도착했는지 어떻게 알지? 1만 개를 while 문으로 계속 read() 해보면 CPU가 폭발하잖아!"
이 딜레마를 해결한 것이 I/O 멀티플렉싱이다. 여러 개의 I/O(소켓) 상태를 단 하나의 함수 호출로 모니터링하는 기술이다.
┌───────────────────────────────────────────────────────────────────┐
│ I/O Multiplexing (epoll) 작동 아키텍처 │
├───────────────────────────────────────────────────────────────────┤
│ │
│ [ 1명의 스레드가 10,000개의 소켓을 감독하는 기적 ] │
│ │
│ App ──▶ OS에게 지시: "epoll_wait() 야! 소켓 1만 개 중에 패킷 도착한 │
│ 놈이 생길 때까지 나 잠시 대기(블로킹)할게." │
│ │
│ OS ──▶ (네트워크 랜카드에 패킷 도착. 하드웨어 인터럽트 발생!) │
│ ──▶ 1. 패킷 까보니 소켓 FD 777번꺼네. │
│ ──▶ 2. epoll의 Red-Black 트리에 관리되던 777번을 │
│ [준비된 리스트 (Ready List)] 로 복사해서 옮겨놓음. │
│ ──▶ 3. 잠들어 있던 App을 깨움! (Wake up) │
│ │
│ App ──▶ 깨어나서 OS가 건네준 [Ready List]를 보니 FD 777번이 있네? │
│ ──▶ 4. 소켓 777번에 대고 `read()` 호출! (무조건 데이터 있음) │
│ ──▶ 5. 다른 9,999개 소켓은 건드리지도 않고 깔끔하게 처리 끝! │
└───────────────────────────────────────────────────────────────────┘
[다이어그램 해설] Nginx와 Redis가 단 하나의 스레드로 세계 최고의 성능을 뽐내는 비밀이 바로 이 epoll(Mac은 kqueue) 아키텍처다. 논블로킹 소켓 1만 개를 일일이 찔러보는 바보짓 대신, 1만 개를 커널에 등록만 해놓고 앱은 epoll_wait()라는 한 곳에서 편하게 블로킹 상태로 기다린다(이때 CPU 소모율 0%). 데이터가 도착하면 커널이 하드웨어 인터럽트를 받아 "이벤트가 발생한 소켓 명단"만 쏙 뽑아서 앱에게 전달한다. 앱은 그 명단에 있는 소켓들만 쏙쏙 논블로킹으로 읽어 들이면 된다. (이벤트 주도형 아키텍처, Event-Driven).
- 📢 섹션 요약 비유: 우편함 1만 개를 1시간마다 전부 열어보며 편지를 확인하는 미친 짓(순수 논블로킹 폴링)을 멈추고, 우체부(커널 epoll)에게 "편지 온 우편함 번호만 쪽지에 적어서 줘"라고 부탁하여, 쪽지에 적힌 우편함 2~3개만 열어보는 최고의 업무 효율화 기법입니다.
Ⅲ. 융합 비교 및 다각도 분석
멀티플렉싱 API 진화사 (select $\rightarrow$ poll $\rightarrow$ epoll)
| 비교 항목 | select (고전) | poll (과도기) | epoll / kqueue (현대 표준) |
|---|---|---|---|
| 소켓 감시 한계 | 최대 1024개 (배열 크기 하드코딩) | 한계 없음 (연결 리스트 사용) | 한계 없음 (레드-블랙 트리 사용) |
| 동작의 치명적 단점 | 패킷 1개 와도 1024개를 처음부터 끝까지 for 문 돌며 전부 검사해야 누군지 알 수 있음 | select와 동일. 감시 대상이 많을수록 O(N)으로 무조건 느려짐 | 이벤트가 발생한 놈들만 따로 모아서 던져줌 O(1). 아무리 늘어나도 성능 저하 없음 |
| 메모리 복사 비용 | 호출할 때마다 소켓 명단(비트맵) 전체를 유저 $\leftrightarrow$ 커널 간 매번 복사 | 호출 시 전체 배열 계속 복사 | 커널에 한 번만 등록해두고, 변화만 통지받음 (공유 메모리 mmap 수준) |
과목 융합 관점
-
네트워크 구조 (C10K Problem): 1999년 제기된 "클라이언트 1만 명(C10K)을 어떻게 한 대의 서버로 처리할 것인가?"라는 난제는 스레드 기반(Thread per connection, Apache 모델)으로는 문맥 교환 비용 탓에 물리적으로 불가능함이 증명되었다. 이를 타파한 것이 1개의 스레드 + 이벤트 루프 +
epoll의 조합(Nginx 모델)이며, 이는 현대 클라우드 로드밸런서와 API 게이트웨이의 유일한 생존 표준이다. -
프로그래밍 언어 (Node.js & libuv): 자바스크립트는 싱글 스레드 언어다. 파일 I/O를 블로킹으로 짰다간 웹 서버가 혼수상태에 빠진다. 그래서 Node.js는 바닥에 C언어로 짠
libuv라는 라이브러리를 깔았다. 유저가 파일 읽기를 시키면libuv가 뒤에서 스레드 풀과 리눅스 비동기 API(io_uring/epoll)를 동원해 논블로킹으로 처리한 뒤, 나중에 자바스크립트의 콜백 큐(Event Loop)로 "다 됐어"라고 이벤트를 쏴주는 환상의 비동기 융합 구조를 만들어냈다. -
📢 섹션 요약 비유: Apache 방식이 1만 명의 손님에게 1만 명의 전담 웨이터(스레드)를 붙여주려다 식당이 웨이터로 꽉 차서 폭발한 모델이라면, Nginx/Node.js 방식은 1명의 초인적인 웨이터가 무전기(epoll)를 차고 주방(커널)에서 요리 완성 알람이 올 때만 손님에게 서빙하며 식당을 여유롭게 굴리는 궁극의 효율화 모델입니다.
Ⅳ. 실무 적용 및 기술사적 판단
실무 시나리오 및 트러블슈팅
-
시나리오 — Node.js 서버의 Event Loop Block (동기 함수 남용): 초당 5,000 요청을 처리하던 Node.js 벡엔드가 갑자기 멈춰버림.
- 원인 분석: 개발자가 로그 파일을 파싱하겠다고
fs.readFileSync()라는 동기/블로킹 함수를 호출해 버림. 단일 스레드로 수천 명을 쳐내던 이벤트 루프가 디스크에서 100MB 파일을 다 읽어올 때까지 1초간 완벽히 기절(Block)함. 그 1초 동안 밀려든 수천 개의 HTTP 요청 소켓이 큐에 쌓이다가 TCP 타임아웃이 터짐. - 아키텍트 판단 (비동기 함수 강제화): 싱글 스레드 논블로킹 기반의 프레임워크(Node.js, Netty, Vert.x)에서는 비즈니스 로직 단에서 무거운 암호화 연산이나 블로킹 I/O를 단 한 줄이라도 호출하면 시스템 전체가 암살당한다. 반드시
fs.readFile()같은 비동기 래퍼(콜백/Promise)를 사용하거나, 무거운 연산은 별도의 Worker Thread Pool(워커 스레드)로 오프로드(Offload)시키는 철저한 아키텍처 리뷰를 거쳐야 한다.
- 원인 분석: 개발자가 로그 파일을 파싱하겠다고
-
시나리오 — 데이터베이스 파일 I/O의 한계 돌파 (io_uring 도입): 고성능 NoSQL DB를 개발 중인데, 디스크에서 데이터를 긁어올 때 비동기 시스템 콜(
aio_read)조차 커널 진입 오버헤드와 캐시 미스 때문에 지연이 크다.- 아키텍트 판단 (리눅스 최고봉 io_uring 사용): 기존 리눅스 AIO는 제약이 많고 멍청했다. 최신 5.1 커널 이상이라면
io_uring프레임워크를 전격 도입한다. 앱과 커널이 메모리를 공유(Shared Ring Buffer)하여, 앱이 버퍼에 "이거 100개 디스크에서 읽어줘"라고 쓱 써놓기만 하면 시스템 콜 호출 없이 커널 백그라운드 스레드가 알아서 읽어오고 결과 버퍼에 표시해 둔다. 앱은 시스템 콜(Syscall) 오버헤드 0번으로 극한의 비동기 I/O 성능(수백만 IOPS)을 달성할 수 있다.
- 아키텍트 판단 (리눅스 최고봉 io_uring 사용): 기존 리눅스 AIO는 제약이 많고 멍청했다. 최신 5.1 커널 이상이라면
┌───────────────────────────────────────────────────────────────────┐
│ 고성능 네트워크/파일 서버 설계를 위한 의사결정 트리 │
├───────────────────────────────────────────────────────────────────┤
│ │
│ [ 동시 접속자 수 및 I/O 특성을 분석한다 ] │
│ │ │
│ ▼ │
│ 동시 커넥션이 수만~수십만 개이고(C10K+), 빠른 응답(I/O Bound) 위주인가? │
│ ├─ 아니오 ──▶ [ Thread-per-Request 모델 무방 (Java Tomcat) ]│
│ │ (비즈니스 로직이 무겁고, 코어 수가 많을 때 안정적임) │
│ └─ 예 ─────▶ [ Event-Driven Non-blocking 모델 도입 필수! ] │
│ │ │
│ ▼ 세부 프레임워크 선택 │
│ 주요 부하가 네트워크 소켓 I/O 인가? 파일 디스크 I/O 인가? │
│ ├─ [ 네트워크 I/O ] ──▶ `epoll` 기반 프레임워크 (Nginx, Netty, Node)│
│ │ │
│ └─ [ 파일 디스크 I/O ] ─▶ `io_uring` 또는 `Direct I/O` + Thread Pool│
│ (※ 일반 파일 I/O는 epoll이 잘 안 먹힘 주의!)│
└───────────────────────────────────────────────────────────────────┘
[다이어그램 해설] 리눅스의 뼈아픈 진실은 "네트워크 소켓은 논블로킹과 epoll이 완벽히 지원되지만, 로컬 파일 시스템의 디스크 파일은 진정한 논블로킹을 거의 지원하지 않는다"는 것이다. 파일 읽기에 O_NONBLOCK을 걸어도 커널이 디스크를 뒤지는 동안은 결국 앱이 블로킹된다. 그래서 아키텍트는 디스크 I/O가 잦은 웹 서버는 Nginx 스레드 풀을 따로 파서 파일 I/O 찌꺼기를 처리하게 격리시키거나, 차세대 마법인 io_uring으로 아키텍처를 갈아엎는 결단을 내려야 한다.
안티패턴
-
콜백 지옥 (Callback Hell): 비동기 방식이 성능엔 최고지만, A 끝난 뒤 B, B 끝난 뒤 C를 실행해야 하는 비즈니스 로직을 짜다 보면
A(function(){ B(function(){ C(function(){...}) }) })형태의 화살표 모양 지옥 코드가 탄생한다. 가독성이 박살 나고 예외 처리가 불가능해져 유지보수 재앙이 온다. -
해결: 최신 언어의
Promise,async / await코루틴(Coroutine) 문법을 도입하여, 겉보기엔 '동기적(Blocking)'인 예쁜 코드처럼 보이지만 컴파일러나 런타임이 밑단에서 100% '비동기(Asynchronous)' 상태 머신으로 찢어서 돌려주는 우아한 프로그래밍 패턴을 써야 한다. -
📢 섹션 요약 비유: 성능을 위해 배달원을 1명만 두는 건 좋지만(비동기), 요리가 다 될 때마다 배달원에게 "이거 받고, 저거 치우고, 저기 갖다줘!"라고 복잡하게 말을 걸면(콜백 지옥) 배달원 머리가 터집니다. 세련된 매뉴얼(async/await)로 절차를 예쁘게 정리해 주는 프레임워크가 필수입니다.
Ⅴ. 기대효과 및 결론
정량/정성 기대효과
| 구분 | 동기적 멀티스레드 모델 | 비동기 + Event Loop 모델 | 개선 효과 |
|---|---|---|---|
| 정량 (동시 연결 수) | 서버 1대당 5,000~1만 개 커넥션 시 RAM 고갈 | 서버 1대당 수십만 커넥션 처리 | 인프라 비용 1/10 수준으로 압축 |
| 정량 (문맥 교환 오버헤드) | 스레드 1만 개의 빈번한 교체 (CPU 40% 낭비) | 1~4개 워커 스레드 고정 (CPU 1% 낭비) | 순수 애플리케이션 비즈니스 로직 처리 스루풋 펌핑 |
| 정성 (아키텍처 대응성) | I/O 대기 시간에 자원이 종속됨 | 느린 API, 빠른 API 호출 병렬 조합 용이 | MSA 환경의 애그리게이션(Aggregation) 응답성 극대화 |
미래 전망
- io_uring의 완전 지배: 20년간 리눅스 I/O의 왕이었던
epoll을 밀어내고, 네트워크 소켓뿐만 아니라 디스크 파일, 심지어 일반 시스템 콜 호출까지 모두 비동기 링 큐(Ring Queue)에 때려 박아 처리하는io_uring생태계가 모든 언어의 런타임(Go, Rust, Node.js) 바닥을 재건축하고 있다. 이는 블로킹/논블로킹의 논쟁 자체를 종식시키고 "Zero-Syscall 비동기 만능주의" 시대를 열고 있다. - 하드웨어 가속 비동기망: 어플리케이션은 비동기 던져놓고 잊어버리는 방식을 더 고도화하여, 메인 메모리가 아닌 DPU(데이터 처리 장치) 스마트 랜카드 안의 메모리에 직접 비동기 명령을 하달하고 CPU는 아예 I/O 스택 근처에도 안 가는 극단적 커널 바이패스로 진화하고 있다.
참고 표준
- POSIX AIO (Asynchronous I/O):
aio_read,aio_write등 스레드 기반으로 에뮬레이션되는 고전적인 비동기 파일 입출력 표준. - Reactor / Proactor Pattern: Nginx와 Node.js, Netty 등 세상의 모든 이벤트 기반 비동기 네트워크 프레임워크의 뼈대가 되는 소프트웨어 디자인 패턴.
블로킹, 논블로킹, 비동기의 진화는 "CPU라는 가장 비싸고 귀한 천재의 시간(Clock)을 1나노초라도 헛된 대기 시간(I/O Wait)으로 낭비하지 않겠다"는 컴퓨터 공학자들의 결벽증적 집착이 만들어낸 예술이다. 인간 세상의 조직 구조가 그렇듯, 컴퓨터 시스템도 하급자(I/O 장치)의 작업이 끝날 때까지 상급자(CPU)가 팔짱 끼고 감시하는 동기적(Synchronous) 구조에서 벗어나, 알아서 다 하면 보고서만 쓱 올리게 놔두고 상급자는 수만 가지 핵심 업무를 결재하는 고도화된 위임형, 비동기적(Asynchronous) 조직으로 완벽하게 진화했다.
- 📢 섹션 요약 비유: 부하 직원이 문서를 복사하러 복사기에 간 동안, 부장이 복사기 앞에서 팔짱 끼고 감시하며 기다리는 조직(블로킹)은 망할 수밖에 없습니다. 부장은 책상에서 100건의 결재를 미친 듯이 쳐내고, 직원들이 복사를 끝내면 책상에 조용히 올려놓고 가는(비동기 이벤트 루프) 회사만이 글로벌 일류 기업(Nginx)으로 성장합니다.
📌 관련 개념 맵 (Knowledge Graph)
| 개념 명칭 | 관계 및 시너지 설명 |
|---|---|
| 문맥 교환 (Context Switch) | 블로킹 I/O 모델에서 수천 개의 스레드를 띄울 때 필연적으로 폭발하여 서버를 마비시키는, 비동기 아키텍처가 피하려 했던 근본 악(Evil)이다. |
| I/O 멀티플렉싱 (epoll) | 단일 스레드로 논블로킹 소켓 수만 개를 관리할 때, 폴링 낭비를 0으로 만들기 위해 커널이 지원하는 현대 비동기 웹 서버의 심장이다. |
| 코루틴 (Coroutine) | 콜백 지옥(Callback Hell)을 해결하기 위해 개발자 눈에는 블로킹처럼 편하게 보이지만, 컴파일러가 비동기로 쪼개주는 언어 차원의 혁명이다. |
| 이벤트 루프 (Event Loop) | 커널이 쏴주는 비동기 알람 이벤트들을 큐에 쌓아놓고, 하나의 스레드가 무한 루프를 돌며 미친 듯이 하나씩 꺼내 처리하는 엔진이다. |
| 인터럽트 (Interrupt) | 비동기 I/O의 철학이 물리적 하드웨어 세계에 구현된 형태로, 장치가 작업을 끝내면 CPU를 찌르는 방식이 소프트웨어 비동기 맵핑의 토대다. |
👶 어린이를 위한 3줄 비유 설명
- 블로킹: 식당에서 짜장면 주문해 놓고 요리사가 면 다 삶을 때까지 주방 앞에서 10분 동안 아무것도 안 하고 멍하니 서 있는 손님이에요.
- 논블로킹: 자리에 앉아 유튜브를 보면서, 1분마다 주방에 가서 "나왔어요? 나왔어요?" 하고 계속 물어봐서 요리사를 귀찮게 하는 손님이에요.
- 비동기 I/O: 자리에서 편하게 유튜브를 보다가, 요리사가 "짜장면 나왔습니다~" 하고 내 번호표를 딩동! 불러주면 그때 딱 가서 받아오는 아주 똑똑한 손님이랍니다! 현대 컴퓨터는 다 이렇게 일해요!