libuv 的API是C风格的,很容易读。你可能觉得uv.h中暴露了太多的数据结构了,不够简洁,我想是因为libuv涵盖的内容非常的广泛,从网络,pipe,文件,终端等等,包罗万象。而且uv.h也不是接口的全部,还有两个头文件,uv-unix.h和uv-win.h,里面定义了操作系统specific的数据结构。其实,libuv已经对接口的简化做了一些努力,比如说,通过uv_write一个函数,我们可以写TCP,Pipe和tty。
因为libuv涵盖广泛,我们的目的只是为了了解它如何与nodejs,v8协调工作的,不会面面俱到。有几个重要的数据结构有必要先了解一下:
#define UV_HANDLE_FIELDS \
uv_loop_t* loop;
uv_handle_type type;
uv_close_cb close_cb;
void* data;
UV_HANDLE_PRIVATE_FIELDS
/* The abstract base class of all handles. */
struct uv_handle_s {
UV_HANDLE_FIELDS
};
uv_handle_s是其它handle的父类,比如说,uv_tcp_s就是它的子类,在那里你能找到socket。loop字段表明它属于哪个loop,handle里还有一些callback函数,异步调用通常会有两个参数,一个是handle,一个是callback函数,调用完成的时候,libuv会调用callback函数。data字段是留给使用者的,nodejs实现异步机制的时候会用到。
#define UV_REQ_FIELDS \
uv_req_type type;
void* data;
UV_REQ_PRIVATE_FIELDS
/* Abstract base class of all requests. */
struct uv_req_s {
UV_REQ_FIELDS
};
这是所有request的父类。loop维护一个request queue。data字段有时用来存放handle。
再来看loop,这是libuv里面最关键的数据结构了,一般会指定一个线程负责一个loop的处理,nodejs只使用了一个loop,由主线程负责对它进行处理。
struct uv_loop_s {
UV_LOOP_PRIVATE_FIELDS
uv_ares_task_t* uv_ares_handles_;
uv_async_t uv_eio_want_poll_notifier;
uv_async_t uv_eio_done_poll_notifier;
uv_idle_t uv_eio_poller;
uv_counters_t counters;
uv_err_t last_err;
void* data;
};
#define UV_LOOP_ONCE(loop, poll)
do {
uv_update_time((loop));
uv_process_timers((loop));
/* Call idle callbacks if nothing to do. */
if ((loop)->pending_reqs_tail == NULL &&
(loop)->endgame_handles == NULL) {
uv_idle_invoke((loop));
}
uv_process_reqs((loop));
uv_process_endgames((loop));
if ((loop)->refs <= 0) {
break;
}
uv_prepare_invoke((loop));
poll((loop), (loop)->idle_handles == NULL &&
(loop)->pending_reqs_tail == NULL &&
(loop)->endgame_handles == NULL &&
(loop)->refs > 0);
uv_check_invoke((loop));
} while (0);
#define UV_LOOP(loop, poll)
while ((loop)->refs > 0) {
UV_LOOP_ONCE(loop, poll)
}
- 函数poll()检查completion port,有事件发生的时候,产生一条request,插入到请求队列中。
- 函数uv_process_reqs()从消息队列中取出请求,处理请求。此函数在处理请求的过程中可能会调用用户传入的callback。
- 函数uv_process_endgames()清理已经关闭的handle,同时减掉refs。