Linux async io 1
asynchronous io的使用越来越普及。现在有相当多的类库,web服务器和网络编程框架使用了这一技术。比如python的tornado,twisted, c写的httperf, libevent, boost asyncIO。我所在的项目有一个c++开源库http://code.mozy.com/projects/mordor, 也用到了这一技术。顺便提一下,这是一个很强大的库,实现了很多很cool的特性(如fiber).
这个系列的文章计划分为4部分:
1,async io的基础,事件循环
2. 具体io回调处理的实现
3.如何用async io 实现一个socket stream
4.有时间的话,基于async io实现一个http server.
先从基础入手,解释一下什么是async io. 普通的blocking io操作,比如说接受socket消息的时候,线程会block住直到io操作完成。
thread1: io_manager.start()
thread2: do_something() ... doIO() ... do_something_else()
thread3: do_something2() ... doIO() ... do_something_else2()
事件循环是async io的核心所在。 这篇文章接下来的内容将集中在这一机制的实现上。
理解时间循环,首先要理解linux下的select/poll/epoll. 可以选择其中的任何一个作为async io的底层调用。下文一律将以epoll为例。提一下epoll有一个特别的地方,就是不支持不会block的fd操作, 比如说读写regular file. 对regular fd的epoll_ctl操作将返回 EPERM(The target file fd does not support epoll). 关于这三者的介绍和比较,可google之.
fd的事件概括起来只有如下几种:read, write, close, error.
在循环中,调用epoll_wait()我们可以知道自从上次调用epoll_wait(),有多少fd有新的事件发生,从而得到一个events列表。可以根据event的fd和event_type做相应的业务逻辑操作。伪代码如下:
// wait for something to do...
int nfds = epoll_wait(epfd, events,
MAX_EPOLL_EVENTS_PER_RUN,
EPOLL_RUN_TIMEOUT);
if (nfds < 0) die("Error in epoll_wait!");
// for each ready socket
for(int i = 0; i < nfds; i++) {
int fd = events[i].data.fd;
handle_io_on_socket(fd);
}
}
注意这里的handle_io_on_socket(fd), 这只是一个假设的函数。
实际实现的时候,一般的流程是这样的:
1. 启动 io manager线程, io manager一般作为一个sungleton存在。多余的io manager 基本是没有什么意义的。
2. 在业务逻辑线程中,需要读写比如socket时,实现一个asyncRW方法,它调用io manager的registerFD方法,往io manager里注册该socket的fd, event(基本就是read/write)和callback(对应实际的业务逻辑,比如读写fd,然后再做处理). 我将用一个类叫做AsyncIOFD来对这些参数做封装。io manager 里维护一个fd->AsyncIOFD的map.
3. io manager的时间循环中,对每个wait到的fd, 查map得到 AsyncIOFD, 然后调用callback。
4. 当对某一fd的处理完毕,close fd,这将取消epoll的监听。也可以显式地调用io manager的 unregisterFD方法
解释至此,整个流程还是很清晰的。不过我们需要回答一个问题:为什么epoll_wait()会放回事件列表,或者说,events列表不会自动产生,整个逻辑中还缺了一环。
让我们以socket为例来解释这个问题。
首先,socket的read ready事件是比较好理解的。read ready的含义是:有数据需要读,这些数据存在于操作系统底层的socket缓存中.我们和远端的socket建立连接,当远端socket send的数据到达,就会触发本地socket的read ready事件。
再来看看write ready. write需要己方主动触发。不可能自己什么都不做却收到一个write ready事件。write ready的含义是:还有数据需要写,或者说,有数据没写完。而这些数据来自于你的程序之中。 这也是non-blocking write的含义。对于一个non-blocking socket, 调用write立刻返回,这并不确保数据都被写入。保证这一点需要你的程序的控制(比如说检查write的返回是写了多少byte,然后计算是否已经写完了所有的数据)。 而一旦数据没写完,就会有一个write ready事件被触发。 所以,在我们的asyncRW函数中,对于write,我们需要有额外的操作。那就是先write一次,然后再把一切交给io manager线程。否则,我们永远接受不到write ready 的通知!
很多情况下,io manager也是一个定时器 manager. 具体的例子,可以参见httperf(http://www.hpl.hp.com/research/linux/httperf/)的源码。httperf是一个开源的用c实现的性能测试工具。httperf在上文提到的while循环中额外干了一件事情,就是检查定时器,做一些计算和统计工作,然后再调用epoll_wait(或者是select 和pol里l相应的函数)。
事件循环的内容基本就是这些了。后续的内容中会贴上简单的实现代码。
posted on 2011-06-05 21:36 freestyleking 阅读(985) 评论(0) 编辑 收藏 举报