libevent学习之跨平台Reactor接口的实现
来源:互联网 发布:python怎么取最大 编辑:程序博客网 时间:2024/05/01 09:16
原文链接:http://blog.csdn.NET/luotuo44/article/details/38458469
上文学习了Libevent中的TAILQ_QUEUE,Libevent最重要的跨平台功能还是实现了多路IO接口的跨平台(即Reactor模式)。这使得用户可以在不同的平台使用统一的接口。这篇博文就是来讲解Libevent是怎么实现这一点的。对于自己以后编写跨平台的网络程序很有好处.
event_base结构体结构体->struct eventop *evsel
struct eventop { const char *name; //多路IO复用函数的名字 void *(*init)(struct event_base *); int (*add)(struct event_base *, evutil_socket_t fd, short old, short events, void *fdinfo); int (*del)(struct event_base *, evutil_socket_t fd, short old, short events, void *fdinfo); int (*dispatch)(struct event_base *, struct timeval *); void (*dealloc)(struct event_base *); int need_reinit; //是否要重新初始化 //多路IO复用的特征。参考http://blog.csdn.net/luotuo44/article/details/38443569 enum event_method_feature features; size_t fdinfo_len; //额外信息的长度。有些多路IO复用函数需要额外的信息 };
struct eventop结构体的成员就是一些函数指针。名称也像一个多路IO复用函数应该有的操作:add可以添加fd,del可以删除一个fd,dispatch可以进入监听。明显只要给event_base的evsel成员赋值就能使用对应的多路IO复用函数了。
Libevent源代码中为每种多路IO复用函数创建了一个文件,比如select.c、poll.c、epoll.c、kqueue.c等。
打开这些文件就可以发现在文件的前面都会声明一些多路IO复用的操作函数,而且还会定义一个struct eventop类型的全局变量。
//select.c文件 static void *select_init(struct event_base *); static int select_add(struct event_base *, int, short old, short events, void*); static int select_del(struct event_base *, int, short old, short events, void*); static int select_dispatch(struct event_base *, struct timeval *); static void select_dealloc(struct event_base *); const struct eventop selectops = { "select", select_init, select_add, select_del, select_dispatch, select_dealloc, 0, /* doesn't need reinit. */ EV_FEATURE_FDS, 0, };
如何选定后端:
看到这里,读者想必已经知道,只需要将对应平台的多路IO复用函数的全局变量赋值给event_base的evsel变量即可.可是系统支持多种IO复用,libevent是如何根据系统环境选择一个IO复用类型的呢?
/* Array of backends in order of preference. */static const struct eventop *eventops[] = {#ifdef _EVENT_HAVE_EVENT_PORTS &evportops,#endif#ifdef _EVENT_HAVE_WORKING_KQUEUE &kqops,#endif#ifdef _EVENT_HAVE_EPOLL &epollops,#endif#ifdef _EVENT_HAVE_DEVPOLL &devpollops,#endif#ifdef _EVENT_HAVE_POLL &pollops,#endif#ifdef _EVENT_HAVE_SELECT &selectops,#endif#ifdef WIN32 &win32ops,#endif NULL};
它根据宏定义判断当前的OS环境是否有某个多路IO复用函数。如果有,那么就把与之对应的struct eventop结构体指针放到一个全局数组中。有了这个数组,现在只需将数组的某个元素赋值给evsel变量即可。
从数组的元素可以看到,低下标存的是高效多路IO复用函数。如果从低到高下标选取一个多路IO复用函数,那么将优先选择高效的。
选择一个可用的IO复用函数类型是在event_base_new_with_config(const struct event_config *cfg)函数中
for (i = 0; eventops[i] && !base->evbase; i++) { if (cfg != NULL) { /* determine if this backend should be avoided */ if (event_config_is_avoided_method(cfg, eventops[i]->name)) continue; if ((eventops[i]->features & cfg->require_features) != cfg->require_features) continue; } /* also obey the environment variables */ if (should_check_environment && event_is_method_disabled(eventops[i]->name)) continue; //选择其中一个系统可用的IO复用模型 base->evsel = eventops[i]; //初始化evbase,后面会说到,init主要是做什么事情呢? base->evbase = base->evsel->init(base); }
后端数据存储结构体
在本文最前面列出的event_base结构体中,除了evsel变量外,还有一个evbase变量。这也是一个很重要的变量,而且也是用于跨平台的。
像select、poll、epoll之类多路IO复用函数在调用时要传入一些数据,比如监听的文件描述符fd,监听的事件有哪些。在Libevent中,这些数据都不是保存在event_base这个结构体中的,而是存放在evbase这个指针指向的一个结构体中。
IO复用结构体:
由于不同的多路IO复用函数需要使用不同格式的数据,所以Libevent为每一个多路IO复用函数都定义了专门的结构体(即结构体是不同的),本文姑且称之为IO复用结构体。evbase指向的就是这些结构体。由于这些结构体是不同的,所以要用一个void类型指针。
在select.c、poll.c这类文件中都定义了属于自己的IO复用结构体,如下面代码所示:
//select.c文件 struct selectop { int event_fds; /* Highest fd in fd set */ int event_fdsz; int resize_out_sets; fd_set *event_readset_in; fd_set *event_writeset_in; fd_set *event_readset_out; fd_set *event_writeset_out; }; //poll.c文件 struct pollop { int event_count; /* Highest number alloc */ int nfds; /* Highest number used */ int realloc_copy; /* True iff we must realloc * event_set_copy */ struct pollfd *event_set; struct pollfd *event_set_copy; };
前面event_base_new_with_config的代码中,有下面一行代码:
base->evbase = base->evsel->init(base);
明显这行代码就是用来赋值evbase的。下面是poll对应的init函数:
//poll.c文件 static void * poll_init(struct event_base *base) { struct pollop *pollop; if (!(pollop = mm_calloc(1, sizeof(struct pollop)))) return (NULL); evsig_init(base);//其他的一些初始化 return (pollop); }
经过上面的一些处理后,Libevent在特定的OS下能使用到特定的多路IO复用函数。
由于有evsel和evbase这个两个指针变量,当初始化完成之后,再也不用担心具体使用的多路IO复用函数是哪个了。evsel结构体的函数指针提供了统一的接口,上层的代码要使用到多路IO复用函数的一些操作函数时,直接调用evsel结构体提供的函数指针即可。也正是如此,Libevent实现了统一的跨平台Reactor接口。
- libevent学习之跨平台Reactor接口的实现
- Libevent源码分析-----跨平台Reactor接口的实现
- Libevent源码分析-----跨平台Reactor接口的实现
- Libevent源码分析-----跨平台Reactor接口的实现
- Libevent学习-----Reactor的事件处理机制
- libevent之reactor
- libevent源码学习-----Reactor模型
- libevent之Reactor模式详解
- multi-reactor服务器模型的C++封装类(libevent+多线程实现)
- multi-reactor服务器模型的C++封装类(libevent+多线程实现)
- 基于Reactor模式的libevent网络库之浅析与使用
- 从 bufferevent 实现学习 Libevent 的使用
- 从 bufferevent 实现学习 Libevent 的使用
- 从 bufferevent 实现学习 Libevent 的使用
- libevent学习之helloworld
- libevent学习之bufferevent
- Libevent学习之event_signal_map()
- 【muduo库学习】实现最简单的reactor模式
- ROS里面的几个python写的工具
- 你中招了吗?加密勒索软件攻击趋势分析
- BZOJ1133: [POI2009]Kon
- strcpy_s,sprintf_s,wcscpy_s,swprintf_s,wcscat_s,加了_s就真的安全吗?
- 计算机--机器学习---机器learning技法sum
- libevent学习之跨平台Reactor接口的实现
- centos安装php模块之后还是提示not found解决方法
- 迈出从3K到1W的重要一步——掌握设计模式
- Java Web 学习笔记(四) 基于 SpringMVC+BootStrap 创建WebApp
- 【技术创业】Excuse me,这家伙的梦想竟然是拯救中国足球
- Linux环境下svn回滚单个文件的shell函数
- Android Studio修改项目目录结构
- “不要相信一个程序员在加班时间写出的代码”这是真的吗?
- python学习记录--日期和时间