epoll移植到windows的可行性研究

1、各有千秋

linux作为高效稳定的操作系统,部署在大量的服务器上。epoll在linux下,一个高性能的网络IO模型,在服务端领域发挥着重要的作用。但在开发效率上,windows以及visual studio系列因其良好的用户体验而更受用户青睐。如果能够将linux的运行期优势和windows开发期的优势,有机地结合在一起,是一个不错的主义。

2、模型差异

和epoll相对应的是网络IO模型是windows下的IOCP模型,但两者之间存在一个很大的差别在于,epoll是在当资源可用时,系统触发事件,应用层在执行操作;而iocp是由应用层预先分配操作资源,在操作完成后,系统再触发事件。在操作和事件触发的时间顺序上正好相反。

除此之外,在接口设计上也诸多不同。虽然windows也提供了诸如socket、accept、send、recv之类的函数。可是,因为epoll和iocp设计上的不同,不但不能直接使用,反而变成了epoll移植到windows上的障碍。

3、accept的移植

异步accept在windows下对应的是AcceptEx函数,这个函数会预先分配一个socket,当真实连接请求进入时,再使用setsockopt和GetAcceptExSockaddr获取对应的信息。于是,我们可以将第一步AcceptEx作为一个事件注册的动作,隐藏在系统内部。当事件触发后,在accept时,就执行后续的连接信息获取动作,就可以和posix c的accept动作对应起来。

4、recv的移植

在正常模式下,应用层需要向IOCP注册一个读取缓冲区,在读取完成后,IOCP触发一个读取完成事件。比较幸运的是,如果应用层注册的是一个零字节的缓冲区,那么在缓冲区可读时,IOCP也会立即返回一个事件。于是,事件和操作的顺序,就可以和epoll一致了。

事情依然没有结束,当recv执行读取操作时,如果返回WSA_IO_PENDING时,那么操作就没有完成,函数就不能返回。这时候,只能进入阻塞状态,等待读取完成。那么总体的性能就会因此而降低。但同样的是,windows为我们提供一个ioctsocket的FIONREAD命令接口,让我们可以偷看读缓冲区中,究竟有多少个字节已经就绪,这样就可以避免陷入WSA_IO_PENDING的状态。

5、send的移植

send遇到的问题,和recv是一样的。也可以通过注册一个零字节的写缓冲区,在IOCP触发可写事件后,再执行写操作,从而保证和epoll的顺序相同。

但不幸的是,send可以写多少个字节而不会触发WSA_IO_PENDING,windows却没有提供对应的接口。这个问题其实很容易理解,send的可写字节是由连接上的收发双方共同决定的,注意,是双方,而不是单纯地由可写缓冲区单独决定。那么,这个事情就会变得复杂。

简单起见,我们只能在内部管理一个缓冲区,将外部的发送请求数据先行拷贝到这个缓冲区,再由这个缓冲区向IOCP发送数据。通过这个缓冲区,我们就可以控制知道多少个字节可写,即使触发WSA_IO_PENDING,应用层也无需知道,也不会造成内部线程阻塞。但是性能也会因为拷贝而损失。

6、接口移植

windows提供了和posix c一样的socket类函数,因此如果我们改造 后的socket函数显然无法和winsock函数共存,但是又不能不用winsock函数。因此,最好的办法是通过动态加载ws2_32.dll,并导入到函数指针中,这个函数指针可以与原始函数名不同。在我们提供的函数中,仍然可以使用原名,同时,在这些函数中,插入我们而外增加的代码。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章