NodeJS는 JS를 구동하는 런타임입니다.
그래서 JS를 구동하기위해 다양한 의존성 모듈을 가지고있는데,
V8
libuv
llhttp
c-ares
tls
OpenSSL
zlip
등이 있습니다.
이번에는 내부 이벤트루프를 담당하는 libuv를 확인해보고자합니다.
libuv는 커널을 추상화해서 wrapping하는 구조입니다.
handle/stream 등의 개념으로 socket등의 개체를 추상화해서 사용할 수 있습니다.
네트워크/파일시스템/concurrency control도 제공합니다.
Node.JS의 특징인 이벤트기반/Non-Blocking 비동기 I/O
를 지원하는게 libuv입니다.
자신만의 쓰레드풀
을 가지고있어 Node가 요청한 비동기 작업들이 여기서 처리됩니다.
kqueue, epoll, iocp 등 kernel event notification mechanism
을 사용해 시간이 오래걸리는 작업들을 Non-Blocking으로 처리하며, 이 작업들을 커널에 위임합니다.
- 운영체제별로 사용할 수 있는 것이 달라서, libuv는 3가지 모두를 구현해 플랫폼에 영향받지 않습니다.
커널에서는 이러한 작업들이 종료되면 이벤트루프에 callback을 등록합니다.
커널이 Non-Blocking을 지원하지 않는 작업은 스레드풀에 작업을 offload합니다.
macrotask queue
+ 비동기 I/O작업을 처리하는 루프입니다.int uv_run(uv_loop_t* loop, uv_run_mode mode) {
int timeout;
int r;
int can_sleep;
r = uv__loop_alive(loop);
if (!r)
uv__update_time(loop);
while (r != 0 && loop->stop_flag == 0) {
//타이머페이즈
uv__update_time(loop);
uv__run_timers(loop);
can_sleep =
QUEUE_EMPTY(&loop->pending_queue) && QUEUE_EMPTY(&loop->idle_handles);
//펜딩페이즈
uv__run_pending(loop);
//Idle페이즈
uv__run_idle(loop);
//Prepare페이즈
uv__run_prepare(loop);
timeout = 0;
if ((mode == UV_RUN_ONCE && can_sleep) || mode == UV_RUN_DEFAULT)
timeout = uv__backend_timeout(loop);
//Poll페이즈
uv__io_poll(loop, timeout);
/* Process immediate callbacks (e.g. write_cb) a small fixed number of
* times to avoid loop starvation.*/
for (r = 0; r < 8 && !QUEUE_EMPTY(&loop->pending_queue); r++)
uv__run_pending(loop);
/* Run one final update on the provider_idle_time in case uv__io_poll
* returned because the timeout expired, but no events were received. This
* call will be ignored if the provider_entry_time was either never set (if
* the timeout == 0) or was already updated b/c an event was received.
*/
uv__metrics_update_idle_time(loop);
//Check페이즈
uv__run_check(loop);
//Close페이즈
uv__run_closing_handles(loop);
if (mode == UV_RUN_ONCE) {
/* UV_RUN_ONCE implies forward progress: at least one callback must have
* been invoked when it returns. uv__io_poll() can return without doing
* I/O (meaning: no callbacks) when its timeout expires - which means we
* have pending timers that satisfy the forward progress constraint.
*
* UV_RUN_NOWAIT makes no guarantees about progress so it's omitted from
* the check.
*/
uv__update_time(loop);
uv__run_timers(loop);
}
r = uv__loop_alive(loop);
if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT)
break;
}
/* The if statement lets gcc compile it to a conditional store. Avoids
* dirtying a cache line.
*/
if (loop->stop_flag != 0)
loop->stop_flag = 0;
return r;
}
다음 글에서 이 루프의 구성요소를 코드레벨에서 하나씩 확인해보도록 하겠습니다.