一、概述
Libuv 采用了异步( asynchronous ), 事件驱动( event-driven )的编程风格, 其核心任务是为开人员提供了一套事件循环和基于 I/O (或其他活动)通知的回调函数, Libuv 提供了一套核心的工具集, 例如定时器, 非阻塞网络编程的支持, 异步访问文件系统, 子进程以及其他功能。
二、I/O 复用机制和线程池
对于网络 I/O ,Libuv 根据将不同的操作系统提供的 I/O 多路复用机制进行了封装;对于文件 I/O、域名解析等,使用了线程池(在 Libuv 0.10, Windows 上使用的是 QueueUserWorkItem
。但在 1.0 也采用和 UNIX 一样的线程池实现。)。当然,用户代码也可以使用 Libuv 提供的线程池。
三、跨平台
Libuv 支持 Windows、Linux(Android)、AIX、SunOS(Solaris)、Darwin(macOS/iOS)、BSD(DragonFly、FreeBSD、OpenBSD、NetBSD),最近打算新增对OS/390的支持。
Windows 和 UNIX/Linux 是两个”敌对”阵营,二者在API上就有较大差异; Linux 和 UNIX 之间,及 UNIX 实现之间,一些 API 、系统限制和选项也不尽相同。下表作出了部分对比:
OS | I/O 多路复用 | 线程 | Socket | extern |
---|---|---|---|---|
Windows | IOCP | Widows Thread | winsock | 动态库导入导出函数、数据、类( Libuv 中主要是函数)需:__declspec(dllexport) __declspec(dllimport) 直接使用源码当然不需要。 |
Linux | epoll | POSIX Thread | socket | 如果使用 GCC (使用 __GNUC__ 测试宏)编译需: __attribute__((visibility("default"))) |
AIX | IOCP ? | 同 Linux | 同 Linux | 同 Linux |
SunOS | event ports | 同 Linux | 同 Linux | 同 Linux |
BSD | kqueue | 同 Linux | 同 Linux | 同 Linux |
Darwin | 同 BSD | 同 Linux | 同 Linux | 同 Linux |
OS/390 | - | - | - | - |
四、第三方源码
Libuv 使用了少量的第三方源码。
源码 | 目的 | 作用 | 备注 |
---|---|---|---|
tree.h | 数据结构和算法 | 提供伸展树( Splay Tree )和红黑树( R-B Tree )的数据结构宏。 | macOS 中的 Kernel.framework 也有该源码。 Libuv 只使用了红黑树。 |
inet.c | 可移植性 | 提供 IP 地址转换函数。 | Linux/UNIX 对应的函数声明位于 /usr/lib/arpa/inet.h。 Windows 虽然有 InetPton 和 InetNtop 等函数可用,也有 LibC for Windows ——不如直接使用一套源码省事。 |
stdint-msvc2008.h | 可移植性 | C 标准库的整数类型别名定义。 | 对于 Windows ,_MSC_VER 1600 ( MS VC++ 10.0 , Visual C++ 2010 )之前需 include stdint-msvc2008.h,之后可直接 include C 标准库中的 stdint.h 。 |
五、代码风格
1、函数、结构和枚举的命名规则
uv_
开头: 公开的函数、结构和枚举都以 uv_
(一个下划线)开头的。uv__
开头: 内部使用的函数、结构和枚举,部分是以 uv__
(两个下划线)开头的。_cb
结尾: 函数指针。_s
结尾: 结构声明。_t
结尾: 使用 typedef 为结构体取的别名。
2、错误处理
一般情况下,Libuv 0.10 函数返回0表示成功, -1 表示失败,通过 uv_last_error
函数获取错误码。在 1.0 ,函数返回 0 表示成功,负数表示失败并对应 UV__E*
错误码; uv_last_error
函数已经在 1.0 被删除。
这里说的函数只要指部分以 int uv_*
格式命名的导出函数;详细的错误号定义见 uv-errno.h 文件;错误号到错误消息的转换、获取错误号对应的错误名分别见 uv_strerror
和 uv_err_name
函数。
3、指针类型星号( * )的位置
这是一个要引起争论的问题。不吵。
Libuv 在定义指针类型变量、声明形参等时,将 *
放在类型后,这样比将 *
挨着变量名头在语义上更贴切。如:
1 | uv_handle_t* h; |
这需要注意不要在一行定义多个变量( Libuv 中基本不会),否则代码就恶心了:
1 | uv_handle_t* h1, *h2; |