栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

通读 libevent 的设计思路与实现细节

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

通读 libevent 的设计思路与实现细节

libevent 流程图

libevent 沉思录 1 libevent的设计思路

设计思路做到统一
统一事件类型:信号事件,定时事件,IO时间
统一系统调用:epoll/ select/ poll/ win 等作为底层 去实现不同的eventop(事件多路分发器)的接口

struct eventop 
{
int (*add)(struct event_base *, evutil_socket_t fd, short old, short events, void *fdinfo);
int (*dispatch)(struct event_base *, struct timeval *);
}

组织形式是reactor模式
反转即cb。每个文件描述符上的可读/可写事件可以创建多个事件处理器(不同的回调函数),libevent的IO事件队列将相同文件描述符的事件处理器组织在一起(event_io_map),当事件就绪时,可以根据fd找到对应的IO事件队列(evmap_io),将队列中的每一个节点(event)按照优先级插入到不同的激活队列中

struct epollop {
	struct epoll_event *events; // reactor(event_base)监听的所有的事件(事件数>文件描述符数)
	int nevents;
	int epfd;
};

struct evmap_io
{
	struct event_list events; // 文件描述符上所有的事件
	ev_uint16_t nread;
	ev_uint16_t nwrite;
}

struct event_base
{
	const struct eventop *evsel; // 多路复用的底层系统实现
	struct event_io_map io; // evmap_io[fd]    idx:fd  --> evmap_io fd到events的映射
struct event_list eventqueue; // 注册事件队列
struct event_list *activequeues; // 激活队列
struct event_changelist changelist; //事件变化队列。 用途:如果一个文件描述符上注册的事件被多次修改,则可以使用缓冲来避免重复的系统调用(比如epoll_ctl).

}
2 注册队列和激活队列的作用

epoll相比于poll在于返回的就绪队列,不需要再次遍历,返回的就是可处理的。
同理,激活队列也是相同作用,激活队列中的都是直接可以执行cb的。

event_queue_insert(struct event_base *base, struct event *ev, int queue)
{
	switch (queue) {
	case EVLIST_INSERTED: // event_add()调用
		TAILQ_INSERT_TAIL(&base->eventqueue, ev, ev_next);
		break;
	case EVLIST_ACTIVE:   // dispatch()调用
		base->event_count_active++;
		TAILQ_INSERT_TAIL(&base->activequeues[ev->ev_pri],ev,ev_active_next);
		break;
	case EVLIST_TIMEOUT: 
{
		if (is_common_timeout(&ev->ev_timeout, base)) {
			struct common_timeout_list *ctl =
			    get_common_timeout_list(base, &ev->ev_timeout);
			insert_common_timeout_inorder(ctl, ev);
		} else
			min_heap_push(&base->timeheap, ev);
		break;
	}
	}
}
3 event_add 为什么没有直接调用epoll_ctl

你先变,稳定了我在加
event_changelist_add() —> event_changelist_get_or_construct() 只修改event_changelist,因为文件描述符可能被多次设置监听读写时间,所以在这里只记录变化的,在dispatch时再epoll_apply_changes()。

//自上次调用 eventtop.dispatch 以来的“更改”列表。
struct event_changelist {
	struct event_change *changes; 
	int n_changes; // 改变的个数
	int changes_size;
};
4 libevent非内部函数是什么 | 是如何实现非阻塞的

在event_loop中

while(!done)
{
if (N_ACTIVE_CALLBACKS(base)) {
			int n = event_process_active(base);
			if ((flags & EVLOOP_ONCE)   // 如果所有激活事件都已经处理 | 
			    && N_ACTIVE_CALLBACKS(base) == 0
			    && n != 0)
				done = 1;
		} else if (flags & EVLOOP_NONBLOCK) // 非阻塞所有激活时间处理完毕就返回了
			done = 1;
}
static int
event_process_active(struct event_base *base)
{
	
	struct event_list *activeq = NULL;
	int i, c = 0;

	for (i = 0; i < base->nactivequeues; ++i) {
		if (TAILQ_FIRST(&base->activequeues[i]) != NULL) {
			base->event_running_priority = i;
			activeq = &base->activequeues[i];
			c = event_process_active_single_queue(base, activeq);
			if (c < 0) {
				base->event_running_priority = -1;
				return -1;
			} else if (c > 0) // 
				break; 
			
			
		}
	}

	event_process_deferred_callbacks(&base->defer_queue,&base->event_break); //执行延迟回调函数,当所有的激活事件都处理完成后,需要做的事情
	base->event_running_priority = -1;
	return c;
}
libevent 中的尾队列TAILQ结构

c语言实现的双向链表 queue.h 用来存储事件 list的原型
尾队列头tqh 和 尾队列元素tqe

  • 通过TAILQ_HEAD(name, type)宏可以快速的定义某一个类型结构体的队列.
    队列处理方面不需要知道队列中数据是什么。类似queue 这种传入泛型。
  • 二级指针的用法
    有一个类型叫做指针的指针tqh_last,于是可以修改指针的指针存储的指针的地址,而不是修改指针存储的数据的地址。这样不用存储最后一个节点,而是最后一个节点的tqe_next指针
  • 插入/删除都是o(1)
  
#define TAILQ_HEAD(name, type)                        
struct name {                                 
    struct type *tqh_first;            
    struct type **tqh_last;        二级指针
}  
  
#define TAILQ_HEAD_INITIALIZER(head)                      
    { NULL, &(head).tqh_first }  
  
#define TAILQ_ENTRY(type)                         
struct {                                  
    struct type *tqe_next;              
    struct type **tqe_prev;     
}  
  
  
#define TAILQ_FIRST(head)       ((head)->tqh_first)  
#define TAILQ_END(head)         NULL  
#define TAILQ_NEXT(elm, field)      ((elm)->field.tqe_next)  
#define TAILQ_LAST(head, headname)                    
    (*(((struct headname *)((head)->tqh_last))->tqh_last))  
  
#define TAILQ_PREV(elm, headname, field)                  
    (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))  
#define TAILQ_EMPTY(head)                         
    (TAILQ_FIRST(head) == TAILQ_END(head))  
  
#define TAILQ_FOREACH(var, head, field)                   
    for((var) = TAILQ_FIRST(head);                    
        (var) != TAILQ_END(head);                     
        (var) = TAILQ_NEXT(var, field))  
  
#define TAILQ_FOREACH_REVERSe(var, head, headname, field)         
    for((var) = TAILQ_LAST(head, headname);               
        (var) != TAILQ_END(head);                     
        (var) = TAILQ_PREV(var, headname, field))  
  
  
#define TAILQ_INIT(head) do {                         
    (head)->tqh_first = NULL;                      
    (head)->tqh_last = &(head)->tqh_first;                  
} while (0)  
  
#define TAILQ_INSERT_HEAD(head, elm, field) do {              
    if (((elm)->field.tqe_next = (head)->tqh_first) != NULL)    
        (head)->tqh_first->field.tqe_prev =             
            &(elm)->field.tqe_next;                
    else                                  
        (head)->tqh_last = &(elm)->field.tqe_next;          
    (head)->tqh_first = (elm);                     
    (elm)->field.tqe_prev = &(head)->tqh_first;             
} while (0)  
  
#define TAILQ_INSERT_TAIL(head, elm, field) do {              
    (elm)->field.tqe_next = NULL;                      
    (elm)->field.tqe_prev = (head)->tqh_last;           
    *(head)->tqh_last = (elm);       elm是一个指针, tqh_last是一个指针的指针 这里相当于操作tqe_next了
    (head)->tqh_last = &(elm)->field.tqe_next;              记录的是最后元素的下一个元素的地址,便于尾插
} while (0)  
  
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do {        
    if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)  
        (elm)->field.tqe_next->field.tqe_prev =             
            &(elm)->field.tqe_next;                
    else                                  
        (head)->tqh_last = &(elm)->field.tqe_next;          
    (listelm)->field.tqe_next = (elm);                 
    (elm)->field.tqe_prev = &(listelm)->field.tqe_next;         
} while (0)  
  
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do {             
    (elm)->field.tqe_prev = (listelm)->field.tqe_prev;          
    (elm)->field.tqe_next = (listelm);                 
    *(listelm)->field.tqe_prev = (elm);                
    (listelm)->field.tqe_prev = &(elm)->field.tqe_next;         
} while (0)  
  
#define TAILQ_REMOVE(head, elm, field) do {               
    if (((elm)->field.tqe_next) != NULL)               
        (elm)->field.tqe_next->field.tqe_prev =             
            (elm)->field.tqe_prev;                 
    else                                  
        (head)->tqh_last = (elm)->field.tqe_prev;       
    *(elm)->field.tqe_prev = (elm)->field.tqe_next;             
} while (0)  
  
#define TAILQ_REPLACE(head, elm, elm2, field) do {            
    if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL)   
        (elm2)->field.tqe_next->field.tqe_prev =        
            &(elm2)->field.tqe_next;               
    else                                  
        (head)->tqh_last = &(elm2)->field.tqe_next;         
    (elm2)->field.tqe_prev = (elm)->field.tqe_prev;             
    *(elm2)->field.tqe_prev = (elm2);                  
} while (0)  
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/833022.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号