论坛首页 编程语言技术论坛

集成 IOCP 到 Libevent

浏览 16194 次
该帖已经被评为良好帖
作者 正文
   发表时间:2008-06-01  
C
集成 IOCP 到 Libevent

完整的代码在
http://spserver.googlecode.com/files/libevent-1.4.4-iocp-3.zip

2008.08.23
http://spserver.googlecode.com/files/libevent-1.4.5-stable-iocp-2.zip
增加了 non-blocking connect 的支持。

IOCP 是真正的异步 IO ,Libevent 提供的是一个 event-driven 的接口。

异步 IO 和 event-driven 的区别:
1.对于 event-driven,需要等待内核通知我们去启动一个IO操作,然后直接得到IO操作的结果
2.对于异步IO,我们可以随时无阻塞地启动 IO,然后由内核通知我们 IO 操作何时完成

要把 IOCP 集成到 libevent ,基本的思路是用 IOCP 模拟 event-driven 接口。
在 IOCP 中有一个特性,刚好可以用来模拟 event-driven 的接口:zero byte buffer recv/send。

当我们发起 WSARecv,WSASend 的时候,需要用 WSABUF 传递一个 buffer 进去。
如果我们把这个 buffer 设置为 zero byte ,那么当 socket 可读,或者可写的时候,
通过 GQCS 同样可以得到结果。这样就等于是模拟了 event-driven 的接口。

另一方面,Windows 的 WSAEventSelect 函数提供了一个 event-driven 的 accept 功能。

...
WSAEventSelect( listenFd, hEvent, FD_ACCEPT );
...


问题在于如何把 IOCP 和 WSAEventSelect 集成起来。

通过查看 WSARecv,WSASend 的接口,可以注意到这两个接口都需要提供一个 OVERLAPPED 的结构体。
在这个结构体里有一个 hEvent 的成员。如果设置了这个 hEvent 参数,那么当这个 IO 请求完成的时候,
hEvent 将会被 signal 。详细信息可以参考下面这个 URL :

http://msdn.microsoft.com/en-us/library/ms742203(VS.85).aspx
引用
If the lpCompletionRoutine parameter is NULL, the hEvent parameter of lpOverlapped is signaled when the overlapped operation completes if it contains a valid event object handle. An application can use WSAWaitForMultipleEvents or WSAGetOverlappedResult to wait or poll on the event object.


这样看起来可以通过 WSAWaitForMultipleEvents 来集成 IOCP 和 WSAEventSelect 。
1.用 WSAEventSelect 来处理 accept event
2.用 IOCP 来处理 recv/send event ,发起 recv/send 请求的时候,设置 OVERLAPPED 的 hEvent 参数
3.把 WSAEventSelect 的 hEvent 和 IOCP 的 hEvent 收集起来,用 WSAWaitForMultipleEvents 统一处理

需要注意的时候,上述的第二步中,不需要为每个 recv/send 请求生成一个新的 hEvent,
而是所有的 recv/send 共用一个 hEvent 就行了。

在下面这个 URL 中提到,对于一个 hEvent 如果已经是 signal ,再次 signal ,不会有副作用。
http://msdn.microsoft.com/en-us/library/ms686211(VS.85).aspx
引用
Setting an event that is already set has no effect.



用代码来描述上面的这个思路,大概是下面这样。
首先创建 64 个 hEvent ,其中的 objects[0] 就用于 IOCP 中所有的 IO 操作。
其他剩余的 63 个 hEvent ,就可以用于 accept event 。
程序首先调用 WSAWaitForMultipleEvents 来等待 event 发生。
如果发现是 objects[0] 有事件,那么是 IOCP 的 IO 操作已经有完成的,就调用 GQCS 来处理。
如果发现是其他的 objects 有事件,那么就是有 accept event 发生。

/* objects[0] for iocp operations, object[1..63] for accept */
HANDLE objects[64];
struct event * accepts[64];
 
struct win32iocp_event event1;
event1.overlapped.hEvent = objects[0];
WSARecv( ..., &event1.overlapped, ... );
....
 
struct win32iocp_event event2;
event2.overlapped.hEvent = objects[0];
WSASend( ..., &event2.overlapped, ... );
...
 
WSAEventSelect( ev1->ev_fd, objects[1], FD_ACCEPT );
accepts[1] = ev1;
...
 
WSAEventSelect( ev2->ev_fd, objects[2], FD_ACCEPT );
accepts[2] = ev2;
...
 
int index = WSAWaitForMultipleEvents( 64, objects, FALSE, timeout, FALSE );
index = index - WSA_WAIT_EVENT_0;
 
if( index > 0 ) {
 struct event * event = win32iocp_op->accepts[index];
 event_active (event, EV_READ | EV_ACCEPT, 1);
}
 
if( index == 0 ) {
 for( ; ; ) {
  GetQueuedCompletionPort( ...... );
 }
}
 
   发表时间:2008-06-03  
近来 iunknown 写了很多用 iocp 的东东。请教一下能否比较一下 Linux 的 epoll 和 windows 的 iocp?

据我所知 Linux 是没有异步 IO 的,那么 epoll 应该相当于 iocp 之 socket 版本,能否论证一下?
0 请登录后投票
   发表时间:2008-06-03  
bigpanda 写道
近来 iunknown 写了很多用 iocp 的东东。请教一下能否比较一下 Linux 的 epoll 和 windows 的 iocp?

据我所知 Linux 是没有异步 IO 的,那么 epoll 应该相当于 iocp 之 socket 版本,能否论证一下?


对于 epoll 和 iocp 都算不上熟悉,只是根据自己的兴趣去找了一下资料来看,然后写了一些代码来验证一下自己的理解。就目前的测试来看,两者在高并发和 IO 性能方面,都是不错的。但依目前的经验,很难说已经测试到了它们各自的最优情况。对于这种高并发的性能测试,对于系统参数的调整也是一个很大的方面。如果要做具体的比较,那么需要列举具体的应用场景,具体把代码实现出来,才能做一个比较客观的对比。

Linux 没有异步 IO 应该说的是没有针对 socket 的异步 IO 吧?对于文件 IO 是有 AIO 这套接口的。

iocp 之 socket 版本?这个描述有点不解。
不过通常都是认为 linux 的 epoll 是和 windows 的 iocp 相对应的一种技术。两者常常会被相提并论,主要应该是两者都对高并发提供了良好的支持。但是从细节的角度来说,两者在 IO 模型上是不同的。epoll api 本身就只支持一种 event-driven 的模型,iocp api 除了 async IO 模型之外,如果使用 zero byte buffer 还可以支持 event-driven 的模型。

0 请登录后投票
   发表时间:2008-06-03  
iunknown 写道
bigpanda 写道
近来 iunknown 写了很多用 iocp 的东东。请教一下能否比较一下 Linux 的 epoll 和 windows 的 iocp?

据我所知 Linux 是没有异步 IO 的,那么 epoll 应该相当于 iocp 之 socket 版本,能否论证一下?


对于 epoll 和 iocp 都算不上熟悉,只是根据自己的兴趣去找了一下资料来看,然后写了一些代码来验证一下自己的理解。就目前的测试来看,两者在高并发和 IO 性能方面,都是不错的。但依目前的经验,很难说已经测试到了它们各自的最优情况。对于这种高并发的性能测试,对于系统参数的调整也是一个很大的方面。如果要做具体的比较,那么需要列举具体的应用场景,具体把代码实现出来,才能做一个比较客观的对比。

Linux 没有异步 IO 应该说的是没有针对 socket 的异步 IO 吧?对于文件 IO 是有 AIO 这套接口的。

iocp 之 socket 版本?这个描述有点不解。
不过通常都是认为 linux 的 epoll 是和 windows 的 iocp 相对应的一种技术。两者常常会被相提并论,主要应该是两者都对高并发提供了良好的支持。但是从细节的角度来说,两者在 IO 模型上是不同的。epoll api 本身就只支持一种 event-driven 的模型,iocp api 除了 async IO 模型之外,如果使用 zero byte buffer 还可以支持 event-driven 的模型。


原来你就是讨论组内的那位stephen.nil阿
呵呵,真不错
0 请登录后投票
   发表时间:2008-06-04  
SPServer是一个很不错的框架,受到启发,我用boost::asio实现了一个类似的框架,其中的一个特点,就是很容易在单线程和多线程之间切换,好像在您以前的文章中讨论过这个问题,
原理就是实现一个Wrap类,自动实现线程的传递

http://www.cppblog.com/eXile/archive/2008/06/04/51430.html#52178
0 请登录后投票
   发表时间:2008-06-05  
linux/solaris上的aio有多种回调/通知方式,可能和libevent结合的最简单方式是signal,因为libevent也支持它,不过我没试过,在linux下用过的是内核线程直接写同步队列然后发送socket通知唤醒libevent去处理。solaris上可以用一个port专门处理aio通知,非常方便。虽然aio大部分时候用于处理文件IO,不过它和iocp应该有相似之处吧?iocp的细节我不清楚,如果是“完成-通知”或“完成-回调”方式,应该都可以和libevent结合。
0 请登录后投票
   发表时间:2008-06-05  
GetQueuedCompletionStatusEx也支持超时,用它代替select也可以吧?试试移植一个iocp版本?
0 请登录后投票
   发表时间:2008-06-05  
qiezi 写道
GetQueuedCompletionStatusEx也支持超时,用它代替select也可以吧?试试移植一个iocp版本?


GetQueuedCompletionStatusEx 和 GetQueuedCompletionStatus 的差别是前者可以一次得到多个 event 。
GetQueuedCompletionStatus 本身也支持超时的。

这里提到的要用 WSAWaitForMultipleEvents ,主要是解决 IOCP 和 WSAEventSelect 的集成。
在使用 libevent 的场景中,通常都需要把 accept,recv,send 这几个事件在同一个 event_base 里面来跑,因此集成 IOCP 和 WSAEventSelect 是很有必要的。如果不需要集成 accept 事件,那么只需要使用 zero byte buffer 就可以把 IOCP 当作 select 来用了。

这两天对 libevent 1.4.4 里面的单元测试案例进行了测试,发现了一个问题

http://msdn.microsoft.com/en-us/library/ms686358(VS.85).aspx
引用
Synchronization and Overlapped Input and Output

If an event is used, the hEvent member of the OVERLAPPED structure specifies a
handle to the allocated event object. The system sets the state of the event object
to nonsignaled when a call to the I/O function returns before the operation has been completed.


当多个 WSARecv/WSASend 使用同一个 Event 的时候,会产生 race condition 。
后来在 WSAWaitForMultipleEvents 之前,先调用一次 GetQueuedCompletionStatus ,这样可以消除这个 race condition 的影响。目前已经可以通过 regress.c 中大部分测试案例了。少部分失败的案例主要是由于环境的问题,现在是用 vc6 作为开发环境的,libevent 1.4.4 中的部分代码需要更高版本的 vs 。

最新的代码:http://spserver.googlecode.com/files/libevent-1.4.4-iocp-2.zip
0 请登录后投票
   发表时间:2008-06-05  
去找了 memcached for win32 的源代码,结果可以顺利编译,并且运行起来也很正常。
http://jehiah.cz/projects/memcached-win32/

修改后的,libevent + iocp + memcached 代码下载
http://spserver.googlecode.com/files/memcached-1.2.1-iocp.zip
http://spserver.googlecode.com/files/libevent-1.4.4-iocp-2.zip
0 请登录后投票
   发表时间:2008-08-23  
http://spserver.googlecode.com/files/libevent-1.4.5-stable-iocp-2.zip

最新的版本使用类似于解决 non-blocking accept 的方法实现了 non-blocking connect。
需要增加一个 EV_CONNECT 的标志位,增加了一个 conn_test 的例子来示范如何使用。
0 请登录后投票
论坛首页 编程语言技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics