libuv design overview를 번역했지만, 번역이 터무니없으니 원본을 읽으세요ㅎㅎ
리버브는 Node.js를 위해 만들어진 cross-platform 보조 라이브러리입니다. 이벤트기반 비동기 IO 모델을 중심으로 설계되었습니다.
libuv는 I/O 폴링 메커니즘을 넘어선 아주 간단한 추성화를 제공해줍니다: ‘handles’, ‘streams’는 소켓등 여러 엔티티를 위한 아주 높은 단계의 추상화를 제공해주죠.
아래 이미지는 libuv를 구성하는 여러 부분들과 관련된 하위 시스템을 보여주는 다이어그램입니다.
Handles and requests
libuv는 이용자들에게 이벤트루프와 함께 일을 할 2가지의 추상화를 제공해줍니다. 바로 handles과 requests입니다.
Handles 긴 수명을가진 객체를 나타냅니다. 활성 상태에서 특정 작업을 수행하는 것들이죠. 예를 들어,
- “prepare handle”은 이벤트 루프가 활성화되었을 때 매 반복마다 해당하는 콜백을 호출합니다.
- “TCP server handle”은 새로운 연결이 있을 때마다 해당 콜백을 호출합니다.
Requests는 (보통) 짧은 수명을 가진 작업을 나타냅니다. 이 작업들은 보통 핸들을 통해 수행됩니다. (반드시는 아님) 예를 들어 write request는 handle에 데이터를 쓰는데 이용됩니다. 혹은 request 홀로 독립적으로 event loop에서 실행될수도 있습니다.
The I/O loop
I/O (event) loop는 libuv의 핵심 요소입니다. 모든 I/O 작업을 위한 내용을 설정합니다. 그리고 싱글 스레드와 연결시킵니다. 각자가 서로 다른 스레드에서 실행되기만 한다면 여러 이벤트 루프를 실행할 수 있습니다. libuv 이벤트 루프는 특별한 셋팅을 하지않는다면 일반적으로 스레드 안전하지 않으니 유의하시기 바랍니다.
thread safe: 멀티스레드에서 어떤 함수 혹은 변수의 접근동시성에 문제가 없는 상황.
이벤트 루프는 전형적인 비동기 I/O 접근 방식을 따릅니다. 모든 I/O는 개별 OS에 맞는 Non Blocking 소켓을 이용하게 됩니다. Linux에서는 epoll, OSX에선 kqueue등이 있죠.
이벤트 루프가 어떻게 작동하는지 이해하기 위해 아래 다이어그램이 루프 반복의 모든 단계를 보여줍니다.
-
루프의 현재 시간이 새로 업데이트 됩니다. 이벤트 루프는 매 tick마다 실행시간을 캐싱해두고 시간 관련된 작업을 할때마다 이용하게 됩니다.
-
루프가 활성화되어있다면, 한번의 반복이 시작됩니다. 그렇지 않다면 즉시 끝나게 되죠. 그렇다면 언제가 활성화되어있는 상태일까요? 루프가 활성화되어있을 때, handle이나 request을 참조하고 있는 상태일 때, 혹은 handlers를 마무리할때가 활성화되어있다고 간주됩니다.
-
Due timer가 실행됩니다. 1번에 캐싱된 시간보다 이전에 예약된 작업들의 콜백들이 실행됩니다.
-
Pending callbacks가 호출됩니다. 모든 I/O 콜백이 대부분 I/O polling 직후에 호출됩니다. 하지만 다음번 루프 반복을 위해 미뤄질때가 있게 됩니다. 그때 미뤄진 콜백들이 이 단계에서 실행됩니다.
-
Idle handle 콜백들이 호출됩니다. 이름의 뜻이 특이합니다만. 매 반복마다 활성화되어있다면 호출됩니다.
-
Prepare handle 콜백들이 호출됩니다. 루프가 I/O를 차단하기 직전에 호출됩니다.
- Poll timeout이 계산됩니다. I/O를 막기전에 이벤트 루프는 얼마나 막을지 시간을 계산하게 됩니다. 아래 목록이 그 시간을 계산하는 기준입니다.
- 이벤트 루프가
UV_RUN)NOWAIT
로 실행되었다면 timeout은 0. - 이벤트 루프가 멈춰질거라면 (uv_stop() 이 호출된다면), timeout은 0.
- 활성된 handles, request가 없다면 0.
- 만약 idle handle이 하나라도 있다면 0,
- 만약 closed() 되기로한 handle이 있다면 0,
- 위의 경우중 하나라도 매치되지않는다면, 가장 가까운 타이머의 timeout이 적용되고, 활성타이머가 없다면 무한으로 설정됩니다.
- 이벤트 루프가
-
이벤트 루프가 I/O를 block합니다. 7번째 단계에서 계산된 값만큼 blocking을 진행하게 됩니다. All I/O related handles that were monitoring a given file descriptor for a read or write operation get their callbacks called at this point. (번역 힘듬..)
-
Check handle 콜백이 호출됩니다. 이벤트루프 blocking이 끝나마자마 호출됩니다. check handle은 기본적으로 prepare handle의 짝이라고 생각하면 좋습니다.
-
close 콜백이 호출됩니다. handle이
uv_close()
로 인해 끝나게되면 호출됩니다. -
UV_RUN_ONCE
로 실행된 경우에는, I/O를 차단한 후 I/O 콜백이 실행되지 않았을 수 있지만 일정 시간이 지나야 기한이 있는 타이머가 있을 수 있으며 해당 타이머는 콜백을 받습니다. - 반복이 종료됩니다. 만약 이벤트 루프가
UV_RUN_NOWAIT
혹은UV_RUN_ONCE
로 실행된다면, 반복이 종료되고uv_run()
이 반환될것입니다. 만약 이벤트 루프가UV_RUN_DEFAULT
로 실행되었다면, 활성화된 상태에서는 처음부터 다시 시작될 것이고 그렇지 않다면 종료될것입니다.