Libuv是一个跨平台的的基于事件驱动的异步io库。但是他提供的功能不仅仅是io,包括进程、线程、信号、定时器、进程间通信等。下面是来自官网对Libuv架构的介绍图。 ? libuv把上层的事件和回调封装成io观察者(uv__io_t)放到底层的事件驱动模块。当事件触发的时候,libuv会执行io观察者中的回调。 Libuv实现一个线程池用来支持上层文件io、dns以及用户层耗cpu的任务。 Libuv的整体执行架构 ? 从上图中我们大致了解到,Libuv分为几个阶段,然后在一个循环里不断执行每个阶段里的任务。下面我们具体看一下每个阶段。 下面是Libuv事件循环实现的逻辑。
#include <stdio.h>#include <spdlog/spdlog.h>#include <uv.h>#include <string>uv_loop_t *loop;uv_fs_t open_fs;uv_buf_t uv_buf;static char buffer[1024];std::string result;void on_read_cb(uv_fs_t *req){ uv_fs_req_cleanup(req); if (req->result < 0) {
#include <stdio.h>#include <spdlog/spdlog.h>#include <string>#include <uv.h>typedef struct { std::string data; char buffer[10]; uv_file open_file_no; size_t wirte_index;} write_context;void on_write_cb(uv_fs_t *req){ auto data = (write_cont
// libuv实现边写边读#include <stdio.h>#include <spdlog/spdlog.h>#include <uv.h>typedef struct { char buffer
Libevent、libev、libuv三个网络库,都是c语言实现的异步事件库Asynchronousevent library)。 对比下三个库: libevent :名气最大,应用最广泛,历史悠久的跨平台事件库; libev :较libevent而言,设计更简练,性能更好,但对Windows支持不够好; libuv :开发node 可见,目前libuv的影响力最大,其次是libevent,libev关注的人较少。 libev中的embed很少用,libuv没有也没关系;cleanup完全可以用libuv中的async_exit来替代;libuv没有fork事件。 所以如果是在windows平台下,使用原生的IOCP进行I/O,或者使用libuv。
简单来说就是调用linux系统的:getifaddrs 和 freeifaddrs,读取系统的网卡接口信息,然后拷贝到用户的缓冲区中,然后再释放函数内部的内存。逻辑比较简单,就不仔细研究网卡接口信息的结构体了。
先看用例源码: #include <stdio.h> #include <uv.h> int64_t counter = 0; void idle_cb(uv_idle_t *handle) { printf("Idle callback\n"); counter++; // 计数停止 if (counter >= 5) { uv_stop(uv_default_loop()); printf("uv_stop() called\
2. 创建2个读者 1个写者 根据读写锁 被系统调度分配执行时机 输出对应自己的读到或者写后的值
功能很简单:载入启动参数中对应的插件动态库,调用它们的 initialize 方法
Something * to revisit in future revisions of the libuv API. */ if (req->error == 0) { if
. */ int stdio_count; uv_stdio_container_t* stdio; /* * Libuv can change the child process
libuv的queue实现得很博大精深。严重考验了c指针的理解。今天就分享一下他的实现。 下面我们接着分析四个举足轻重的宏定义,理解他们就相当于理解了libuv的队列。在分析之前,我们先来回顾一下数组指针和二维数组的知识。 因为libuv的数组只有两个元素。相当于p[2][2]变成了*p[2][1]。所以上面的代码简化为。 在libuv中如下 ? *(QUEUE *) &(((q))[0])解引用取得q下一个节点的地址(作为右值),或者修改当前节点的next域内存里的值(作为左值),类型是void (*)[2]。 这就是libuv队列的亮点了。下面我们看一下这些宏的使用。
总结:信号处理handler是被插入到红黑树中,按照一定规则排序插入的,信号越小,不带oneshot等规则。信号处理函数统一触发信号管道可读,然后loop从信号io管道可读端读取信号结构体,执行这个信号上的handler的回调。大概主体流程就是这样的。跟我们平常自己写某些信号的handler的方法类似:注册信号和信号函数,触发信号管道可读,主循环捕获io可读事件,根据信号值调用对应回调。
可以看到 fs_event_s 也是由基础的handler和一个path 以及 它独有的字段组成
总结:用户自己初始化的async handler 也可以被插入到异步handler队列中,当管道[0]可读的时候,代表某个异步handler可以处理了,这时候遍历队列,处理pengding状态的handler。
运行 loop 循环; 3. loop循环结束后 close做一些清理工作; 接下来详细分析这几个过程: 首先,libuv中总共主要提供了如下一些结构体: /* Handle types. */ typedef work_done uv__work_done 就从工作队列中用锁 同步取出任务 执行我们在初始化异步任务的时候绑定的done回调 这个逻辑就是代表着 其他某个工作线程已经完成了任务 可以执行成功回调了 libuv By then, libuv has forgotten about the * hangup and the kernel won't report EPOLLIN again because If anything, libuv is to blame here. The * current hack is just a quick bandaid; to properly fix it, libuv * needs to remember
先看用例源码: #include <assert.h> #include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <uv.h> void on_read(uv_fs_t *req); // 几个req请求 uv_fs_t open_req; uv_fs_t read_req; uv_fs_t write_req; // 读写的缓冲区都是这个 static char buffer[1024]; static uv_buf_t
libuv在inotify机制的基础上做了一层封装。 今天分析一下libuv中的实现。我们从一个使用例子开始。 , cb, argv[argc], UV_FS_EVENT_RECURSIVE); // 开启事件循环 return uv_run(loop, UV_RUN_DEFAULT); } libuv 并且往libuv插入一个观察者io,libuv会在poll io阶段注册到epoll中。 2 往操作系统注册一个待监听的文件。返回一个id。 3 libuv判断该id是不是在自己维护的红黑树中。 libuv在poll io阶段如果检测到有文件发生变化,则会执行回调uv__inotify_read。 总结:本文介绍了libuv中的inotify机制。他是对操作系统的封装,但是也加入了自己的一些逻辑。文中有很多地方没有展开分析,是因为在之前的文章中已经分析过了很多次。如果有疑问可以留言。
之前分析了unix域在libuv的基本原理。今天以一个简单的例子看一下如何使用它。本文涉及到一些网络编程的知识,不过文章不打算讲解这些,如果不了解可以先了解一下,或者留言。
libuv初始化的时候会初始化信号处理相关的逻辑。 进程或fork出来的进程)和libuv进程通信。 然后往libuv的io观察者队列注册一个观察者,libuv在poll io阶段会把观察者加到epoll中。 接下来在libuv的poll io阶段才做真正的处理。回到文章开头我们知道在poll io阶段。 如果是第一次调用,则申请一个管道,然后把管道的读端fd和回调封装成一个观察者oi,插入libuv的观察者队列。libuv会在poll io阶段往epoll里插入。