libev 学习笔记之主体事件循环
来源:互联网 发布:甲醛无色无味 知乎 编辑:程序博客网 时间:2024/05/17 06:40
一. epoll简介
作为linux下的IO多路复用神器,一经问世便得到了众多程序员的赞赏,其出色的绑定 fd 监听事件能力使其在有 x 个事件同时触发时能够在O(x)时间内处理到激活的事件,此非select和poll所能比。
在linux环境下,使用epoll作为libev的后端支持是极好的选择。
二. libev中对epoll的封装
1. ev_epoll.c文件
libev能够在编译时依赖系统的宏定义来自动选择恰当的后端事件循环支撑。linux下,首选epoll。libev对epoll进行了一次简单的封装,代码位于ev_epoll.c
文件。
2. 主要使用的epoll相关函数
epoll_create
epoll_create() returns a file descriptor referring to the new epoll instance. This file descriptor is used for all the sub‐sequent calls to the epoll interface. When no longer required, the file descriptor returned by epoll_create() should be closed by using close(2). When all file descriptors referring to an epoll instance have been closed, the kernel destroys the instance and releases the associated resources for reuse.
一言以蔽之: 这个系统调用会在创建一个epoll实例,返回一个与该实例相关连的 epfd 供后续epoll类函数调用
epoll_ctl
This system call performs control operations on the epoll(7) instance referred to by the file descriptor epfd. It requests that the operation op be performed for the target file descriptor, fd.
一言以蔽之: 这个系统调用用来往 epoll_create 返回的 epfd 相关联的epoll实例内 添加/修改/删除监听事件
epoll_wait
The epoll_wait() system call waits for events on the epoll(7) instance referred to by the file descriptor epfd. The memory area pointed to by events will contain the events that will be available for the caller. Up to maxevents are returned by epoll_wait(). The maxevents argument must be greater than zero.
一言以蔽之: 此系统调用用来等待通过 epoll_ctl 往 epfd 相关的 epoll 实例内添加的事件就绪
3. epoll封装函数分析
a. epoll_modify
函数原型: void epoll_modify (EV_P_ int fd, int oev, int nev)
函数说明: 用来往epoll中添加或修改 fd关联的事件
参数说明: EV_P_
宏留在下文介绍,fd 即为往epoll中待添加或修改事件所关联的fd,oev为old event,nev为new event
功能实现: 调用epoll_ctl来实现添加/修改
核心代码:
struct epoll_event ev;.../* store the generation counter in the upper 32 bits, the fd in the lower 32 bits */ev.data.u64 = (uint64_t)(uint32_t)fd | ((uint64_t)(uint32_t)++anfds [fd].egen << 32);ev.events = (nev & EV_READ ? EPOLLIN : 0) | (nev & EV_WRITE ? EPOLLOUT : 0);if (expect_true (!epoll_ctl (backend_fd, oev && oldmask != nev ? EPOLL_CTL_MOD : EPOLL_CTL_ADD, fd, &ev))) return;
可以看到,待添加或修改的事件相关联的 fd 被设置在struct epoll_event
结构体的data.u64
的低32位,后续代码中会使用同样的方式来获取就绪事件关联的 fd。另外,值得关注的是此处的epoll只关注EPOLLIN
和EPOLLOUT
两种事件
b. epoll_poll
函数原型: void epoll_poll (EV_P_ ev_tstamp timeout)
函数说明: 设定超时时间为timeout
,等待所监听的事件就绪
参数说明: timeout
,单位为s
,类型为double
功能实现: 调用epoll_wait来实现事件监听
c. epoll_init
函数原型: int epoll_init (EV_P_ int flags)
函数说明: 生成epoll系列函数所需的与epoll实例相关的 epfd,初始化相关参数
参数说明: flags
,未使用
功能实现: 调用epoll_create来创建 epfd
d. epoll_destroy
函数原型: void epoll_destroy (EV_P)
函数说明: 释放空间
e. epoll_fork
函数原型: void epoll_fork (EV_P)
函数说明: 关闭原有 epfd,生成新的 epfd,清空老的epfd上管理的所有 fd上监听的事件
4. 多事件循环机制
a. EV_P系列宏定义:
/* support multiple event loops? */#if EV_MULTIPLICITYstruct ev_loop;# define EV_P struct ev_loop *loop /* a loop as sole parameter in a declaration */# define EV_P_ EV_P, /* a loop as first of multiple parameters */# define EV_A loop /* a loop as sole argument to a function call */# define EV_A_ EV_A, /* a loop as first of multiple arguments */# define EV_DEFAULT_UC ev_default_loop_uc_ () /* the default loop, if initialised, as sole arg */# define EV_DEFAULT_UC_ EV_DEFAULT_UC, /* the default loop as first of multiple arguments */# define EV_DEFAULT ev_default_loop (0) /* the default loop as sole arg */# define EV_DEFAULT_ EV_DEFAULT, /* the default loop as first of multiple arguments */#else# define EV_P void# define EV_P_# define EV_A# define EV_A_# define EV_DEFAULT# define EV_DEFAULT_# define EV_DEFAULT_UC# define EV_DEFAULT_UC_# undef EV_EMBED_ENABLE#endif
可见,libev支持多个事件循环,这在多线程环境下能够支持经典的多线程并发模型per thread per loop
。而在不至此多时间循环时,EV_P系列宏定义为空,即此时在编译时,原有的函数都将自动去掉第一个struct ev_loop *loop
使得其成为仅支持单时间循环的函数。
b. ev_loop 结构
#if EV_MULTIPLICITY struct ev_loop { ev_tstamp ev_rt_now; #define ev_rt_now ((loop)->ev_rt_now) #define VAR(name,decl) decl; #include "ev_vars.h" #undef VAR }; #include "ev_wrap.h" static struct ev_loop default_loop_struct;#else EV_API_DECL ev_tstamp ev_rt_now = 0; /* needs to be initialised to make it a definition despite extern */ #define VAR(name,decl) static decl; #include "ev_vars.h" #undef VAR static int ev_default_loop_ptr;#endif
由以上宏定义可知:
- 在可使用多事件循环机制的情况下,会定义出
struct ev_loop
结构体,并且注意到,在该结构体内借由#include "ev_vars.h"
机制定义了诸多的成员变量,之后再通过#include "ev_wrap.h"
来将诸多成员变量使用宏重定义 - 在不可使用多事件循环机制的情况下,则是直接通过
#include "ev_vars.h"
机制来将之前定义在struct ev_loop
结构体内的成员变量直接定义为全局变量
这样做是何意图,相信读者心中早已明了。就是为了使libev相关函数,即支持多事件循环机制(ev_vars.h
里的所有变量均为struct ev_loop
成员变量)的编译又支持单事件循环机制(ev_vars.h
里的所有变量均为全局变量)的编译。而struct ev_loop
的成员访问方式和全局变量的访问方式必定不同,libev又是如何解决这个问题的呢?
5. ev_wrap.h文件
在ev_wrap.h
文件中使用宏重定义了许多struct ev_loop *loop
结构体的成员,借以实现支持多事件循环(ev_vars.h
里的所有变量均为struct ev_loop
成员变量)和支持单事件循环(ev_vars.h
里的所有变量均为全局变量)两种编译模式。
其中部分定义如下:
...#define anfdmax ((loop)->anfdmax)#define anfds ((loop)->anfds)#define async_pending ((loop)->async_pending)#define asynccnt ((loop)->asynccnt)#define asyncmax ((loop)->asyncmax)#define asyncs ((loop)->asyncs)#define backend ((loop)->backend)#define backend_fd ((loop)->backend_fd)#define backend_mintime ((loop)->backend_mintime)#define backend_modify ((loop)->backend_modify)#define backend_poll ((loop)->backend_poll)...
可见,如此一来,对于支持多事件循环(ev_vars.h
里的所有变量均为struct ev_loop
成员变量)的libev而言,在libev与loop相关函数中便可使用struct ev_loop
成员变量的变量名来直接操作;而对于不支持多事件循环的libev(ev_vars.h
里的所有变量均为全局变量)而言,使用相同的变量名即为直接操作全局变量。
因此,在libev函数中遇到“未定义”的变量直接使用的情况,多半是作为struct ev_loop
的成员变量(支持多事件循环机制)或全局变量(不支持多事件循环机制)来使用。
6. 主体事件循环
经过上述分析,主体事件循环流程可谓显而易见。即通过不断的epoll_wait
来实现事件等待及分发。通过支持多事件循环的struct ev_loop
结构或不支持多事件循环的众多全局变量来实现loop中所有监听事件的管理。
- libev 学习笔记之主体事件循环
- libev 学习笔记之源码树
- libev 学习笔记之timer实现原理
- Libev学习笔记1
- libev 学习笔记
- libev 中IO事件循环解析
- Qt事件循环、IO、基于libev的Qt事件循环
- 四、机器学习系统设计笔记之主体模型
- HTML学习笔记之--HTML主体的常用设置
- Libev 官方文档学习笔记
- Libev 官方文档学习笔记
- Libev 官方文档学习笔记
- nodejs事件循环学习笔记
- Libev事件库源码阅读笔记
- Libev事件库源码阅读笔记
- 事件库之Libev(一)
- 【SQL Server学习笔记】Windows主体、SQL Server主体、数据库主体
- libev学习笔记(持续更新)
- ora-19624 ora-19502 ora-27040
- 经典的线程池
- R语言笔记四
- 我的Java学习之路
- Grid View 网格视图——翻译自developer.android.com api guides
- libev 学习笔记之主体事件循环
- 2016-06-15-课程设计日志
- kali 私人笔记
- [Python]Merge Intervals
- WebView基本使用方法
- CentOS7 安装 MariaDB
- <Sicily> 生成字符串
- POJ-3368 Frequent values
- 字符串长度与strlen、sizeof