libuv学习笔记(2)

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也会在之后的学习中具体分析,此处并没有深入研究。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
libhv和libuv都是开源的跨平台网络库,用于处理网络编程中的底层操作。它们的主要功能是封装对操作系统提供的异步IO接口,使开发者能更加方便地编写高性能的网络应用程序。 libhv是基于C语言的网络库,提供了一套简洁易用的API接口。它开发的初衷是为了满足高性能Web服务器的需求,因此在性能和效率方面有很好的表现。它支持多线程、异步IO、定时器、事件派发等功能,可以在处理大量并发连接的场景中保持低延迟和高吞吐量。libhv还能处理HTTP、WebSocket等应用层协议的解析和处理,使开发者能更加专注于业务逻辑的实现。 libuv也是跨平台的网络库,但是与libhv不同的是,libuv更加注重事件驱动的编程模型。它采用了事件循环机制,可以处理大量同时发生的事件,并将事件分发给相应的事件处理器进行处理。libuv的优点在于它的跨平台性和高性能,它的事件循环机制可以充分利用操作系统提供的异步IO接口,使得网络应用程序能在不同的操作系统上实现高度一致的性能。 在使用上,libhv和libuv都可以在不同的操作系统上运行,包括Windows、Linux、macOS等。它们都提供了丰富的API接口,使得开发者能够灵活地处理网络编程中的各种需求。同时,它们都有活跃的开发社区,可以获取到及时的技术支持和更新。 总体来说,libhv和libuv都是优秀的网络库,具有很高的性能和可靠性。它们的出现不仅使网络编程更加容易,也为开发高性能的网络应用提供了很好的选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值