데모 테스트
#include <stdio.h>
#include <stdlib.h>
#include <uv.h>
void idle_cb(uv_idle_t *handle) {
printf("idle callback
");
}
int main() {
uv_idle_t idle;
//
uv_idle_init(uv_default_loop(), &idle);
// 시작하면 이벤트 루프의 각 라운드가 유휴 상태로 실행됩니다._cb
uv_idle_start(&idle, idle_cb);
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
return 0;
}
uv_idle_t의 데이터 구조
uv_default_loop()로 이동합니다.
static uv_loop_t default_loop_struct;
static uv_loop_t* default_loop_ptr;
uv_loop_t* uv_default_loop(void) {
if (default_loop_ptr != NULL)
return default_loop_ptr;
if (uv_loop_init(&default_loop_struct))
return NULL;
default_loop_ptr = &default_loop_struct;
return default_loop_ptr;
}
uv_loop_init으로 이동하여 기본 루프로 돌아가거나 이미 실행된 경우 기본 루프로 돌아갑니다.
루프 구조
uv_loop_init으로 이동하여 루프를 초기화합니다.
int uv_loop_init(uv_loop_t* loop) {
uv__loop_internal_fields_t* lfields;
void* saved_data;
int err;
saved_data = loop->data;
memset(loop, 0, sizeof(*loop));
loop->data = saved_data;
lfields = (uv__loop_internal_fields_t*) uv__calloc(1, sizeof(*lfields));
if (lfields == NULL)
return UV_ENOMEM;
loop->internal_fields = lfields;
err = uv_mutex_init(&lfields->loop_metrics.lock);
if (err)
goto fail_metrics_mutex_init;
memset(&lfields->loop_metrics.metrics,
0,
sizeof(lfields->loop_metrics.metrics));
heap_init((struct heap*) &loop->timer_heap);
uv__queue_init(&loop->wq);
uv__queue_init(&loop->idle_handles);
uv__queue_init(&loop->async_handles);
uv__queue_init(&loop->check_handles);
uv__queue_init(&loop->prepare_handles);
uv__queue_init(&loop->handle_queue);
loop->active_handles = 0;
loop->active_reqs.count = 0;
loop->nfds = 0;
loop->watchers = NULL;
loop->nwatchers = 0;
uv__queue_init(&loop->pending_queue);
uv__queue_init(&loop->watcher_queue);
loop->closing_handles = NULL;
uv__update_time(loop);
loop->async_io_watcher.fd = -1;
loop->async_wfd = -1;
loop->signal_pipefd[0] = -1;
loop->signal_pipefd[1] = -1;
loop->backend_fd = -1;
loop->emfile_fd = -1;
loop->timer_counter = 0;
loop->stop_flag = 0;
err = uv__platform_loop_init(loop);
if (err)
goto fail_platform_init;
uv__signal_global_once_init();
err = uv__process_init(loop);
if (err)
goto fail_signal_init;
uv__queue_init(&loop->process_handles);
err = uv_rwlock_init(&loop->cloexec_lock);
if (err)
goto fail_rwlock_init;
err = uv_mutex_init(&loop->wq_mutex);
if (err)
goto fail_mutex_init;
err = uv_async_init(loop, &loop->wq_async, uv__work_done);
if (err)
goto fail_async_init;
uv__handle_unref(&loop->wq_async);
loop->wq_async.flags |= UV_HANDLE_INTERNAL;
return 0;
fail_async_init:
uv_mutex_destroy(&loop->wq_mutex);
fail_mutex_init:
uv_rwlock_destroy(&loop->cloexec_lock);
fail_rwlock_init:
uv__signal_loop_cleanup(loop);
fail_signal_init:
uv__platform_loop_delete(loop);
fail_platform_init:
uv_mutex_destroy(&lfields->loop_metrics.lock);
fail_metrics_mutex_init:
uv__free(lfields);
loop->internal_fields = NULL;
uv__free(loop->watchers);
loop->nwatchers = 0;
return err;
}
이번에는 주로 다음 사항에 중점을 두고 유휴 대기열을 분석합니다.
// ...
uv__queue_init(&loop->idle_handles);
// ...
uv__queue_init(&loop->handle_queue);
// ...
err = uv_async_init(loop, &loop->wq_async, uv__work_done);
큐는 큐에 대한 포인터이며, 큐의 첫 번째 요소는 큐에 대한 포인터를 가리킵니다.
static inline void uv__queue_init(struct uv__queue* q) {
q->next = q;
q->prev = q;
}
대기열 구조는 다음과 같습니다.
struct uv__queue {
struct uv__queue* next;
struct uv__queue* prev;
};
idle_handles 대기열에 대한 포인터 &loop->idle_handles = 0x00000001000794d8.
핸들_큐 큐에 대한 포인터 &loop->handle_queue = 0x00000001000792a0.
err = uv_async_init(loop, &loop->wq_async, uv__work_done);
이 문장은 비동기성을 포함하며, 자세한 내용은 지금은 생략하고 loop->handle_queue 미치는 영향은 wq_async->handle_queue 머리에 loop->handle_queue 삽입하는 것입니다.
wq_async->handle_queue 대기열에 대한 포인터 &loop->wq_async->handle_queue = 0x0000000100079378.
uv_default_loop가 초기화되면 uv_idle_init이 실행됩니다.
idle_handle 구조
int uv_##name##_init(uv_loop_t* loop, uv_##name##_t* handle) { \
uv__handle_init(loop, (uv_handle_t*)handle, UV_##type); \
handle->name##_cb = NULL; \
return 0; \
}
#define uv__handle_init(loop_, h, type_) \
do { \
(h)->loop = (loop_); \
(h)->type = (type_); \
(h)->flags = UV_HANDLE_REF; /* Ref the loop when active. */ \
uv__queue_insert_tail(&(loop_)->handle_queue, &(h)->handle_queue); \
uv__handle_platform_init(h); \
} \
while (0)
static inline void uv__queue_insert_tail(struct uv__queue* h,
struct uv__queue* q) {
q->next = h;
q->prev = h->prev;
q->prev->next = q;
h->prev = q;
}
이 단계는 uv_async_init처럼 작동하여 idle_handle->handle_queueloop->handle_queue 삽입합니다.
따라서 loop->handle_queue 루프 대기열에는
- 루프 -> 핸들 큐에 대한 포인터
- wq_async->handle_queue
- idle_handle->handle_queue
로 이동
v_idle_start(&idle, idle_cb);
int uv_##name##_start(uv_##name##_t* handle, uv_##name##_cb cb) { \
if (uv__is_active(handle)) return 0; \
if (cb == NULL) return UV_EINVAL; \
uv__queue_insert_head(&handle->loop->name##_handles, &handle->queue); \
handle->name##_cb = cb; \
uv__handle_start(handle); \
return 0; \
}
h는 loop->idle_handles, q는 idle_handle->queue이니 헷갈리지 마세요.
static inline void uv__queue_insert_head(struct uv__queue* h,
struct uv__queue* q) {
q->next = h->next;
q->prev = h;
q->next->prev = q;
h->next = q;
}
- 루프->유휴_핸들에 대한 포인터
- idle_handle->queue
그 후
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
루프가 이미 정의되어 있으므로 uv_default_loop()는 정의된 루프를 직접 반환합니다.
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);
/* Maintain backwards compatibility by processing timers before entering the
* while loop for UV_RUN_DEFAULT. Otherwise timers only need to be executed
* once, which should be done after polling in order to maintain proper
* execution order of the conceptual event loop. */
if (mode == UV_RUN_DEFAULT && r != 0 && loop->stop_flag == 0) {
uv__update_time(loop);
uv__run_timers(loop);
}
while (r != 0 && loop->stop_flag == 0) {
can_sleep =
uv__queue_empty(&loop->pending_queue) &&
uv__queue_empty(&loop->idle_handles);
uv__run_pending(loop);
uv__run_idle(loop);
uv__run_prepare(loop);
timeout = 0;
if ((mode == UV_RUN_ONCE && can_sleep) || mode == UV_RUN_DEFAULT)
timeout = uv__backend_timeout(loop);
uv__metrics_inc_loop_count(loop);
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 && !uv__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);
uv__run_check(loop);
uv__run_closing_handles(loop);
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;
}
이번에는 uv__run_idle(loop)를 살펴보세요;
void uv__run_##name(uv_loop_t* loop) { \
uv_##name##_t* h; \
struct uv__queue queue; \
struct uv__queue* q; \
uv__queue_move(&loop->name##_handles, &queue); \
while (!uv__queue_empty(&queue)) { \
q = uv__queue_head(&queue); \
h = uv__queue_data(q, uv_##name##_t, queue); \
uv__queue_remove(q); \
uv__queue_insert_tail(&loop->name##_handles, q); \
h->name##_cb(h); \
} \
}
static inline void uv__queue_move(struct uv__queue* h, struct uv__queue* n) {
if (uv__queue_empty(h))
uv__queue_init(n);
else
uv__queue_split(h, h->next, n);
}
loop->idle_handles가 비어 있지 않습니다.
h는 루프->유휴_핸들입니다.
Q는 루프->유휴_핸들->넥스트입니다.
n은 새로 정의된 빈 대기열입니다.
static inline void uv__queue_split(struct uv__queue* h,
struct uv__queue* q,
struct uv__queue* n) {
n->prev = h->prev;
n->prev->next = n;
n->next = q;
h->prev = q->prev;
h->prev->next = h;
q->prev = n;
}
loop->idle_handles는 이제 큐에서 제외되고 새 큐 큐가 loop->idle_handles를 대신합니다.
동안 루프 입력
while (!uv__queue_empty(&queue)) { \
q = uv__queue_head(&queue); \
h = uv__queue_data(q, uv_##name##_t, queue); \
uv__queue_remove(q); \
uv__queue_insert_tail(&loop->name##_handles, q); \
h->name##_cb(h); \
}
q는 idle_handle->queue에 대한 포인터입니다.
static inline struct uv__queue* uv__queue_head(const struct uv__queue* q) {
return q->next;
}
구조체 uv_idle_t의 큐 위치와 idle_handle에 대한 포인터를 기반으로, idle_handle에 대한 전체 정보를 유추할 수 있습니다.
h = uv__queue_data(q, uv_idle_t, queue);
#define uv__queue_data(pointer, type, field) \
((type*) ((char*) (pointer) - offsetof(type, field)))
대기열에서 idle_handle 제외하기
static inline void uv__queue_remove(struct uv__queue* q) {
q->prev->next = q->next;
q->next->prev = q->prev;
}
그런 다음 idle_handle을 loop->idle_handles에 다시 넣습니다.
마지막 통화
h->name##_cb(h);
데모에서 cb 실행하기
void idle_cb(uv_idle_t *handle) {
printf("idle callback
");
}
마지막 uv__queue_insert_tail은 idle_handle을 loop->idle_handles 큐에 다시 넣기 때문에, 이 콜백은 항상 현재 실행됩니다.




