200字范文,内容丰富有趣,生活中的好帮手!
200字范文 > libuv 原理_理解libuv的基本原理

libuv 原理_理解libuv的基本原理

时间:2022-12-17 06:53:47

相关推荐

libuv 原理_理解libuv的基本原理

libuv的实现是一个很经典生产者-消费者模型。libuv在整个生命周期中,每一次循环都执行每个阶段(phase)维护的任务队列。逐个执行节点里的回调,在回调中,不断生产新的任务,从而不断驱动libuv。今天我们分析一下libuv的整体架构,从而学会如何使用libuv。我们从libuv的一个小例子开始。

#include

#include

int64_t counter = 0;

void wait_for_a_while(uv_idle_t* handle) {

counter++;

if (counter >= 10e6)

uv_idle_stop(handle);

}

int main() {

uv_idle_t idler;

// 获取事件循环的核心结构体。并初始化一个idler

uv_idle_init(uv_default_loop(), &idler);

// 往事件循环的idle节点插入一个任务

uv_idle_start(&idler, wait_for_a_while);

// 启动事件循环

uv_run(uv_default_loop(), UV_RUN_DEFAULT);

// 销毁libuv的相关数据

uv_loop_close(uv_default_loop());

return 0;

}

使用libuv,我们首先需要获取libuv的核心结构体uv_loop_t。uv_loop_t是一个非常大的结构体。里面记录了libuv整个生命周期的数据。

uv_default_loop为我们提供了一个默认已经初始化了的uv_loop_t结构体。当然我们也可以自己去分配一个,自己初始化。

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;

}

libuv维护了一个全局的uv_loop_t结构体,使用uv_loop_init进行初始化。不打算展开uv_loop_init函数。因为他大概就是对uv_loop_t结构体各个字段进行初始化。接着我们看一下uv_idle_*系列的函数。

1 uv_idle_init

int uv_idle_init(uv_loop_t* loop, uv_idle_t* handle) {

/*

初始化handle的类型,所属loop,打上UV_HANDLE_REF,

并且把handle插入loop->handle_queue队列的队尾

*/

uv__handle_init(loop, (uv_handle_t*)handle, UV_IDLE);

handle->idle_cb = NULL;

return 0;

}

执行uv_idle_init函数后,libuv的内存视图如下

2 uv_idle_start

int uv_idle_start(uv_idle_t* handle, uv_idle_cb cb) {

// 如果已经执行过start函数则直接返回

if (uv__is_active(handle)) return 0;

if (cb == NULL) return UV_EINVAL;

// 把handle插入loop中idle的队列

QUEUE_INSERT_HEAD(&handle->loop->idle_handles, &handle->queue);

// 挂载回调,下一轮循环的时候被执行

handle->idle_cb = cb;

/*

设置UV_HANDLE_ACTIVE标记位,并且loop中的handle数加一,

init的时候只是把handle挂载到loop,start的时候handle才处于激活态

*/

uv__handle_start(handle);

return 0;

}

执行完uv_idle_start的内存视图

然后执行uv_run进入libuv的事件循环。

int uv_run(uv_loop_t* loop, uv_run_mode mode) {

int timeout;

int r;

int ran_pending;

// 在uv_run之前要先提交任务到loop

r = uv__loop_alive(loop);

// 事件循环没有任务执行,即将退出,设置一下当前循环的时间

if (!r)

uv__update_time(loop);

// 没有任务需要处理或者调用了uv_stop

while (r != 0 && loop->stop_flag == 0) {

// 更新loop的time字段

uv__update_time(loop);

// 执行超时回调

uv__run_timers(loop);

// 执行pending回调,ran_pending代表pending队列是否为空,即没有节点可以执行

ran_pending = uv__run_pending(loop);

// 继续执行各种队列

uv__run_idle(loop);

uv__run_prepare(loop);

timeout = 0;

// 执行模式是UV_RUN_ONCE时,如果没有pending节点,才会阻塞式poll io,默认模式也是

if ((mode == UV_RUN_ONCE && !ran_pending) || mode == UV_RUN_DEFAULT)

timeout = uv_backend_timeout(loop);

// poll io timeout是epoll_wait的超时时间

uv__io_poll(loop, timeout);

uv__run_check(loop);

uv__run_closing_handles(loop);

// 还有一次执行超时回调的机会,因为poll io阶段可能是因为定时器超时返回的。

if (mode == UV_RUN_ONCE) {

uv__update_time(loop);

uv__run_timers(loop);

}

r = uv__loop_alive(loop);

// 只执行一次,退出循环,UV_RUN_NOWAIT表示在poll io阶段不会阻塞并且循环只执行一次

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.

*/

// 是因为调用了uv_stop退出的,重置flag

if (loop->stop_flag != 0)

loop->stop_flag = 0;

// 返回是否还有活跃的任务(handle或request),业务代表可以再次执行uv_run

return r;

}

我们看到有一个函数是uv__run_idle。这就是处理idle阶段的函数。我们看一下他的实现。

// 在每一轮循环中执行该函数,执行时机见uv_run

void uv__run_idle(uv_loop_t* loop) {

uv_idle_t* h;

QUEUE queue;

QUEUE* q;

// 把该类型对应的队列中所有节点摘下来挂载到queue变量,变量回调里不断插入新节点,导致死循环

QUEUE_MOVE(&loop->idle_handles, &queue);

// 遍历队列,执行每个节点里面的函数

while (!QUEUE_EMPTY(&queue)) {

// 取下当前待处理的节点

q = QUEUE_HEAD(&queue);

// 取得该节点对应的整个结构体的基地址

h = QUEUE_DATA(q, uv_idle_t, queue);

// 把该节点移出当前队列,否则循环不会结束

QUEUE_REMOVE(q);

// 重新插入原来的队列

QUEUE_INSERT_TAIL(&loop->idle_handles, q);

// 执行回调函数

h->idle_cb(h);

}

}

我们看到uv__run_idle的逻辑并不复杂。就是遍历idle_handles队列的节点,然后执行回调。在回调里我们可以插入新的节点(产生新任务)。从而不断驱动libuv的运行。我们看到uv_run退出循环的条件下面的代码为false

r != 0 && loop->stop_flag == 0

stop_flag由用户主动关闭libuv事件循环。

void uv_stop(uv_loop_t* loop) {

loop->stop_flag = 1;

}

r是代表事件循环是否还存活,这个判断的标准是由uv__loop_alive提供

static int uv__loop_alive(const uv_loop_t* loop) {

return loop->active_handles > 0 ||

loop->active_reqs.count > 0 ||

loop->closing_handles != NULL;

}

这时候我们有一个actived handles。所以libuv不会退出。当我们调用uv_idle_stop函数把idle节点移出handle队列的时候,libuv就会退出。

int uv_idle_stop(uv_idle_t* handle) {

if (!uv__is_active(handle)) return 0;

// 把idle节点从loop中idle队列移除,但是还挂载到handle_queue中

QUEUE_REMOVE(&handle->queue);

// 清除active标记位并且减去loop中handle的active数

uv__handle_stop(handle);

return 0;

}

执行uv_idle_stop后active_handles为0,这时候事件循环就结束了。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。