前面介绍了libevent中的hash表,在添加事件时,具体是如何操作的呢?事件操作主要是在evmap.c文件中,包含了io事件,signal事件的操作。在事件操作时,分两种情况,一种是利用hash表,另外一种是不用hash表。

1、hash表

hash表结构主要是针对io事件时,通过两个宏定义了hash表,及hash表相关的操作

HT_PROTOTYPE(event_io_map, event_map_entry, map_node, hashsocket, eqsocket)
HT_GENERATE(event_io_map, event_map_entry, map_node, hashsocket, eqsocket,0.5, mm_malloc, mm_realloc, mm_free)

其中event_io_map是hash表的结构体名,event_map_entry是hash表中的一个元素的表示,而map_node是表中元素的一个成员。

event_map_entry定义如下:

struct event_map_entry {HT_ENTRY(event_map_entry) map_node;evutil_socket_t fd;union { /* This is a union in case we need to make more things that canbe in the hashtable. */struct evmap_io evmap_io;} ent;
};

而evmap_io定义为

struct evmap_io {struct event_list events;   //event_list是以TAILQ_HEAD(event_list, event)宏来定义的ev_uint16_t nread;ev_uint16_t nwrite;
};

其所使用的hash函数为用io的fd来计算hash

static inline unsigned
hashsocket(struct event_map_entry *e)
{/* On win32, in practice, the low 2-3 bits of a SOCKET seem not to* matter.  Our hashtable implementation really likes low-order bits,* though, so let's do the rotate-and-add trick. */unsigned h = (unsigned) e->fd;h += (h >> 2) | (h << 30);return h;
}

在io的fd知道的情况下,是如何来找到对应的事件的呢?如果没有找到,又是做什么操作呢?实际上,针对这两种情况,libevent中两个相对应的宏,一个是直接查找,另一个是也是查找,但是在没有找到的情况下,就将其插入hash表中,分别为

#define GET_IO_SLOT(x, map, slot, type)                  \do {                               \struct event_map_entry _key, *_ent;            \_key.fd = slot;                       \_ent = HT_FIND(event_io_map, map, &_key);     \(x) = _ent ? &_ent->ent.type : NULL;           \} while (0);
#define GET_IO_SLOT_AND_CTOR(x, map, slot, type, ctor, fdinfo_len)  \do {                               \struct event_map_entry _key, *_ent;            \_key.fd = slot;                       \_HT_FIND_OR_INSERT(event_io_map, map_node, hashsocket, map, \event_map_entry, &_key, ptr,          \{                          \_ent = *ptr;              \},                         \{                          \_ent = mm_calloc(1,sizeof(struct event_map_entry)+fdinfo_len); \if (EVUTIL_UNLIKELY(_ent == NULL))     \return (-1);           \_ent->fd = slot;               \(ctor)(&_ent->ent.type);            \_HT_FOI_INSERT(map_node, map, &_key, _ent, ptr) \});                   \(x) = &_ent->ent.type;                 \} while (0)

2、针对信号的宏

对于信号事件,没有使用hash表,使用的是动态数组,与信号相关的结构为

struct evmap_signal {struct event_list events;
};

针对信号查找与io查找一样,分为两种情况

#define GET_SIGNAL_SLOT(x, map, slot, type)          \ //不添加的查找(x) = (struct type *)((map)->entries[slot])
#define GET_SIGNAL_SLOT_AND_CTOR(x, map, slot, type, ctor, fdinfo_len)  \  //添加查找do {                               \if ((map)->entries[slot] == NULL) {           \(map)->entries[slot] =             \mm_calloc(1,sizeof(struct type)+fdinfo_len); \if (EVUTIL_UNLIKELY((map)->entries[slot] == NULL)) \return (-1);               \(ctor)((struct type *)(map)->entries[slot]);    \}                          \(x) = (struct type *)((map)->entries[slot]);       \} while (0)

3、I/O添加、删除、激活操作

I/O添加操作是通过evmap_io_add来完成的,删除操作evmap_io_del完成,激活操作evmap_io_active完成。添加操作只有在第一次添加读或者第一次添加写时,才会调用evsel->add,而删除操作是在evmap_io中的nread或者nwrite中一个为0时,才会调用evsel->del。其具体函数为

int
evmap_io_add(struct event_base *base, evutil_socket_t fd, struct event *ev)
{const struct eventop *evsel = base->evsel;  //io的操作集struct event_io_map *io = &base->io;struct evmap_io *ctx = NULL;int nread, nwrite, retval = 0;short res = 0, old = 0;struct event *old_ev;EVUTIL_ASSERT(fd == ev->ev_fd);if (fd < 0)return 0;#ifndef EVMAP_USE_HTif (fd >= io->nentries) {  //如果fd大于当前动态数组的大小,就重新调整数据大小if (evmap_make_space(io, fd, sizeof(struct evmap_io *)) == -1)return (-1);}
#endifGET_IO_SLOT_AND_CTOR(ctx, io, fd, evmap_io, evmap_io_init,evsel->fdinfo_len);nread = ctx->nread;nwrite = ctx->nwrite;if (nread)old |= EV_READ;if (nwrite)old |= EV_WRITE;if (ev->ev_events & EV_READ) {if (++nread == 1)res |= EV_READ;}if (ev->ev_events & EV_WRITE) {if (++nwrite == 1)res |= EV_WRITE;}if (EVUTIL_UNLIKELY(nread > 0xffff || nwrite > 0xffff)) {event_warnx("Too many events reading or writing on fd %d",(int)fd);return -1;}if (EVENT_DEBUG_MODE_IS_ON() &&(old_ev = TAILQ_FIRST(&ctx->events)) &&(old_ev->ev_events&EV_ET) != (ev->ev_events&EV_ET)) {event_warnx("Tried to mix edge-triggered and non-edge-triggered"" events on fd %d", (int)fd);return -1;}if (res) {  //如果nwrite,nread中有一个为1void *extra = ((char*)ctx) + sizeof(struct evmap_io);/* XXX(niels): we cannot mix edge-triggered and* level-triggered, we should probably assert on* this. */if (evsel->add(base, ev->ev_fd,old, (ev->ev_events & EV_ET) | res, extra) == -1)return (-1);retval = 1;}ctx->nread = (ev_uint16_t) nread;ctx->nwrite = (ev_uint16_t) nwrite;TAILQ_INSERT_TAIL(&ctx->events, ev, ev_io_next);return (retval);
}int
evmap_io_del(struct event_base *base, evutil_socket_t fd, struct event *ev)
{const struct eventop *evsel = base->evsel;struct event_io_map *io = &base->io;struct evmap_io *ctx;int nread, nwrite, retval = 0;short res = 0, old = 0;if (fd < 0)return 0;EVUTIL_ASSERT(fd == ev->ev_fd);#ifndef EVMAP_USE_HTif (fd >= io->nentries)return (-1);
#endifGET_IO_SLOT(ctx, io, fd, evmap_io);nread = ctx->nread;nwrite = ctx->nwrite;if (nread)old |= EV_READ;if (nwrite)old |= EV_WRITE;if (ev->ev_events & EV_READ) {if (--nread == 0)res |= EV_READ;EVUTIL_ASSERT(nread >= 0);}if (ev->ev_events & EV_WRITE) {if (--nwrite == 0)res |= EV_WRITE;EVUTIL_ASSERT(nwrite >= 0);}if (res) {//如果nread,nwrite有一个为0void *extra = ((char*)ctx) + sizeof(struct evmap_io);if (evsel->del(base, ev->ev_fd, old, res, extra) == -1)return (-1);retval = 1;}ctx->nread = nread;ctx->nwrite = nwrite;TAILQ_REMOVE(&ctx->events, ev, ev_io_next);return (retval);
}void
evmap_io_active(struct event_base *base, evutil_socket_t fd, short events)
{struct event_io_map *io = &base->io;struct evmap_io *ctx;struct event *ev;#ifndef EVMAP_USE_HTEVUTIL_ASSERT(fd < io->nentries);
#endifGET_IO_SLOT(ctx, io, fd, evmap_io);EVUTIL_ASSERT(ctx);TAILQ_FOREACH(ev, &ctx->events, ev_io_next) {if (ev->ev_events & events)event_active_nolock(ev, ev->ev_events & events, 1);   //将io事件添加到激活队列中}
}

4、signal的添加、删除、激活操作

Signal添加时只有当信号对应的事件队列为空时,才会调用evsel->add,删除时只有当signal对应的事件队列中只有一个事件时,才会调用evsel->del,其具体实现为

int
evmap_signal_add(struct event_base *base, int sig, struct event *ev)
{const struct eventop *evsel = base->evsigsel;struct event_signal_map *map = &base->sigmap;struct evmap_signal *ctx = NULL;if (sig >= map->nentries) {if (evmap_make_space(map, sig, sizeof(struct evmap_signal *)) == -1)return (-1);}GET_SIGNAL_SLOT_AND_CTOR(ctx, map, sig, evmap_signal, evmap_signal_init,base->evsigsel->fdinfo_len);if (TAILQ_EMPTY(&ctx->events)) {  //事件队列空if (evsel->add(base, ev->ev_fd, 0, EV_SIGNAL, NULL)== -1)return (-1);}TAILQ_INSERT_TAIL(&ctx->events, ev, ev_signal_next);  //将事件添加到队列中return (1);
}int
evmap_signal_del(struct event_base *base, int sig, struct event *ev)
{const struct eventop *evsel = base->evsigsel;struct event_signal_map *map = &base->sigmap;struct evmap_signal *ctx;if (sig >= map->nentries)return (-1);GET_SIGNAL_SLOT(ctx, map, sig, evmap_signal);if (TAILQ_FIRST(&ctx->events) == TAILQ_LAST(&ctx->events, event_list)) {   //队列中只有一个事件if (evsel->del(base, ev->ev_fd, 0, EV_SIGNAL, NULL) == -1)return (-1);}TAILQ_REMOVE(&ctx->events, ev, ev_signal_next);  //将事件从队列中删除return (1);
}void
evmap_signal_active(struct event_base *base, evutil_socket_t sig, int ncalls)
{struct event_signal_map *map = &base->sigmap;struct evmap_signal *ctx;struct event *ev;EVUTIL_ASSERT(sig < map->nentries);GET_SIGNAL_SLOT(ctx, map, sig, evmap_signal);TAILQ_FOREACH(ev, &ctx->events, ev_signal_next)event_active_nolock(ev, EV_SIGNAL, ncalls);  //将事件加入到激活队列中
}

libevent中事件的添加与删除相关推荐

  1. mysql 语句怎样修饰约束_MySQL中的约束,添加约束,删除约束,以及一些其他修饰讲解...

    (1)创建数据库 CREATE DATABASES 数据库名; (2)选择数据库 USE 数据库名; (3)删除数据库 DROP DATAVBASE 数据库名; (4)创建表 CREATE TABLE ...

  2. mysql中如何删除表中int约束,MySQL中的约束,添加约束,删除约束,以及其他修饰

    一.NOT NULL(非空约束) 添加非空约束 1)建表时直接添加 CREATE TABLE t_user(user_id INT(10) NOT NULL); 2)通过ALTER 语句 ALTER ...

  3. mysql怎么添加默认约束_分享知识-快乐自己:MySQL中的约束,添加约束,删除约束,以及一些其他修饰...

    创建数据库: CREATE DATABASES 数据库名: 选择数据库: USE 数据库名: 删除数据库: DROP DATAVBASE 数据库名: 创建表: CREATE TABLE IF NOT ...

  4. 计算机控制面板中无法删除程序,电脑在控制面板中无法打开添加或删除程序

    如下所示我们在xp系统中进入到电脑中的"控制面板"你会发现找不到"添加或删除程序"域者找到了也无法打开"添加或删除程序"选项后报错. 打开& ...

  5. AutoCAD Electrical 2022—项目中新建、添加、删除图纸

    右键点击项目-选择新建图纸: 点击快捷图标,新建图形: 弹出对话框,在名称中输入图纸名称: 模板为图框的样式,位置代号,图纸保存的位置: 其他根据需要填写: 填写完点击确定,弹出下面的对话框: 点击确 ...

  6. JS中创建、添加、删除节点

    createElement():创建节点 appendChild():添加节点(还有一个很相似的 inertBefore():插入节点,两个参) removeChild():删除节点 <!DOC ...

  7. java删除mysql 数据库语句怎么写_怎么用JAVA语句在Mysql中查询,添加,删除语句,说的详细点,谢谢!...

    展开全部 创建一个java project:对着project右键62616964757a686964616fe59b9ee7ad9431333264633564->属性 然后就 如图所示:导入 ...

  8. 给GridView中的buttonField添加一个删除确认功能

    问题: GridView的第一列是ButtonField,字段名是"删除",想一点之后弹出确认框,否则返回.应该如何写? 解决方法: 1.点击GridView的快捷箭头,选'编辑列 ...

  9. 【Unity3D】资源文件 ① ( Unity 中常用的文件类型 | Unity 文件操作 | 文件系统中查看文件 | 添加文件 | 删除文件 | 导入文件 | 复制文件 | 缩略图显示 )

    文章目录 一.Unity 中常用的文件类型 二.Unity 文件操作 1.文件系统中查看文件 2.添加目录 / 文件 3.删除目录 / 文件 4.导入资源 5.复制资源 6.缩略图显示 7.meta ...

最新文章

  1. 【Android】ADT中使用NDK编译已有的C++实现的库文件
  2. android横竖屏切换生命周期
  3. Kali Linux软件更新日报20190623
  4. 初识vue+elementUi
  5. 前端学习(2008)vue之电商管理系统电商系统之获取静态属性列表
  6. html 复选框name值,HTML(5)表单元素以及对各个表单元素的name、value属性的理解
  7. win c语言创建线程,初学者 CWinThread 线程类
  8. 团队开发——个人工作总结01
  9. 程序—java记事本
  10. Unity2018.1中文更新日志速览版
  11. 36幅非常漂亮的阳光摄影作品欣赏
  12. csr 蓝牙驱动_【BTS001】开源蓝牙协议栈BTStack初体验
  13. 通过web的方式动态查看tomcat的catalina.out的日志(web.py)
  14. Go Web框架 Gin路由(一)
  15. 十大排序算法Java版
  16. 蒙版操作—快速蒙版扣图
  17. 离散数学:用python实现矩阵乘法与关系矩阵
  18. 大数据量分页存储过程效率测试附代码(转http://www.cnblogs.com/lli0077/archive/2008/09/03/1282862.html)...
  19. A+B 输入输出练习VIII
  20. POJ 3669 Meteor Shower 流星雨 解题思路心得 BFS广搜 C/C++AC代码(另有TLE不知其因)

热门文章

  1. Extjs显示时间兼容性问题——firefox正常显示,IE不正常出现NaN-NaN-NaN的解决方式...
  2. 模拟jQuery构造对象
  3. python第三方库排行-Python模块汇总(常用第三方库)
  4. python学到什么程度可以做兼职-Python学到什么程度可以面试工作(解答一)
  5. python安装步骤图解-Python安装-小白图文教程(精)
  6. python3.7入门教程-python 3.7极速入门教程6文件处理
  7. python 用途-python主要用途
  8. python web-2019年Python Web五大主流框架
  9. python爬虫招聘-Python爬虫-爬取招聘网站信息(一)
  10. python手机版iphone-Python编程狮下载