Kernel Space

January 12, 2007

编程原语之一 — I/O 模型

Filed under: 编程珠玑 — agassi @ 2:30 am

 工作了半年了,发现了一些概念性的东西在工作当中非常有用。这些概念几乎场场用到,无论是做什么应用(所以我称之为“原语 primitives”)。以前也略知一二,但是现在发现自己懂得实在是太浅显。最近看了以前找借口没有完成的书,受益匪浅。

 说起来接触网络编程也有很久了,但是始终觉得是混沌一坛。不过仔细一回想,自己确实很少写网络程序。再加上这两年都在学校混,主要也就写写course project,没有什么练手的机会。最近需要实现一个SDK的传输层,打开IDE发现无从下手,顿时觉得自己确实都还给老师了(好像没有老师教过这个:))。立刻找来UNP (3rd edition) 和 NPMW (2nd edition)。这两本书在前面有提到。是Unix 和 Windows下网络编程的圣典。对于这种大簿头的书,直接拣重点看。那些基本的API还是有印象的。

Unix: UNP这本书也是摆在书架很久了,一直觉得是手册,其实就是一个不去读的借口。翻到“I/O Models” 一章,立刻看到几个非常形象的图,顿时心里有了概念。

  • blocking : 最简单的模型
  • non-blocking: 看似节省时间,实际上poll会占用更多的资源,需要和其他模型配合使用
  • I/O multiplexing: 大名鼎鼎的select和poll。写网络程序第一个就会想到select。最大的特点其实就是可以同时wait在多个 I/O 上。从原理上讲还是blocking的,而且不太efficient,因为相比于blocking的I/O,select需要多一个系统调用。poll和select很相似,都是wait指定fd上特定事件的发生。select用fd_set把希望得到相同事件的fd放在一起,而poll是用pollfd 结构保存fd和对应感兴趣的信号。两者的设计大同小异。poll看似几乎被POSIX淘汰了。不过最近出现的epoll(一个对2.4 kernel的补丁,最终在2.6 kernel中正式加入),一跃成为Unix下处理海量连接的最佳方案。
  • signal-driven I/O: 真正的异步模式。(所谓异步,就是I/O事件发生的时候Kernel会利用Callback来通知处理数据的线程,然后该线程再去取数据。上面三个模式都是同步的,也就是说Kernel不会告诉用户程序什么时候I/O ready了。)这个模式利用的是sigaction来注册一个SIGIO的处理函数,然后系统通过SIGIO来callback 信号处理函数。
  • Asynchronous I/O: 另外一种异步模式。主要的区别是,Kernel不仅通知用户进程I/O时间,同时把数据从Kernel空间复制到用户空间,当用户空间进行处理的时候,I/O已经处于“完成”状态了。这个模型实际上就是Windows下的”完成端口”模型。书上说这个模型少有系统实现了,不过Linux是有实现的,不过据说在2.4的Kernel上不太稳定,不知2.6是否好很多。

Windows: “Windows 网络编程”一书,也是很久以前就有了,但是从来没有下功夫去看过。这次翻开,也首先翻到了 “Socket I/O Models”。简单的blocking, non-blocking 在Introduction中的connection-oriented和connection-less中就介绍了。

  • select : 上来就是select模型。这个blocking的模型基本上和Unix下的一模一样。
  • WSAAsyncSelect : Windows自然离不开它自己的东西。这个就是为Windows量身定做的函数。这个函数要求用CreateWindow创建一个窗口。然后在WSAAsyncSelect的参数中放上这个窗口的句柄。在Windows中,窗口就是一个等待处理时间的Entity,当有网络事件发生的时候,这个窗口就会收到一个在WSAAsyncSelect中指定的message,同时包括一个事件的类型。随后在窗口的消息处理函数中进行实际的网络操作。这个模型就是Windows消息句柄版本的signal-driven I/O。
  • WSAEventSelect : 这个其实是blocking的select模式的一种。Windows把Socket和著名的WaitForMultipleObjects结合起来了,利用Event来实现网络事件的通知。说到Event,自然就有signaled和non-signaled的状态以及manual和automatic的reset模式。这个也同样适用于WSAEvent。所有的Event函数都加了WSA的前缀。WaitForMultipleObjects有著名的64个Event的上限,这个WSAWaitForMultipleObjects也不例外,所以一个线程里面只能最多监视64个socket。如果要监视更多,就继续增加线程数量。看起来不是很promising哦。
  • Overlapped: 这个是书中推荐的模式。这个模型需要创建一个WSAOverlapped 结构,然后把这个结构的指针传给WSA函数,WSA函数就会自动把socket设置为non-blocking,然后进入overlapped模式。根据通知事件处理函数的方法不同,这个模型有两个形式:Event notification 和 Completion Routine。 前一个和WSAEventSelect几乎一样,主要是通过放入WSAOverlappped结构的Event句柄来唤醒等待这个I/O的线程,事件处理线程还是依旧使用WSAWaitForMultipleObjects等待;WSAGetOverlappedResult 最终用来取得事件的信息 (依旧有64的上限)。后面一个版本,表面上用到了WSAOverlapped结构,实际上只是初始化一个dummy结构,然后传给WSA函数,让其进入Overlapped模式。真正有用的是创建一个CALLBACK函数,然后把这个函数指针传给WSA函数。当有事件发生的时候,这个回调函数会被调用。这样就绕过了64个的限制。不过第二种方法要求主线程处于Alertable的状态,从而completion routine 有足够的时间处理事件。 Overlapped模型引入了完成的概念。在WSA函数中,会传入一个WSABUF结构,它里面包含了程序开辟的缓冲区,当事件被通知的时候,实际的数据已经放入这个buffer了,而不需要再复制一遍,从而提高了效率(一般的做法是把WSAOverlapped结构和一个WSABUF以及一个缓冲区做成一个PerIOData结构)。
  • Completion Port: 有名的”完成端口“模型。就是Windows版本的Asynchronous I/O。基本步骤就是创建少量的工作线程,然后创建完成端口,创建监听端口,绑定监听端口到完成端口,issue WSAAccept异步事件,在工作线程中接收连接,把新连接加入到完成端口中,在工作线程中处理已经建立的连接的IN/OUT I/O。当然,既然是完成端口,传输的数据在事件通知的时候就已经在buffer里面了。这个模型比Overlapped模型又更进了一步:每个完成端口都可以绑定一个整型的CompletionKey,这样就可以放入一个指针指向一个PerSocketData的结构,其中包括Socket的Handle,Peer的IP地址等等(类似于epoll当中的epoll_data,看到这里觉得有一种天下一统的感觉)。这个模型是目前Windows下处理海量连接的最高效的方法,稍后将讨论和比较它和epoll。

IMHO,其实就只有两种模型,至于blocking和non-blocking只是socket的本身的类型,blocking的socket函数本身就是一种blocking的模型,而non-blocking的socket是其他所有模型的基础:

  1. Synchronous,表示有一个线程处于blocked状态专门负责等待网络事件的到来(including blocking,select, poll, WSAAsyncSelect, WSAEventSelect, Overlapped with Event Notification, Completion Port); WSAAsyncSelect 其实比较特殊,用户本身不用专门创建一个线程来等待,但是Windows窗口本身有一个消息循环,在等待网络事件的时候,这个消息循环实际上帮助实现了blocked线程的工作,所以还是把它放在这个类别下。
  2. Asynchronous,表示只是提供一个回调函数,issue 网络动作后,不需要专门的线程进行等待,网络事件发生后,kernel会自动调用回调处理函数 (including signal-driven I/O, Asynchronous I/O,Overlapped with Completion Routine)。

暂时就想到这些,以后有新的想法再补充。

Advertisements

Leave a Comment »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Create a free website or blog at WordPress.com.

%d bloggers like this: