libuv学习笔记(2)
大致上以libuv文档中的顺序,对重要的结构体以及API深入学习。
uv_loop_t结构体以及相关函数(windows平台)
loop的许多细节与具体的handle对象以及req相关,在学习完相关的内容之后再对loop进行补充与完善。
结构体定义
在uv.h文件中定义,定义如下:
typedef struct uv_loop_s uv_loop_t;
struct uv_loop_s{
void* data; //指向用户自定义数据
unsigned int active_handles; //循环引用计数
void* handle_queue[2]; //handle队列
void* active_reqs[2]; //请求队列
unsigned int stop_flag; //告知循环停止的内部标记
UV_LOOP_PRIVATE_FIELDS; //私有成员宏
}
私有成员宏UV_LOOP_PRIVATE_FIELDS展开如下:
#define UV_LOOP_PRIVATE_FIELDS \
/*循环的完成端口句柄*/ \
HANDLE iocp; \
/*当前时间,毫秒*/ \
uint_64 time; \
/*等待处理的请求的单向循环队列的尾结点*/ \
/*如果队列为空,节点为空,如果只有一个节点*/ \
/*那么tail_->next_req == tail_*/ \
uv_req_t* pending_req_tail; \
/*已关闭的句柄的单向列表的头节点*/ \
uv_handle_t* endgame_handles; \
/*定时器红黑树的头节点*/ \
struct uv_timer_tree_s timers; \
/*事件循环的3种监控handle列表*/ \
uv_prepare_t* prepare_handles; \
uv_check_t* check_handles; \
uv_idle_t* idle_handles; \
/*指向即将被调用的handle。要能够再本次循环*/ \
/*正在结束时安全的移除*/ \
uv_prepare_t* next_prepare_handle; \
/*This handle holds the peer sockets for the fast variant of uv_poll_t*/\
SOCKET poll_peer_sockets[UV_MSAFD_PROVIDER_COUNT]\
/*活动的tcp流的计数*/ \
unsigned int active_tcp_stream; \
/*活动的udp流计数*/ \
unsigned int active_udp_stream; \
/*启动的定时器的计数*/ \
uint64_t timer_counter; \
/*线程池*/ \
//所有需要在线程池中处理的请求结束之后或者被成功取消(uv_cancel),会添加到该列表\
void* wq[2];
uv_mutex_t wq_mutex; \
uv_async_t wq_async;//线程池中的请求处理结束之后,通过这个请求来wakeup loop\
相关函数
循环初始化,导出函数,在uv.h中声明,core.c中定义
int uv_loop_init(uv_loop_t* loop)
{
int err;
/*先初始化libuv本身*/
uv__once_init(); //本函数只会初始化一次,内部调用uv_once(),回调函数只会执行一次。
/*创建iocp句柄*/
loop->iocp=CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,1);
if(loop->iocp == null)
Return uv_translate_sys_error(GetLastError());//将系统错误转换为UV_E*错误
//为了防止使用未初始化的内存,在第一次调用uv_updata_time之前loop->timer必 须被初始化为0
loop->time = 0;
uv_updata_time(loop);//内部调用uv_hrtime(UV__MILLISEC)来获取高精度时间
QUEUE_INIT(&loop->wq); //初始化loop->wq双向链表
QUEUE_INIT(&loop->handle_queue);//初始化loop->handle_queue双向链表
QUEUE_INIT(&loop->active_reqs);//初始化loop->active_req双向链表
loop->active_handles = 0;
loop->pending_req_tail = NULL;
loop->endgame_handles = NULL;
RB_INIT(&loop->timers); //初始化定时器红黑树,将头节点设为null
loop->check_handles = NULL;
loop->prepare_handle = NULL;
loop->idle_handles = NULL;
loop->next_prepare_handle = NULL;
loop->next_check_handle = NULL;
loop->next_idle_handle = NULL;
memset(&loop->poll_peer_sockets, 0, sizeof loop->poll_peer_sockets);
loop->active_tcp_streams = 0;
loop->active_udp_streams = 0;
loop->timer_counter = 0;
loop->stop_flag = 0;
err = uv_mutex_init(&loop->wq_mutex);//初始化互斥量 底层通过临界区实现
if (err)
Goto fail_mutex_init;
//初始化异步handle
err = uv_async_init(loop, &loop->wq_async, uv__work_done);
if (err)
Goto fail_async_init;
uv__handle_unref(&loop->async);
//uv_async_init改变了wq_async.flags = UV__HANDLE_REF
loop->wq_async.flags |= UV__HANDLE_INTERNAL;
return 0;
fail_async_init:
uv_mutex_destroy(&loop->mutex);
fail_mutex_inif:
CloseHandle(loop->iocp);
loop->iocp = INVALID_HANDLE_VALUE;
return err;
}
设置loop配置,导出函数,在uv.h中声明,uv-common.c中定义
windows平台不支持
int uv_loop_configure(uv_loop_t* loop, uv_loop_option option, ...)
{
va_list ap;
int err;
va_start(ap, option);
/*所有平台无关的配置都在此处理*/
err = uv__loop_configure(loop, option, ap); //windows平台直接返回UV_ENOSYS(不支持)
va_end(ap);
return err;
}
关闭loop,导出函数,在uv.h中声明,uv-common.c中定义
int uv_loop_close(uv_loop_t* loop)
{
QUEUE* q; //typedef void *QUEUE[2], q指向内含两个void*的数组
uv_handle_t* h;
//判断loop中保存的请求双向列表是否为空
if (!QUEUE_EMPTY(&(loop)->active_reqs))
retirn UV_EBUSY; //不为空
QUEUE_FOREACH(q, &loop->handle_queue)
{
//遍历队列
//根据变量的偏移获取结构体指针
//#define QUEUE_DATA(ptr, type, field) \
//((type *) ((char *) (ptr) - offsetof(type, field)))
//handle队列中用到的是uv_handle_t的handle_queue成员
h = QUEUE_DATA(q, uv_handle_t, handle_queue);
//有任何不是内部使用的handle,表示还有handle出于激活状态,返回UV_EBUSY
if (!(h->flags & UV_HANDLE_INTERNAL))
return UV_EBUSY;
}
//内部调用uv__loop_close(loop)
uv__loop_close(loop);
//如果没有定义NDEBUG ,也就是release版本
#ifndef NDEBUG
memset(loop, -1, sizeof(*loop));
#endif
if (loop == default_loop_ptr)
default_loop_ptr = NULL;
return 0;
}
内部关闭函数,在uv-common.h中声明,core.c中定义
void uv__loop_close(uv_loop_t* loop)
{
size_t i;
/*关闭不需要额外循环迭代的异步handle*/
//表示loop已经发出了一个异步消息(PostQueuedCompletionStatus),但是消息还没有被处理,需要在
//下一个循环迭代中才能处理
assert(!loop->wq_async.async_sent);
loop->wq_async.close_cb = NULL;
uv__handle_closing(&loop->wq_async);
uv__handle_close(&loop->wq_async);
for(i =0; i < ARRAY_SIZE(loop->poll_peer_sockets); i++)
{
SOCKET sock = loop->poll_peer_sockets[i];
if (sock != 0 && sock != INVALID_SOCKET)
Closesocket(sock);
}
uv_mutex_lock(&loop->wq_mutex); //进入临界区
assert(QUEUE_EMPTY(&loop->wq) && "thread pool work queue not empty!");
assert(!uv__has_active_reqs(loop));
uv_mutex_unlock(&loop->wq_mutex);
uv_mutex_destroy(&loop->wq_mutex);
CloseHandle(loop->iocp);
}
获取默认loop,导出函数, 在uv.h中声明,在uv-common.c中定义
uv_loop_t* uv_default_loop(void)
{
if (default_loop_ptr != NULL)
Return loop_ptr;
//初始化默认的loop
if (uv_loop_init(&default_loop_struct))
Return null;
//返回默认loop的指针
default_loop_ptr = &default_loop_struct;
return default_loop_ptr;
}
运行事件循环,导出函数,在uv.h文件中声明,在core.c文件中定义
与文档设计中的i/o循环流程相符
int uv_run(uv_loop_t* loop, uv_run_mode mode)
{
DEWORD timeout;
int r;
int ran_pending;
void (*poll)(uv_loop_t* loop, DWORD timeout);
if (pGetQueuedCompletionStatusEx)//在uv_winapi_init函数中赋值
poll = &uv_poll_ex; //使用pGetQueuedCompletionStatusEx
else
poll = &uv_poll;//使用GetQueuedCompletionStatus
r = uv__loop_alive(loop);//判断是否存活,存活返回true
if (!r)
uv_update_time(loop); //更新当前时间
while(r != 0 && loop->stop_flag == 0)
{
//循环alive
uv_update_time(loop);//更新时间
uv_process_timer(loop);//运行定时器
ran_pending = uv_process_reqs(loop);//处理请求 详细步骤在req中再说明
uv_idle_invoke(loop);//空转回调
uv_prepare_invoke(loop);//预处理回调
//设置超时
timeout = 0;
if ((mode == UV_RUN_ONCE && !ran_pending) || mode == UV_RUN_DEFAULT)
timrout = uv_backend_timeout(loop); //获取超时
//轮询IO,并将结果以req的形式添加到loop的pending列表,这点与API文档上的说明有点不同。
(*poll)(loop, timeout);
uv_check_invoke(loop);//check回调调用
uv_process_endgames(loop);//处理所有关闭的handle,调用对应的uv_xxx_endgame
if (mode == UV_RUN_ONCE)
{
/* UV_RUN_ONCE 意味着向前运行: 在返回之前至少调用一次回调。
* uv__io_poll()可能在超时之后直接返回而并不进行I/O操作(没有回调函数)
* 这意味着我们有一些满足继续运行条件的等待调用的定时器
*
* UV_RUN_NOWAIT 不保证后续运行,所以check之后的步骤省略。
*/
uv_process_timers(loop);
}
r = uv_loop_alive(loop);
if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT)
Break;
}
if (loop->stro_flag != 0)
Loop->stop_flag = 0;
return r;
}
判断uv_loop_t是否是alive状态
static int uv__loop_alive(const uv_loop_t* loop)
{ //判断loop是否存活 内部函数
Return loop->active_handle > 0 || //有活动的handle
!QUEUE_EMPTY(&loop->active_reqs) || //有活动的请求
Loop->endgame_handle != NULL; //有待关闭的handle
}
处理所有定时器
void uv_process_timer(uv_loop_t* loop)
{ //处理所有定时器
uv_timer_t* timer;
for(timer = RB_MIN(uv_timer_tree_s, &loop->timers); //定时器红黑树的最小节点
timer != NULL && timer->due <= loop->time; //时间条件符合
timer = RB_MIN(uv_timer_tree_s, &loop->timers))
{
uv_timer_stop(timer);//停止定时器,从红黑树种删除
uv_timer_again(timer);//再开定时器,如果有定时器repeat值不为0,调用start,否则不做任何操作
timer->timer_cb((uv_timer_t*) timer);//调用回调函数
}
}
遍历循环中的handle,导出函数,uv.h中声明,uv-common.c中定义
uv_walk(uv_loop_t* loop, uv_walk_cb, void* arg)
{
//遍历循环中的handle
QUEUE queue;
QUEUE* q;
uv_handle_t* h;
QUEUE_MOVE(&loop->handle_queue, &queue);
//通过定义的宏遍历队列
while(!QUEUE_EMPTY(&queue))
{
q = QUEUE_HEAD(&queue);
h = QUEUE_DATA(q, uv_handle_t, handle_queue);
QUEUE_REMOVE(q);
QUEUE_INSERT_TAIL(&loop->handle_queue, q);
if (h->flags & UV__HANDLE_INTERNAL) continue;
//所有非内部的handle,调用回调
walk_cb(h, arg);
}
}
通过以上的一些函数可以简单的了解loop的运行流程,主要是一个单线程的循环,不断的从初始化时创建的完成端口中获取事件,同时为了兼顾定时器以及idle等监视器的回调,对于轮询(通过完成端口获取事件)的等待超时做了处理,根据不同的情况会有不同的超时值,有些类似于windows窗口的消息循环。
loop同时与libuv的线程池有关系,这个会学习线程池时分析。
不同的handle以及requite也会在之后的学习中具体分析,此处并没有深入研究。