服务器并发调度与 select() 优化策略详解
1. 事件驱动调度器
在服务器应用中,传统的线程调度可能会带来较高的开销,因为存储每个线程或进程状态所需的内存会占用文件缓存,从而可能导致更高的缺失率。当通用操作系统设施成本过高时,应用程序可以考虑自行实现调度。
事件驱动调度器就是一种解决方案。应用程序可以实现自己的内部调度器,通过状态机来管理每个客户端的状态。例如,应用程序可以记住客户端 1 处于 HTTP 处理阶段,客户端 2 正在等待磁盘 I/O,客户端 3 正在等待套接字缓冲区清空以发送响应的下一部分。
然而,内核在处理 I/O 完成事件方面具有优势。当磁盘控制器中断 CPU 表示数据已在内存中时,内核可以尝试调度等待 I/O 的客户端线程。因此,如果 Web 服务器应用要自行在客户端之间进行调度,内核必须通过 API 传递信息,使单线程应用能够查看其发起的所有 I/O 的完成情况。许多操作系统提供了这样的功能,如 Windows NT 3.5 的 I/O 完成端口(IOCP)机制和 UNIX 的 select() 系统调用。
应用程序通常会在一个循环中调用 FindActive() (如 select())。假设总是有一些客户端工作要做,该调用将返回一个有待处理工作的 I/O 描述符列表。Web 服务器处理这些活动描述符后,会再次调用 FindActive()。如果总是有客户端需要关注,就无需进行上下文切换,因为应用程序可以通过状态机在多个并发请求之间进行自己的上下文切换。这种特定于应用程序的内部调度比调用通用的外部调度器更高效,因为应用程序知道在客户端之间切换时必须保存的最小上下文集。
一些服务器,如 Zeus 服务器和原始的