• libevent 源码学习六 —— 事件处理框架 event_base


    前言 :Reactor 模型的框架组件 event_base 结构体, 位于 event-internal.h 文件中 1. 结构体定义与解释 struct event_base { const struct eventop *evsel; void *evbase; int event_count; /* counts number of total events */ int event_count_active; /* counts number of active events */ int event_gotterm; /* Set to terminate loop */ int event_break; /* Set to terminate loop immediately */ /* active event management */ struct event_list **activequeues; int nactivequeues; /* signal handling info */ struct evsignal_info sig; struct event_list eventqueue; struct timeval event_tv; struct min_heap timeheap; struct timeval tv_cache; }; evsel 和 evbase :evsel 通过 ev_base 来实际执行操作。ev_sel 指向全局变量 eventops[] 中的一个。 eventop 封装系统提供的 I/O 多路复用机制。 eventop 结构体的成员是一系列的函数指针,在event-internal.h 中 struct eventop { const char *name; void *(*init)(struct event_base *); // 初始化 int (*add)(void *, struct event *); // 注册事件 int (*del)(void *, struct event *); // 删除事件 int (*dispatch)(struct event_base *, void *, struct timeval *); //事件分发 void (*dealloc)(struct event_base *, void *); // 注销,释放资源 /* set if we need to reinitialize the event base */ int need_reinit; }; 在 libevent 中 ,每种 I/O demultiplex 机制的实现都要提供这5个函数接口,完成自身的初始化、销毁释放、对事件的注册、注销、和分发。 activequeues 是一个二级指针, libevent 支持事件的优先级,activequeues[priority] 是一个链表,每个节点指向一个优先级为 priorrity 的就绪事件 event。 eventqueue 链表。保存了所有的注册事件 event 的指针 sig 管理信号的结构体 timeheap 管理定时事件的小根堆 event_tv 和 tv_cache 是 libevent 用于时间管理的变量 2 创建和初始化 event_base 程序通过调用 event_init 函数来创建和初始化。 3 接口函数 libevent中对应的接口函数主要就是 int event_add(struct event *ev, const struct timeval *timeout); int event_del(struct event *ev); int event_base_loop(struct event_base *base, int loops); void event_active(struct event *event, int res, short events); void event_process_active(struct event_base *base); a 注册事件 int event_add (struct event *ev, const struct timeval *tv) ev : 要注册的事件 tv : 超时时间 函数将 ev 注册到 ev->ev_base 上, 事件类型由 ev->ev_base 指明,如果注册成功,ev 将被插入到已注册链表中;如果 rv 不为 NULL,则会同时注册定时事件,将 ev 添加到 timer堆上。如果有一步操作失败,函数保证没有事件会被注册。 int event_add(struct event *ev, const struct timeval *tv){ struct event_base *base = ev->ev_base; // 要注册到的 event_base const struct eventop *evsel = base -> evsel; void * evbase = base -> evbase; // 系统采用的 I/O 策略 /* ** 新的 timer 事件,调用 timer heap 接口在堆上预留一个位置 ** 保证操作的原子性 ** 向系统 I/O 注册可能会失败,而当在堆上预留成功后,定时事件的添加肯定不会失败 ** 预留位置的可能结果是堆扩充,内部元素并不会改变 */ if(tv != NULL && !(ev -> ev_flags & EVLIST_TIMEOUT)) { if(min_heap_reserve(&base -> timeheap, 1 + min_heap_size(&base -> timeheap)) == -1) return -1 ; } // 如果事件 ev 不在已注册或激活链表中,测调用 evbase 注册事件。 if((ev -> ev_events & (EV_READ|EV_WRITE|EVSIGNAL)) && !(ev -> ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE))) { res = evsel -> add(evbase, ev); if(res != -1) // 注册成功, 插入 event 到已注册链表中 event_queue_insert(base, ev, EVLIST_INSERTED); } // 准备添加定时事件 if(res != -1 && ev != NULL) { struct timeval now; // EVLIST_TIMEOUT 表明 event 已经在定时器堆中了,删除旧的 if(ev -> ev_flags & EVLSIT_TIMEOUT) event_queue_remove(base, ev, EVLIST_TIMEOUT); // 如果事件已经是就绪状态,则从激活链表中删除 if((ev -> ev_flags & EVLIST_ACTIVE) && (ev -> ev_res & EV_TIMEOUT)) { if(ev -> ev_ncalls && ev -> ev_pnacalls) *ev -> ev_pncalls = 0; event_queue_remove(base, ev, EVLIST_ACTIVE); } // 计算时间,并插入到 timer 小根堆中 gettime(base, &now); evutil_timeradd(&now, tv, &ev -> ev_timeout); event_queue_insert(base, ev, EVLIST_TIMEOUT); } return res; } event_queue_insert() 负责将事件插入到对应的链表中。 void event_queue_insert(struct event_base *base, struct event *ev, int queue){ if(ev -> ev_flags & queue) if(queue & EVLIST_ACTIVE) return; ev -> ev_flags |= queue; swith(queue) { case EVLIST_INSERT: TAILQ_INSERT_TAIL(&base -> eventqueue, ev, ev_next); break; case EVLIST_ACTIVE: base -> event_count_active++; TAILQ_INSERT_TAIL(base -> activequeues[ev -> ev_pri], ev, ev_active_next); break; case EVLIST_TIMEOUT: min_heap_push(&base -> timeheap, ev); break; } } b 删除事件 int event_del (struct event *ev); // 删除事件的操作不一定是原子的 int event_del(struct event *ev){ struct event_base *base; const struct eventop *evsel; void *evbase; // 如果 ev_base 为 NULL, 表明 ev 没有被注册 if(ev -> ev_base == NULL) return -1; // 取得 ev 注册的 event_base 和 eventop 指针 base = ev -> ev_base; evsel = base -> evsel; evbase = base -> evbase; // 将 ev_callback 调用次数设置为 0 if(ev -> ev_nacalls && ev -> ev_pncalls) * ev -> ev_pncalls = 0; // 从对应的链表中删除 if(ev -> ev_flags & EVLIST_TIMEOUT) event_queue_remove(base, ev, EVLIST_TIMEOUT); if(ev -> ev_flags & EVLIST_ACTIVE) event_queue_remove(base, ev, EVLIST_ACTIVE); if(ev -> ev_flags & EVLIST_INSERT) { event_queue_remove(base, ev, EVLIST_INSERT); return (evsel -> del(evbase, ev)); } return 0; }
  • 相关阅读:
    4、10种数组去重的方法
    3.实现一个函数clone 可以对Javascript中的五种主要数据类型(Number、string、Object、Array、Boolean)进行复制
    2.怎么添加、移除、复制、创建、和查找节点
    1.简述同步和异步的区别
    89. 一行或多行文本超出隐藏
    88.阐述一下CSS Sprites(雪碧图)
    87、CSS属性overflow属性定义溢出元素内容区的内容会如何处理
    86.style标签写在body后与body前有什么区别?
    正则表达式:获取运算符之间变量
    反射问题(欢迎交流)
  • 原文地址:https://www.cnblogs.com/sanerer/p/10765466.html
Copyright © 2020-2023  润新知