2010-08-29

Lighttpd mod_fastcgi源码分析

Views: 27390 | Add Comments

最近在设计一种网络服务器架构, 最重要的一点是把耗时操作委托给工作进程(或者线程)来做, 所以考察一下 fastcgi. 大概看了下 lighttpd 的 mod_fastcgi 的源码, 没想到立即被卡住了. 根据我的想法, PHP 等 fastcgi 程序(php-cgi 进程)监听网络, 然后 mod_fastcgi 只需要 connect 这些进程即可, 奇怪的是, 我竟然看到了 listen! -

fcgi_spawn_connection() 函数:

fcgi_fd = socket(socket_type, SOCK_STREAM, 0);

if (-1 == connect(fcgi_fd, fcgi_addr, servlen)) {
    close(fcgi_fd);
    fcgi_fd = socket(socket_type, SOCK_STREAM, 0);
    /* create socket */
    bind(fcgi_fd, fcgi_addr, servlen);
    listen(fcgi_fd, 1024);

    switch ((child = fork())) {
    case 0: {
        // child process
        if(fcgi_fd != FCGI_LISTENSOCK_FILENO) {
            close(FCGI_LISTENSOCK_FILENO);
            dup2(fcgi_fd, FCGI_LISTENSOCK_FILENO);
            close(fcgi_fd);
        }
        /* exec the cgi */
        execve(arg.ptr[0], arg.ptr, env.ptr);
        exit(errno);
    }
}

我很奇怪, mod_fastcgi 为什么要监听网络连接? 谁会连接它? 问了同事, 同事说可能是 fastcgi 进程要连接它, 可问题是 fastcgi 进程怎么知道连接什么地方, 更不用说, fastcgi 协议里没有提到要 fastcgi 进程主动连接 Web Server 一说.

再看一遍代码, 我注意到了 dup2() 调用, 会不会是 lighttpd 为 php-cgi 进程创建了一个监听 socket? 于是看了下 PHP fastcgi 的代码, 如下解析命令行参数的一段:

"  -b <address:port>|<port> Bind Path for external FASTCGI Server mode\n"
/* if we're started on command line, check to see if
   we are being started as an 'external' fastcgi
   server by accepting a bindpath parameter. */
case 'b':
...
if (bindpath) {
    fcgi_fd = fcgi_listen(bindpath, 128);
}

也就是说, 如果指定了 -b 选项, php-cgi 就会创建监听进程, 如果没有的话, fcgi_fd 就是默认的 0(标准输入的文件描述符), 也就是 FCGI_LISTENSOCK_FILENO.

所以, lighttpd mod_fastcgi 的逻辑是这样, 如果需要 spawn php-cgi 进程, 它会创建一个监听的 socket, 然后把该 socket dup2 到 标准输入的 fd, 接着 fork 并用 execve() 函数执行 php-cgi 程序, 这样, php-cgi 程序就可以使用该监听 socket 来接受 mod_fastcgi 的网络连接了.

只要知道了这个逻辑, 细节问题不必深究.

Related posts:

  1. Linux下编译安装Apache/Nginx/Lighttpd+PHP+MySQL
  2. lighttpd配置HTTPS(SSL)
  3. Master-Workers 模式处理高负载
  4. Ideawu.P2P API 简介
  5. SSH ProxyCommand及其思想
Posted by ideawu at 2010-08-29 18:00:51

Leave a Comment