Alby's blog

世上没有巧合,只有巧合的假象。

0%

Libuv 源码分析(2):源码一览

一、概述

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 提供的线程池。

图1: Libuv 子系统

三、跨平台

Libuv 支持 Windows、Linux(Android)、AIX、SunOS(Solaris)、Darwin(macOS/iOS)、BSD(DragonFly、FreeBSD、OpenBSD、NetBSD),最近打算新增对OS/390的支持。

图2: Libuv 支持的操作系统

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_strerroruv_err_name 函数。

3、指针类型星号( * )的位置

这是一个要引起争论的问题。不吵。
Libuv 在定义指针类型变量、声明形参等时,将 * 放在类型后,这样比将 * 挨着变量名头在语义上更贴切。如:

1
uv_handle_t* h;

这需要注意不要在一行定义多个变量( Libuv 中基本不会),否则代码就恶心了:

1
uv_handle_t* h1, *h2;

参考资料