备忘。
1. 错误处理
一个库,让用户知道内部错误,一般就是返回错误码,直接从函数返回;或者用一个错误接口统一起来,在函数调用后告诉用户(类似openssl)。libuv用的是前一种。
关于错误码libuv提供有几个常用的接口,自己写库的时候也可以参考。
1 | // 错误码--->错误描述字符串 |
2. 版本号的处理
libuv提供有几个宏,用于在编译时候区分版本:
1 | UV_VERSION_MAJOR |
还有两个用于在运行时区分版本的函数:
1 | // 返回 UV_VERSION_HEX |
编译时和运行时都需要,所以要给用户提供宏和函数两种形式的版本号。
3. libuv中的对象
libuv中组对象的方式很有意思,比如说 uv_handle_t
这样定义:
1 | typedef struct uv_handle_s uv_handle_t; |
然后uv_stream_t
是这样定义的:
1 | typedef struct uv_stream_s uv_stream_t; |
另外还有 uv_tcp_t
:
1 | typedef struct uv_tcp_s uv_tcp_t; |
可以看到 uv_handle_t, uv_stream_t, uv_tcp_t
第一个都是 UV_HANDLE_FIELDS
; uv_stream_t, uv_tcp_t
第二个都是 UV_STREAM_FIELDS
,这样就可以把 uv_handle_t
当成父类,uv_stream_t, uv_tcp_t
当成子类和孙子——当然要用到c的强制类型转换。这其实不算是个很好的用法,但好在简单。
有些对象中的字段可以给用户使用,但其实所有字段对用户都是公开的,这就需要做好注释,没有办法用编译器去约束。
libuv中主要对象有以下这些:
uv_loop_t
: libuv的事件驱动对象,也就是主循环在这上边挂着,关于事件相关的上下文都在这里;uv_handle_t
:表示一个基本对象,这个相当于父类,下边还有子类;uv_req_t
: 一个连接上的具体请求,比如说在tcp上调用int uv_write(uv_write_t* req, uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs, uv_write_cb cb)
进行一次写操作, 这个操作会立马返回,写成功之后的结果会在void (*uv_write_cb)(uv_write_t* req, int status)
中返回给用户,这里uv_req_t
相当于在uv_write()
和uv_write_cb()
之间传递libuv和用户自己的一些数据的。可以看到这也是个父类;
uv_handle_t
下又分:
uv_timer_t
: 定时器;uv_prepare_t
: 每次轮训io之前,会调用这个事件句柄一次;uv_check_t
: 每次轮训io之后,会调用这个;uv_idle_t
: 每次循环在uv_prepare_t
之前会调用这个,跟uv_prepare_t
不同的是,如果有uv_idle_t
句柄,epoll
会立马返回,不会阻塞(超时等待);uv_async_t
: 提供一个异步通知句柄,可以将当前任务放到额外的线程去做,然后用该句柄通知主循环最后的结果。有eventfd
就用eventfd
实现,没有就用pipe
实现;uv_poll_t
: 有些第三方库依赖socket事件通知进行驱动,该句柄可以将这些句柄接入libuv的主循环去监听读写和连接的事件;uv_signal_t
: 信号封装;uv_process_t
: 进程封装;uv_stream_t
: 双工连接句柄。有三个子类:uv_tcp_t, uv_pipe_t, uv_tty_t
。还有几个相关的uv_req_t
子类:uv_connect_t, uv_shutdown_t, uv_write_t
;uv_udp_t
: 封装UDP通信句柄,还有个相关的req
子类:uv_udp_send_t
;uv_fs_event_t
: 可以监听一个文件路径的变化,比如文件重命名等。uv_fs_poll_t
: 跟uv_fs_event_t
类似,但使用state
去监听路径变化。
uv_req_t
下又分:
uv_connect_t
:uv_stream_t
建立连接的请求uv_shutdown_t
:uv_stream_t
关闭连接uv_write_t
:uv_stream_t
写请求uv_udp_send_t
:uv_udp_t
写请求uv_fs_t
: 文件异步操作请求uv_work_t
: 线程池异步任务请求uv_getaddrinfo_t
和uv_getnameinfo_t
: 对DNS的异步请求uv_random_t
: 生成随机数的请求
4. 事件驱动的一个常规流程
就是这个图了