libuv 源码分析(1) - event loop的初始化

by gngshn gngshn@gmail.com

从这里开始我将从linux的角度来看看libuv的工作原理, 如果您希望看到libuv跨平台的实现方式, 那你可能要失望了. 因为这一系列文章都将从linux的角度来写.

我们知道如果需要使用libuv的event loop需要通过uv_default_loop或者uv_loop_init来获得一个loop, 前者也是会调用后者的. 所以我们来看看uv_loop_init到底做了些什么, 我将在source code中进行直接写注释来描述相关字段的含义.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
int uv_loop_init(uv_loop_t* loop) {
void* saved_data;
int err;

/* 创建一ipe, 往其中一端写能解锁读端, 这与信号处理有关 */
uv__signal_global_once_init();

saved_data = loop->data;
memset(loop, 0, sizeof(*loop));
loop->data = saved_data;

/* 保存libuv中timer的堆, 用来计算下次poll的超时时间 */
heap_init((struct heap*) &loop->timer_heap);
/* libuv线程池完成工作以后, 会把相关的request(uv_work_t)放在这个链表上, 从而libuv可以通过他来调用after_work_done回调 */
QUEUE_INIT(&loop->wq);
/* event loop中的所有的request */
QUEUE_INIT(&loop->active_reqs);
/* event loop中的idle handles, 这个handle每次loop都会调用一次 */
QUEUE_INIT(&loop->idle_handles);
/* event loop中的async_handles, 这个handle主要用来管理多线程的异步通知, 如libuv的工作队列的完成通知 */
QUEUE_INIT(&loop->async_handles);
/* event loop中的prepare handles, 这个handle每次loop都会调用一次 */
QUEUE_INIT(&loop->check_handles);
/* event loop中的check handles, 这个handle每次loop都会调用一次, 和prepare是一对, 分别在poll前后调用 */
QUEUE_INIT(&loop->prepare_handles);
/* event loop中所有的handle */
QUEUE_INIT(&loop->handle_queue);

/* event loop监听的描述符数量 */
loop->nfds = 0;
/* event loop监听的事件的表(数组, 但不一定充满, 用fd来索引), 最后两个元素是特殊的 */
loop->watchers = NULL;
/* watchers数组的大小 - 2 */
loop->nwatchers = 0;
/* TODO */
QUEUE_INIT(&loop->pending_queue);
/* 所有待加入poll的事ueue, libuv通过它来确定需要往epoll中加入哪些描述符和事件 */
QUEUE_INIT(&loop->watcher_queue);

/* 需要关闭的handles链表 */
loop->closing_handles = NULL;
/* 更新时间 */
uv__update_time(loop);
/* 这里存放异步通知所用的eventfd或者pipe的描述符 */
loop->async_io_watcher.fd = -1;
/* 和上面一样, 存放pipe对的另一个描述符, 如果是eventfd, 就是-1 */
loop->async_wfd = -1;
/* uv信号处理回调所用的pipe, 信号处理函数往[1]写, event loop poll [0], 从而获取msg来处理信号 */
loop->signal_pipefd[0] = -1;
loop->signal_pipefd[1] = -1;
/* event loop的epoll描述符 */
loop->backend_fd = -1;
/* TODO */
loop->emfile_fd = -1;

/* timer id的counter, 为了给timer一个唯一的id, 每次创建timer, 这个值都加1 */
loop->timer_counter = 0;
/* event loop的停止标志*/
loop->stop_flag = 0;

/* 将backend_fd设为创建的epoll的描述符, 并初始化了inotify为-1 */
err = uv__platform_loop_init(loop);
if (err)
return err;
/* 这里会初始化前面的signal_pipefd为一对pipe并将signal_pipefd[0]的POLLIN事件加入监听列表, 用来处理信号 */
/* 这里的child_watcher是用来处理子进程的退出的, 会在创建进程(uv_spam)时添加SIG_CHILD的处理 */
err = uv_signal_init(loop, &loop->child_watcher);
if (err)
goto fail_signal_init;

/* unref是为了不让event loop停不下来, 也就是event loop判定alive的条件去除这个handle, 后面文章会说 */
uv__handle_unref(&loop->child_watcher);
/* 标记为UV__HANDLE_INTERNAL, 从而handle不会被关闭, 也不会被uv_walk影响 */
loop->child_watcher.flags |= UV__HANDLE_INTERNAL;
/* 所有子进程的队列 */
QUEUE_INIT(&loop->process_handles);

/* TODO */
err = uv_rwlock_init(&loop->cloexec_lock);
if (err)
goto fail_rwlock_init;

/* 用来保护loop->wq的保护锁 */
err = uv_mutex_init(&loop->wq_mutex);
if (err)
goto fail_mutex_init;

/* 创建eventfd放在async_io_watcher.fd(前面说过), 并把其POLLIN加入到监听事件中, 用来处理进程发来的异步通知 */
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);

return err;
}

以上就是libuv的event loop的初始化流程, 后续我将分模块来讲解libuv各个功能模块的实现原理.