libuv强制使用异步和事件驱动的编程风格。它的核心工作是提供一个
event-loop
,还有其他基于IO和其他事件通知的回调函数。libhv
还提供了一些核心工具,比如定时器,非阻塞的网络支持,异步文件系统访问,子进程等
实践
接下来我们安装这个程序
https://github.com/libuv/libuv
在README.md里,我们就可以在Build Instructions找到安装方法
$ tar -xvf libuv-v1.9.1.tar.gz
$ sh autogen.sh
+ libtoolize --copy
autogen.sh:行43: libtoolize: 未找到命令
$ yum install libtool
$ ./configure
$ make
$ make check
[% 96|+ 273|- 1|T 0|S 2]: thread_stack_size
`thread_stack_size` skipped
Output from process `thread_stack_size`:
OSX only test
=============================================================
[% 100|+ 282|- 1|T 0|S 3]: Done.
FAIL: test/run-tests
======================================================
1 of 1 test failed
Please report to https://github.com/libuv/libuv/issues
======================================================
make[1]: *** [check-TESTS] 错误 1
make[1]: 离开目录“/home/oceanstar/CLionProjects/libuv-v1.9.1”
make: *** [check-am] 错误 2
$ make install
在事件驱动编程中,程序会关注每一个事件,并且对每一个事件的发生做出反应。libuv会负责将来自操作系统的事件收集起来,或者监视其他来源的事件。这样,用户就可以注册回调函数,回调函数将会在事件发生的时候被调用。event-loop
会一直保持运行的状态。用伪代码描述如下:
while there are still events to process:
e = get the next event
if there is a callback associated with e:
call the callback
event-loop
最终会被uv_run()启动-:当使用libuv
时,最后都会调用uv_run()
系统编程最经常处理的一般是输入和输出,而不是一大堆数据处理。问题在于传统的输入、输出函数(比如read
、fprintf
)都是阻塞的。实际上,系统IO操作比CPU慢太多,如果任务没有完成,函数是不会返回的,所以程序在这段时间内什么都做不了。对于需要高性能的程序来说,这是一个主要的障碍因为其他活动和I/O操作都在保持等待。
其中一个标准的解决方案是使用多线程。每一个阻塞的IO操作都会被分配到各个线程中(或者线程池)。当某个线程一旦阻塞,处理器就可以调度处理其他需要CPU资源的线程。
但是libuv使用了另外一个解决方案,那就是异步,非阻塞风格。大多数的现代操作系统提供了基于事件通知的子系统。例如,一个正常的socket上的read调用会发生阻塞,直到发送方把信息发送过来。但是,实际上程序可以请求操作系统监视socket事件的到来,并将这个事件通知放到事件队列中。这样,程序就可以很简单地检查事件是否到来(可能此时正在使用cpu做数值处理的运算),并及时地获取数据。
- 说libuv是异步的,是因为程序可以在一头表达对某一事件的兴趣,并在另一头获取到数据(对于时间或是空间来说)
- 它是非阻塞是因为应用程序无需在请求数据后等待,可以自由地做其他的事
让我们开始写第一个libuv程序吧!它什么都没做,只是开启了一个loop,然后很快地退出了。
cmake_minimum_required(VERSION 3.16)
project(libuv_test C)
set(CMAKE_C_STANDARD 99)
link_directories(usr/local/lib)
include_directories(usr/local/include)
add_executable(${PROJECT_NAME} main.c)
target_link_libraries (${PROJECT_NAME} -luv ) #libuv.a
#include <stdio.h>
#include <stdlib.h>
#include <uv.h>
int main() {
uv_loop_t *loop = malloc(sizeof(uv_loop_t));
uv_loop_init(loop);
printf("Now quitting.\n");
uv_run(loop, UV_RUN_DEFAULT);
uv_loop_close(loop);
free(loop);
return 0;
}
运行,出错了:
错误: error while loading shared libraries: libuv.so.1: cannot open shared object file: No such file or directory
原因:ld提示找不到库文件,而库文件就在/usr/local/lib目录下。
链接器ld默认的目录是/lib/和/usr/lib,如果放在其他路径也可以,需要让ld知道库文件在哪里。
解决:
在/etc/ld.so.conf.d/目录下新建任何以.conf为后缀的文件,如
$sudo vim libuv.conf
内容如下:
/usr/local/lib
运行,如下命令,以更新/etc/ld.so.cache文件;
sudo ldconfig
继续运行:
我们可以发现程序很快就退出了,因为没有可以处理的事件。我们可以使用各种API函数来告诉event-loop
我们要监视的事件。
- 从libuv的1.0版本开始,用户可以在使用
uv_loop_init
之前,给其分配相应的内存。我们可以使用自定义的内存管理方法 - 记住要使用uv_loop_close(uv_loop_t *)关闭loop,然后再回收内存空间
- 可以使用
uv_default_loop
获取libuv提供的默认loop。如果你只需要一个loop的话,可以使用这个。
libevent、libev、libuv
- libevent :名气最大,应用最广泛,历史悠久的跨平台事件库
- libev :较 libevent 而言,设计更简练,性能更好,但对 Windows 支持不够好;
- libuv :开发 node 的过程中需要一个跨平台的事件库,他们首选了 libev,但又要支持Windows,故重新封装了一套,linux 下用 libev 实现,Windows 下用 IOCP 实现;
优先级:
-
libevent:激活的事件组织在优先级队列中,各类事件默认的优先级是相同的,可以通过设置事件的优先级使其优先被处理
-
libev:也是通过优先级队列来管理激活的时间,也可以设置事件优先级
-
libuv:没有优先级概念,按照固定的顺序访问各类事件
资料
两个重要的libuv学习库
https://github.com/nikhilm/uvbook
翻译的libuv的中文教程
https://github.com/luohaha/Chinese-uvbook