libevent源码阅读 -- select模型的实现

一、libevent的select模型原理概述

select异步IO模型使用轮询机制,因此简单易用易懂,但是性能一般。对于小并发的环境下,这种模型效果不错,大并发时,轮询机制会影响性能。
正是因此,windows和linux系统对于select模型同时监控的最大socket数量都做了限制。windows平台下,根据msdn 2013版本(以下所有的VC代码都是该版本)描述,最大支持的socket数量是64个,linux平台本人没有看到权威数据,据说1024个。想要突破这个限制,windows平台只需要在#include

typedef struct fd_set {
        u_int   fd_count;               /* how many are SET? */
        SOCKET  fd_array[FD_SETSIZE];   /* an array of SOCKETs */
} fd_set;

fd_set结构,只包含两个成员:
fd_count:当前fd_set结构中的socket数量。
fd_array:当前轮询监控的socket句柄数组,最大值FD_SETSIZE=64。

从这个结构可以推导:select函数本事并没有限制监控的socket数量。那么我们只要实现这个结构,并自己完成对这个结构的设置、清理、判断等操作,即可突破系统对select最大socket数量的限制。libevent的select模型就是用这个原理实现的。

下面对libevent中,负责实现windows平台下select模型的Win32select.c文件分析其实现原理。这里只分析libevent中,select模型相关的代码,对于其他的内容,比如事件注册,回调函数等,不做探讨。

二、关键结构定义 win_fd_set 和 win32op

libevent定义了一个结构体:

struct win_fd_set {
    unsigned int fd_count;
    SOCKET fd_array[1];
};

这个结构就相当于fd_set结构。同时,还定义了几个do_fd_xxx的几个函数,这几个函数就相当于FD_SET,FD_ISSET,FD_CLR,FD_ZERO等宏,负责对win_fd_set操作。

struct win32op {
    unsigned num_fds_in_fd_sets;
    int resize_out_sets;
    struct win_fd_set *readset_in;
    struct win_fd_set *writeset_in;
    struct win_fd_set *readset_out;
    struct win_fd_set *writeset_out;
    struct win_fd_set *exset_out;
    unsigned signals_are_broken : 1;
};

其中,readset_in指针存放需要执行读监控的socket列表,writeset_in指针存放需要写监控的socket列表,这两个只在执行select之前,存储设置的socket句柄,在真正执行select函数时,这两个并不会作为参数传入select。
在执行select函数时,libevent先将readset_in,writeset_in中的数据,使用memcpy,考入readset_out,writeset_out中,并将out这两个作为第二和第三个参数,传入select函数中。这样做的好处分析如下:
由于select函数每次返回以后,传入参数都可能发生变化,不能作为下次调用select函数的参数,因此完整的需要执行读监控和写监控的socket列表必须单独保存,而libevent的select模型并没有限制socket的个数,也就是说,libevent的select模型可能用在较大规模的并发场景中,如果每次调用select函数,都需要执行像FD_SET这样的操作,构造select的参数,那么效率必然比memcpy要慢得多。
exset_out 这个结构只存储写错误的socket,读错误不会再这里体现。
num_fds_in_fd_sets 这个是win_fd_set的最大可设置socket数,也就是win_fd_set中fd_array的内存大小
resize_out_sets 标识win_fd_set中fd_array的存储是不是重新分配过,如果为1,表示xxx_out也需要重新分配内存,否则不需要。另外,libevent这里分配内存的原则是:初始分配64个socket,后续每次都是上次的2倍。

三、相关函数

在懂的原理和关键数据结构以后,具体的实现就比较简单了。这里只简单说一下几个关键函数。
do_fd_set函数这个相当于FD_SET宏的功能,不过它还有自动增加socket存储区的功能,这个功能是grow_fd_sets函数实现的。
do_fd_clear函数相当于FD_CLR宏的功能,不过这里为了提高删除socket的性能,避免使用遍历方法,使用了一些特殊操作。

最近在开发im服务器 需要大并发链接 QT默认的是使用select模型的 这种轮询方式非常慢 在高并发连接 我们需要epoll才能发挥linux服务器的性能 而且使用简单 整个服务端代码架构无需修改 直接可以使用 只要在 main文件添加: int main int argc char argv[] { #ifdef Q OS LINUX QCoreApplication::setEventDispatcher new EventDispatcherLibEvent ; qInstallMessageHandler customMessageHandler ; #endif QCoreApplication a argc argv ; auto ser new ConfigServer; ser >startServer ; return a exec ; } 在 pro文件添加 linux{ LIBS + levent core SOURCES + common eventdispatcher libevent eventdispatcher libevent cpp common eventdispatcher libevent eventdispatcher libevent config cpp common eventdispatcher libevent eventdispatcher libevent p cpp common eventdispatcher libevent socknot p cpp common eventdispatcher libevent tco eventfd cpp common eventdispatcher libevent tco pipe cpp common eventdispatcher libevent tco cpp common eventdispatcher libevent timers p cpp HEADERS + common eventdispatcher libevent common h common eventdispatcher libevent eventdispatcher libevent h common eventdispatcher libevent eventdispatcher libevent config h common eventdispatcher libevent eventdispatcher libevent config p h common eventdispatcher libevent eventdispatcher libevent p h common eventdispatcher libevent libevent2 emul h common eventdispatcher libevent qt4compat h common eventdispatcher libevent tco h common eventdispatcher libevent wsainit h } 可以直接跨平台了使用了 csdn博客:http: blog csdn net rushroom">最近在开发im服务器 需要大并发链接 QT默认的是使用select模型的 这种轮询方式非常慢 在高并发连接 我们需要epoll才能发挥linux服务器的性能 而且使用简单 整个服务端代码架构无需修改 直接可以使用 只要在 main文件添加: [更多]
libevent是一个事件触发的网络库,适用于windows、linux、bsd等多种平台,内部使用select、epoll、kqueue等系统调用管理事件机制。著名分布式缓存软件memcached也是libevent based,而且libevent在使用上可以做到跨平台,而且根据libevent官方网站上公布的数据统计,似乎也有着非凡的性能。 编辑本段 详细   编译库代码,编译脚本会判断OS支持哪种类型的事件机制(select、epoll或kqueue),然后条件编译相应代码,供上层使用的接口仍然是保持统一的(否则也不能所谓的跨平台了)。在linux redhat as 4 u 2 上编译相当容易,configure以后make,make install就可以了,windows上编译似乎有点小麻烦,不过稍微改点东西也就通过了。   从代码中看,libevent支持用户使用三种类型的事件,分别是网络IO、定时器、信号三种,在定时器的实现上使用了RB tree的数据结构,以达到高效查找、排序、删除定时器的目的,网络IO上,主要关注了一下linux上的epoll(因为目前的开发主要在linux平台),结果发现libevent的epoll居然用的EPOLLLT,水平触发的方式用起来比较方便,不容易出错,但是在效率上可能比EPOLLET要低一些。   跟网络无关的,libevent也有一些缓冲区管理的函数,而且是c风格的函数,实用性不是太大。libevent没有提供缓存的函数。   虽然libevent实用上的价值不大,但它提供的接口形式还是不错的,实现类似的lib的时候仍然是可以参考的。   Libevent定时器的数据结构自version 1.4起已由红黑树改为最小堆(Min Heap),以提高效率;网络IO和信号的数据结构采用了双向链表(TAILQ)。在实现上主要有3种链表: EVLIST_INSERTED, EVLIST_ACTIVE, EVLIST_TIMEOUT,一个ev在这3种链表之间被插入或删除,处于EVLIST_ACTIVE链表中的ev最后将会被调度执行。   Libevent提供了DNS,HTTP Server,RPC等组件,HTTP Server可以说是Libevent的经典应用。从http.c可看到Libevent的很多标准写法。写非阻塞式的HTTP Server很容易将socket处理与HTTP协议处理纠缠在一起,Libevent在这点上似乎也有值得推敲的地方。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值