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的性能,避免使用遍历方法,使用了一些特殊操作。
相关文章推荐
- memcached源码阅读----使用libevent和多线程模型
- 基于WSAEventSelect模型的TCP服务器实现
- WSAAsyncSelect 模型 - 还是接着以前的socket、 部分继续研究, 近期将精力 放在各种模型上面,比较缺点,实现出来
- Select I/O模型来实现一个并发处理多个客户端的TCP服务器
- Select I/O模型来实现一个并发处理多个客户端的TCP服务器 <转>
- CodeIgniter源码阅读(6)Hook.php 扩展框架的实现原理
- Select模型的简单实现
- [Chrome源码阅读] 理解ObserverList类的实现技巧
- 分布式文件系统KFS源码阅读与分析(三):RPC实现机制(MetaServer端)
- 使用Select I/O模型来实现一个并发处理多个客户端的TCP服务器
- select模型的实现
- Select 模型的使用 简单聊天室的实现
- android 源码阅读之-----ADN实现
- 通过select模型实现具有超时设定的connect
- 分布式文件系统KFS源码阅读与分析(四):RPC实现机制(KfsClient端)
- select模型的服务器源码
- windows下的网络编程——Select模型实例,一款ECHO服务的实现代码
- windows下的网络编程——Select模型实例,一款ECHO服务的实现代码
- 分布式文件系统KFS源码阅读与分析(四):RPC实现机制(KfsClient端)
- select模型linux实现代码