资源的限制条件
在设计任何服务器应用程序时,其强健性是主要的目标。也就是说,
你的应用程序要能够应对任何突发的问题,例如并发客户请求数达到峰值、可用内存临时出现不足、以及其它短时间的现象。这就要求程序的设计者注意Windows NT和2000系统下的资源限制条件的问题,从容地处理突发性事件。
你可以直接控制的、最基本的资源就是网络带宽。通常,使用用户数据报协议(UDP)的应用程序都可能会比较注意带宽方面的限制,以最大限度地减少包的丢失。然而,在使用TCP连接时,服务器必须十分小心地控制好,防止网络带宽过载超过一定的时间,否则将需要重发大量的包或造成大量连接中断。关于带宽管理的方法应根据不同的应用程序而定,这超出了本文讨论的范围。
虚拟内存的使用也必须很小心地管理。通过谨慎地申请和释放内存,或者应用lookaside lists(一种高速缓存)技术来重新使用已分配的内存,将有助于控制服务器应用程序的内存开销(原文为“让服务器应用程序留下的脚印小一点”),避免操作系统频繁地将应用程序申请的物理内存交换到虚拟内存中(原文为“让操作系统能够总是把更多的应用程序地址空间更多地保留在内存中”)。你也可以通过SetWorkingSetSize()这个Win32 API让操作系统分配给你的应用程序更多的物理内存。
在使用Winsock时还可能碰到另外两个非直接的资源不足情况。一个是被锁定的内存页面的极限。如果你把AFD.SYS的缓冲关闭,当应用程序收发数据时,应用程序缓冲区的所有页面将被锁定到物理内存中。这是因为内核驱动程序需要访问这些内存,在此期间这些页面不能交换出去。如果操作系统需要给其它应用程序分配一些可分页的物理内存,而又没有足够的内存时就会发生问题。我们的目标是要防止写出一个病态的、锁定所有物理内存、让系统崩溃的程序。也就是说,你的程序锁定内存时,不要超出系统规定的内存分页极限。
在Windows NT和2000系统上,所有应用程序总共可以锁定的内存大约是物理内存的1/8(不过这只是一个大概的估计,不是你计算内存的依据)。如果你的应用程序不注意这一点,当你的发出太多的重叠收发调用,而且I/O没来得及完成时,就可能偶尔发生ERROR_INSUFFICIENT_RESOURCES的错误。在这种情况下你要避免过度锁定内存。同时要注意,系统会锁定包含你的缓冲区所在的整个内存页面,因此缓冲区靠近页边界时是有代价的(译者理解,缓冲区如果正好超过页面边界,那怕是1个字节,超出的这个字节所在的页面也会被锁定)。
另外一个限制是你的程序可能会遇到系统未分页池资源不足的情况。所谓未分页池是一块永远不被交换出去的内存区域,这块内存用来存储一些供各种内核组件访问的数据,其中有的内核组件是不能访问那些被交换出去的页面空间的。Windows NT和2000的驱动程序能够从这个特定的未分页池分配内存。
当应用程序创建一个套接字(或者是类似的打开某个文件)时,内核会从未分页池中分配一定数量的内存,而且在绑定、连接套接字时,内核又会从未分页池中再分配一些内存。当你注意观察这种行为时你将发现,如果你发出某些I/O请求时(例如收发数据),你会从未分页池里再分配多一些内存(比如要追踪某个待决的I/O操作,你可能需要给这个操作添加一个自定义结构,如前文所提及的)。最后这就可能会造成一定的问题,操作系统会限制未分页内存的用量。
在Windows NT和2000这两种操作系统上,给每个连接分配的未分页内存的具体数量是不同的,未来版本的Windows很可能也不同。为了使应用程序的生命期更长,你就不应该计算对未分页池内存的具体需求量。
你的程序必须防止消耗到未分页池的极限。当系统中未分页池剩余空间太小时,某些与你的应用程序毫无关系的内核驱动就会发疯,甚至造成系统崩溃,特别是当系统中有第三方设备或驱动程序时,更容易发生这样的惨剧(而且无法预测)。同时你还要记住,同一台电脑上还可能运行有其它同样消耗未分页池的其它应用程序,因此在设计你的应用程序时,对资源量的预估要特别保守和谨慎。
处理资源不足的问题是十分复杂的,因为发生上述情况时你不会收到特别的错误代码,通常你只能收到一般性的WSAENOBUFS或者ERROR_INSUFFICIENT_RESOURCES 错误。要处理这些错误,首先,把你的应用程序工作配置调整到合理的最大值(译者注:所谓工作配置,是指应用程序各部分运行中所需的内存用量,请参考 http://msdn.microsoft.com/msdnmag/issues/1000/Bugslayer/Bugslayer1000.asp ,关于内存优化,译者另有译文),如果错误继续出现,那么注意检查是否是网络带宽不足的问题。之后,请确认你没有同时发出太多的收发调用。最后,如果还是收到资源不足的错误,那就很可能是遇到了未分页内存池不足的问题了。要释放未分页内存池空间,请关闭应用程序中相当部分的连接,等待系统自行渡过和修正这个瞬时的错误。
(译者) 刘西齐 [email protected]