C/C++编程:从0到1学习libuv(linux、Clion)

  • Post author:
  • Post category:linux


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