libuv事件循环
1、说明
事件循环是 libuv 的核心功能,负责 IO 的轮询和事件回调的调度。
2、数据类型
2.1、uv_loop_t
事件循环数据类型,结构体
uv_loop_t.data 用于传递用户数据,libuv 不会触碰
2.2、uv_walk_cb
传递给 uv_walk() 方法的回调函数类型
void (*uv_walk_cb)(uv_handle_t* handle, void* arg);
3、API
3.1、uv_loop_init
int uv_loop_init(uv_loop_t* loop);
初始化 uv_loop_t 结构体
要注意,调用之前需要先给 uv_loop_t 分配内存资源,否则会崩溃
3.2、uv_loop_configure
int uv_loop_configure(uv_loop_t* loop, uv_loop_option option, ...);
设置事件循环配置,一般应该在第一次调用 uv_run() 之前执行
返回值: 0表示成功,错误则返回一个 UV_E 错误码,UV_ENOSYS 表示平台不支持该事件循环配置
支持选项:
- UV_LOOP_BLOCK_SIGNAL :轮询新事件时阻塞指定事件,uv_loop_configure 的第二个参数是信号编号;
- UV_METRICS_IDLE_TIME :在事件的提供者的事件循环中收集空闲时间,使用 uv_metrics_idle_time() 方法需要使用这个选项
3.3、uv_loop_close
int uv_loop_close(uv_loop_t* loop);
释放所有内部循环资源
仅当所有循环完成并且所有打开的句柄和请求都已经关闭的时候才可调用此函数,否则将返回 UV_EBUSY
此函数返回后,用户可以释放为循环申请的内存
3.4、uv_default_loop
uv_loop_t* uv_default_loop(void);
返回 libuv 的默认事件循环,如果分配失败了,可能返回NULL
这个方法是在整个应用程序中进行全局循环的一个便捷的方式,和自定义的事件循环相同
3.5、uv_run
int uv_run(uv_loop_t* loop, uv_run_mode mode);
运行事件循环,mode 指定运行模式,有如下几种:
- UV_RUN_DEFAULT :运行事件循环,知道没有活动的和被引用的句柄和请求。如果使用 uv_stop() 方法终止还未停止的循环(仍然有活动的和被引用的句柄和请求),则返回值非0,其他情况下返回0;
- UV_RUN_ONCE :只进行一次 IO 口轮询,如果没有需要执行的回调函数,这个方法会阻塞,返回0表示完成(没有活动的和被引用的句柄和请求),返回非0表示需要进行更多的回调(此时,需要再次进行事件循环);
- UV_RUN_NOWAIT :和 UV_RUN_ONCE 的区别是,不会阻塞;
uv_run() 方法是不可重入的,它不能作为回调函数被调用
3.6、uv_loop_alive
int uv_loop_alive(const uv_loop_t* loop);
判断事件循环是否还在活动,返回非0表示还在活动
有以下几种情况之一表示还在活动:
- 有被引用的活动句柄或者活动请求;
- 正在关闭的句柄;
3.7、uv_stop
void uv_stop(uv_loop_t* loop);
停止事件循环,会让 uv_run() 方法尽快结束
该方法调用之后,下次事件循环迭代之前结束循环,正在进行的事件循环仍然继续
如果该函数在 IO 阻塞之前执行,则在当前的事件迭代中, IO 不会被阻塞
3.8、uv_loop_size
size_t uv_loop_size(void);
返回 uv_loop_t 结构体 size
3.9、uv_backend_fd
int uv_backend_fd(const uv_loop_t* loop);
获取后端文件描述符,仅使用于 kqueue、epoll 和 事件端口
该方法可以和 run(loop, UV_RUN_NOWAIT) 一起联合使用,在一个线程中轮询 IO 和处理回调
3.10、uv_backend_timeout
int uv_backend_timeout(const uv_loop_t* loop);
获取 IO 轮询超时时间,单位为毫秒,没有超时时返回 -1
3.11、uv_now
uint64_t uv_now(const uv_loop_t* loop);
返回当前的时间戳,单位毫秒
时间戳在事件循环开始时缓存
3.12、uv_update_time
void uv_update_time(uv_loop_t* loop);
更新事件循环的时间戳,会影响 uv_now() 的返回值,libuv 会在事件循环开始时缓存当前时间,以减少系统的时间相关的方法的调用
通常情况下不需要调用此方法,除了事件循环中的某个回调会阻塞相当长的时间,这个所谓的相当长的时间是有些主观的,可能是一毫秒或者更长
3.13、uv_walk
void uv_walk(uv_loop_t* loop, uv_walk_cb walk_cb, void* arg);
遍历句柄列表,执行回调
arg 参数会传递给回调函数
void (*uv_walk_cb)(uv_handle_t* handle, void* arg);
handle 为遍历中的某个句柄
3.14、uv_loop_fork
int uv_loop_fork(uv_loop_t* loop);
在调用 fork(2) 之后,如果有必要,在子进程中重新初始化内核状态
在子进程中以观察者的身份继续事件循环
如果你想继续在子进程中使用事件循环,包括默认的事件循环(尽管不想在父进程中使用它),那么,在每个父进程中创建的事件循环中显示地调用此方法是很有必要的
该方法必须在 uv_run() ,或者其他想要在子进程中调用其他API之前调用。如果不这样做,可将将导致一些未知的错误,比如事件被重复交给父进程和子进程,或者子进程异常退出
可以的话,最好在子进程中创建一个新的循环,而不是重复使用父进程创建的循环。在 fork 子进程之后,并在子进程中创建的新的循环不应该使用此方法
该方法不适用于windows操作系统,他会返回 UV_ENOSYS
需要注意的是,该方法可能存在BUG
3.15、uv_loop_get_data
void* uv_loop_get_data(const uv_loop_t* loop);
返回 loop->data
3.16、uv_loop_set_data
void* uv_loop_set_data(uv_loop_t* loop, void* data);
设置 loop->data 的值