Libevent 源码剖析一

libevent概述

Libevent 是一个用C语言编写的、轻量级的开源高性能事件通知库,主要有以下几个亮点:事件驱动( event-driven),高性能;轻量级,专注于网络,不如 ACE 那么臃肿庞大;源代码相当精炼、易读;跨平台,支持 Windows、 Linux、 *BSD 和 Mac Os;支持多种 I/O 多路复用技术, epoll、 poll、 dev/poll、 select 和 kqueue 等;支持 I/O,定时器和信号等事件;注册事件优先级。

Libevent 已经被广泛的应用,作为底层的网络库;比如 memcached、 Vomit、 Nylon、 Netchat等等。
链接-来自百度百科: https://baike.baidu.com/item/libevent/.

libevent下载和安装

github:https://github.com/libevent/libevent
官网:https://libevent.org/

安装:tar -zxvf libevent-2.1.11-stable.tar.gz cd
libevent-2.1.11-stable ./configure --prefix=/usr/local/ (你需要的指定路径)
sudo make && make install

注:运行是出现找不到libevent.so库的情况,这是链接时没有将你的链接库添加进去
可以参考:https://blog.csdn.net/mybelief321/article/details/9099659

libevent源码组织架构

  • 头文件 :主要就是event.h :事件宏定义,接口函数声明,主体结构体event的声明
  • 内部头文件:xxx-internal.h,内部的数据结构,对外界不可见,以达到信息隐藏的目的
  • libevent框架:event.c 对整体框架的实现
  • 对IO复用的封装:epoll.c/select.c/devpoll/kequeue.c
  • 定时事件管理:min-heap.h 一个以时间作为key的小根堆的结构
  • 信号管理:signal.c:对信号的处理
  • 辅助功能函数:evutil.h/evutil.c:包括创建socket pair和一些时间操作函数:加减和比较等等
  • 日志:log.h/log.c : log日志函数
  • 缓冲区管理:evbuffer.c / buffer.c : libevent对缓冲区的封装
  • 基本数据结构:compat/sys : queue.h是libevent基本数据结构的实现,包括链表,双向链表,队列等等;_libevent_time.h : 用于时间操作的结构体定义函数和宏定义
  • 实用网络库:http/evdns : 基于libevent实现的http服务器和异步dns查询库

Reactor模式

组成:事件源、框架部分(Reactor)、事件多路分发机制(event demultiplexing)、事件处理程序(event handler)。

在这里插入图片描述
事件源:Linux 上的文件描述符,程序指定的句柄上注册关心的事件,比如IO事件

event demultiplexer——事件多路分发机制
由操作系统提供的I/O多路复用机制,比如select和epoll。
程序首先将其关心的句柄(事件源)及其事件注册到event demultiplexer上;
当有事件到达时,event demultiplexer会发出通知“在已经注册的句柄集中,一个或多个句柄的事件已经就绪”;
程序收到通知后,就可以在非阻塞的情况下对事件进行处理了。
对应到libevent中,依然是select、poll、epoll等,但是libevent使用结构体eventop进行了封装,以统一的接口来支持这些I/O多路复用机制,达到了对外隐藏底层系统机制的目的。

Reactor——反应器
Reactor,是事件管理的接口,内部使用event demultiplexer注册、注销事件;并运行事件循环,当有事件进入“就绪”状态时,调用注册事件的回调函数处理事件。
对应到libevent中,就是event_base结构体。
一个典型的Reactor声明方式

class Reactor  
{  
public:  
    int register_handler(Event_Handler *pHandler, int event);  
    int remove_handler(Event_Handler *pHandler, int event);  
    void handle_events(timeval *ptv);  
    // ...  
};  

Event Handler——事件处理程序
事件处理程序提供了一组接口,每个接口对应了一种类型的事件,供Reactor在相应的事件发生时调用,执行相应的事件处理。通常它会绑定一个有效的句柄。
对应到libevent中,就是event结构体。
下面是两种典型的Event Handler类声明方式,二者互有优缺点。

class Event_Handler  
{  
public:  
    virtual void handle_read() = 0;  
    virtual void handle_write() = 0;  
    virtual void handle_timeout() = 0;  
    virtual void handle_close() = 0;  
    virtual HANDLE get_handle() = 0;  
    // ...  
};  
class Event_Handler  
{  
public:  
    // events maybe read/write/timeout/close .etc  
    virtual void handle_events(int events) = 0;  
    virtual HANDLE get_handle() = 0;  
    // ...  
};  

在这里插入图片描述

EPOLL反应堆详解(libevent核心)

  1. 第一步,epoll反应堆模型雏型 ----- epoll模型
    epoll模型和epoll接口本质区别在于epoll模型传入联合体的是一个自定义结构体指针,该结构体的基本结构包括
		struct my_events {  
    int        m_fd;                             //监听的文件描述符
    void       *m_arg;                           //泛型参数
    void       (*call_back)(void *arg);          //回调函数
    /*
     *  你可以在此处封装更多的数据内容
     *  例如用户缓冲区、节点状态、节点上树时间等等
     */
     int Status; //1 代表被监听(添加到RBTree), 0 代表没有被监听
	 char buf[BUFLEN];
	 long last_active;//记录最后一次响应时间,做超时处理
};
/*
 * 注意:用户需要自行开辟空间存放my_events类型的数组,并在每次上树前用epoll_data_t里的  
 *      ptr指向一个my_events元素。
 */

2.epoll_wait()返回直接调用事件中对应的回调函数,就像这样

/*
 *  -[ epoll模型使用描述01  ]-
 */
 while(1) {
      /* 监听红黑树, 1秒没事件满足则返回0 */ 
      int n_ready = epoll_wait(ep_fd, events, MAX_EVENTS, 1000);
      if (n_ready > 0) {
         for (i=0; i<n_ready; i++) 
            events[i].data.ptr->call_back(/* void *arg */);
       }
       else
          /*  
           * (3) 这里可以做很多很多其他的工作,例如定时清除没读完的不要的数据
           *     也可以做点和数据库有关的设置
           *     玩大点你在这里搞搞分布式的代码也可以
           */
 }

在这里插入图片描述
到了这里,也将是epoll的最终成型,如果从前面到这里你都明白了,epoll的知识你已经十之七八了
让我们先回想以下epoll模型的那张图,我们来理一理思路。
(1) 程序设置边沿触发以及每一个上树的文件描述符设置非阻塞
(2) 调用epoll_create()创建一个epoll对象
(3) 调用epoll_ctl()向epoll对象中进行增加、删除等操作
上树的文件描述符与之对应的结构体,该结构体应该满足填充事件与自定义结构体ptr,此时,监听的事件与回调函数已经确定了对吧?
(4) 调用epoll_wait()(定时检测) 返回待处理的事件集合。
(5) 依次调用事件集合中的每一个元素中的ptr所指向那个结构体中的回调函数。
以上为雏形版本,那么epoll反应堆模型还要比这个雏形版本多了什么呢?
请看第三步的粗体字,当我们把描述符和自定义结构体上树以后,如果放的是监听可读事件并做其对应的回调操作。也就是说,它将一直作为监听可读事件而存在。
其流程是:
监听可读事件(ET) ⇒ 数据到来 ⇒ 触发事件 ⇒ epoll_wait()返回 ⇒ 处理回调 ⇒ 继续epoll_wait() ⇒ 直到程序停止前都是这么循环

那么接下来升级为成型版epoll反应堆模型
其流程是:
监听可读事件(ET) ⇒ 数据到来 ⇒ 触发事件 ⇒ epoll_wait()返回 ⇒
读取完数据(可读事件回调函数内) ⇒ 将该节点从红黑树上摘下(可读事件回调函数内) ⇒ 设置可写事件和对应可写回调函数(可读事件回调函数内) ⇒ 挂上树(可读事件回调函数内) ⇒ 处理数据(可读事件回调函数内)
⇒ 监听可写事件(ET) ⇒ 对方可读 ⇒ 触发事件 ⇒ epoll_wait()返回 ⇒
写完数据(可写事件回调函数内) ⇒ 将该节点从红黑树上摘下(可写事件回调函数内) ⇒ 设置可读事件和对应可读回调函数(可写读事件回调函数内) ⇒ 挂上树(可写事件回调函数内) ⇒ 处理收尾工作(可写事件回调函数内) ⇒ 直到程序停止前一直这么交替循环
————————————————
https://blog.csdn.net/qq_36359022/article/details/81355897

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
libevent是一个事件驱动的网络编程库,适用于高并发的网络应用。通过深度剖析libevent源码,我们可以更好地理解其工作原理和实现机制。 首先,在下载libevent源码之前,我们需要确认所需的版本和平台兼容性,这样可以避免不必要的错误和兼容性问题。 在深度剖析libevent源码时,我们可以从以下几个方面入手: 1. 事件循环机制:libevent基于事件循环机制实现事件的响应和处理。源码中会包含事件循环的实现细节,如事件的注册、删除、触发等操作。研究这些实现可以帮助我们理解事件驱动模型的运行机制。 2. IO多路复用:libevent在底层使用了IO多路复用技术,可以同时处理多个网络连接,提高并发处理能力。源码中会涉及到IO多路复用的实现细节,如select、epoll等。了解这些实现可以帮助我们深入理解libevent是如何高效地管理和处理网络连接的。 3. 常用数据结构和算法:libevent源码中使用了一些常用的数据结构和算法,如链表、堆等。通过研究这些数据结构和算法的实现,可以提高我们对libevent整体架构的理解。 4. 错误处理和调试机制:源码中通常也会包含一些错误处理和调试机制,可以帮助我们排查和解决问题。了解这些机制可以提高我们在使用libevent时的调试和排错能力。 总之,深度剖析libevent源码可以帮助我们更好地理解其工作原理和实现机制,从而更好地使用和调优libevent,提高网络应用的性能和并发处理能力。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值