本章提要
网络应用程序、特别是服务器,经常需要并发地处理请求,以满足“服务质量(quality of service)”的要求。在第5章,我们讲述了“和并发选择(concurrency alternative)相关”的一般性设计权衡(design tradeoff)。在本章,我们将对同步事件多路分离(synchronous event demultiplexing)、多进程、多线程和同步机制作简要概述,利用这些机制,就可以实现第5章介绍过的那些设计。如果通过原始的“C层次”的并发API来开发网络应用程序,将会遇到一些常见的可移植性问题和编程方面的问题,本章也对这些问题进行了讨论。
6.1 同步事件多路分离
“同步事件多路分离器(synchronous event demultiplexer)”是OS提供的一个函数,用于在一组事件源(event source)上等待特定事件的发生。当一个或多个事件源被激活时,函数将返回至调用者。于是,调用者可以处理这些“来自多个源”的事件。通常,同步事件多路分离器被用作“反应式服务器”事件循环(event loop)的基本构件;它以一种“连续”而且“有序”的方式,对“来自客户的事件”进行检查并作出反应。
大多数操作系统都支持一个或多个“同步事件多路分离”函数,例如:
l poll()函数[Rag93],出自System V UNIX;
l WaitForMultipleObjects()函数[Sol98],由Win32提供;
l select()函数[Ste98],在I/O句柄上对事件源进行多路分离1。
我们将重点讨论select(),因为它最常见。在事件驱动型(event-driven)网络应用程序中,可以运用select(),确定I/O操作可以在哪些句柄上同步调用,但不会阻塞那些发起这些调用的应用程序线程。select()函数的C API版本大致如下所示:
int select (int width, // Maximum handle plus 1
fd_set *read_fds, // Set of "read" handles
fd_set *write_fds, // Set of "write" handles
fd_set *except_fds, // Set of "exception" handles
struct timeval *timeout);// Time to wait for events
fd_set是一个结构,表示一组句柄(一个句柄集),用于检查I/O事件。三个fd_set地址参数分别是read_fds、write_fds和except_fds;select()分别检查它们,看它们的句柄是否处于“读”、“写”活动状态,或处于“异常条件”(例如,带外数据(out-of-band data))。select()函数通过修改它的fd_set参数,向调用者通知“活动”和“非活动”句柄的情况。
l 在fd_set中,如果一个句柄值是“非活动”的,那个句柄将被忽略;在select()返回的fd_set中,它的值将是“非活动”的。
l 如果fd_set中的一个句柄值是“活动”的,则select()将会在相应的句柄上检查是否有未处理的事件。如果在这个句柄上有一个未处理事件,则在select()返回的fd_set中,相应的值将是“活动”的。如果这个句柄没有未处理事件,则在返回的fd_set中,它的值将是“非活动”的。
每一个OS平台都会提供一组基本操作,允许应用程序“查询”、“操纵”fd_set。这些操作虽然在某些平台上是宏(macro),在另外一些平台上是函数,但它们具有一组统一的名称和行为:
|
函 数 |
说 明 |
|
FD_ZERO() |
将fd_set中所有句柄的值标记为“非活动” |
|
FD_CLR() |
将fd_set中的一个句柄标记为“非活动” |
|
FD_SET() |
将fd_set中的一个句柄标记为“活动” |
|
FD_ISSET() |
检测fd_set中某一句柄是否“活动” |
select()的最后一个参数是一个“指向struct timeval”的指针;这个结构包含以下两个字段:
struct timeval
{
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
以下三种timeval值可以传给select(),以控制“超时”行为:
|
值 |
行 为 |
|
NULL timeval指针 |
表示select()应该无限期等待,直至至少有一个I/O事件发生,或有一个信号中断了这个调用 |
|
非NULL timeval指针,tv_sec和tv_usec字段为0 |
表示select()应该执行“轮询(poll)”,即,检查所有句柄,并立即返回所有结果 |
|
tv_sec或tv_usec字段 > 0的timeval |
表示select()应该在这段时间上等待I/O事件发生 |






