王骏的博客
编程、网络技术点滴...

公告

逐渐将VC知识库的博客迁移到这里!

随笔分类

随笔档案

相册

最新评论

阅读排行榜

评论排行榜

程序员博客   首页  新随笔  订阅  管理  登录 
 
王骏的博客 阅读(1635) 评论(1)
这里要说的是Unix socket API的一个缺点。假设,网站服务器使用了多个Listen语句以监听多个端口或者多个地址, Apache会使用select(2)以检测每个socket是否就绪。 select(2)会表明一个socket有 0 或者 至少一个 连接正等候处理。 由于Apache的模型是多子进程的,所有空闲进程会同时检测新的连接。 一个很天真的实现方法是这样的(这些例子并不是源代码,只是为了说明问题而已):

for (;;) {
    
for (;;) {
        fd_set accept_fds;

        FD_ZERO (
&accept_fds);
        
for (i = first_socket; i <= last_socket; ++i) {
        FD_SET (i, 
&accept_fds);
        }

        rc 
= select (last_socket+1&accept_fds, NULL, NULL, NULL);
        
if (rc < 1continue;
        new_connection 
= -1;
        
for (i = first_socket; i <= last_socket; ++i) {
        
if (FD_ISSET (i, &accept_fds)) {
            new_connection 
= accept (i, NULL, NULL);
            
if (new_connection != -1break;
        }

        }

        
if (new_connection != -1break;
    }

    process the new_connection;
    }


这种天真的实现方法有一个严重的“饥饿”问题。如果多个子进程同时执行这个循环, 则在多个请求之间,进程会被阻塞在select, 随即进入循环并试图accept此连接, 但是只有一个进程可以成功执行(假设还有一个连接就绪), 而其余的则会被阻塞在accept。 这样,只有那一个socket可以处理请求,而其他都被锁住了,直到有足够多的请求将它们唤醒。 此“饥饿”问题在PR#467中有专门的讲述。 至少有两种解决方案.

一种方案是使用非阻塞型socket,不阻塞子进程并允许它立即继续执行。 但是,这样会浪费CPU时间。设想一下,select有10个子进程, 当一个请求到达的时候,其中9个也被唤醒,并试图accept此连接, 继而进入select循环,而无所事事,并且其间, 没有一个子进程能够响应出现在其他socket的请求,直到退出select循环。 总之,这个方案效率并不怎么高,除非你有很多的CPU,而且开了很多的子进程。

另一种也是Apache所使用的方案是,使内层循环的入口串行化,形如 (不同之处以高亮度显示):

for (;;) {
    accept_mutex_on ();
    
for (;;) {
        fd_set accept_fds;

        FD_ZERO (
&accept_fds);
        
for (i = first_socket; i <= last_socket; ++i) {
        FD_SET (i, 
&accept_fds);
        }

        rc 
= select (last_socket+1&accept_fds, NULL, NULL, NULL);
        
if (rc < 1continue;
        new_connection 
= -1;
        
for (i = first_socket; i <= last_socket; ++i) {
        
if (FD_ISSET (i, &accept_fds)) {
            new_connection 
= accept (i, NULL, NULL);
            
if (new_connection != -1break;
        }

        }

        
if (new_connection != -1break;
    }

    accept_mutex_off ();
    process the new_connection;
    }

函数accept_mutex_on和accept_mutex_off 实现了一个互斥的信号灯,在任何时刻只能为一个进程所拥有。实现互斥的方法有多种, 其定义位于src/conf.h(1.3以前的版本) 或src/include/ap_config.h (1.3及更新版本)中。在有些根本没有锁定机制的体系中,使用多个Listen指令就是不安全的。

评论列表
Alanis
Just cause it's simple doesn't mean it's not super hepflul.

发表评论
切换编辑模式