您的位置:首页 > 其它

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

2016-12-11 17:28 597 查看

一、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的性能,避免使用遍历方法,使用了一些特殊操作。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  libevent socket select