首页 > 代码库 > lighttpd+fastcgi模块分析

lighttpd+fastcgi模块分析

一开始不怎么明白fastcgi和cgi的区别,查了资料说,fastcgi多了一个进程池,不要每次都fork和退出

这个不是重点,还是对着代码看吧

怎样在lighttpd运行php呢,需要下面这样配置

把fastcig模块的前面井号去掉,然后在下面加上这个相关的配置就可以

fastcgi.debug = 1
fastcgi.server = ( ".php" =>
( "localhost" =>
  (
  "host"=>"127.0.0.1",
  "port"=>4000,
  #"socket" => "/tmp/php.socket",
  "bin-path" => "/usr/bin/php-cgi"
  )
)

)

重启lighttpd,然后就可以访问了

用命令看一下cgi进程

[root@fire-16-168 ~]# ps aux|grep cgi
root 21614 0.0 0.1 471616 18276 ? Ss 15:29 0:00 /usr/bin/php-cgi
root 21615 0.0 0.1 471616 18280 ? Ss 15:29 0:00 /usr/bin/php-cgi
root 21616 0.0 0.1 471616 18276 ? Ss 15:29 0:00 /usr/bin/php-cgi
root 21617 0.0 0.1 471616 18280 ? Ss 15:29 0:00 /usr/bin/php-cgi
root 21619 0.0 0.0 471616 6092 ? S 15:29 0:00 /usr/bin/php-cgi
root 21620 0.0 0.0 471616 6088 ? S 15:29 0:00 /usr/bin/php-cgi
root 21625 0.0 0.0 471616 6088 ? S 15:29 0:00 /usr/bin/php-cgi
root 21628 0.0 0.0 471616 6088 ? S 15:29 0:00 /usr/bin/php-cgi

呵呵,总共有8个进程,

S Interruptible sleep (waiting for an event to complete)

s is a session leader

仔细观察,你会发现这些php-cgi的状态不尽相同,有的是Ss,有的是S,通过man ps你能找到这些状态的含义:

也就是说,Ss状态的进程都是主进程(max-procs代表的那些进程),而S状态的进程都是子进程(PHP_FCGI_CHILDREN代表的那些进程)。如果不相信,你可以使用命令核实一下数量:

再看看网络连接是怎样的

[root@fire-16-168 ~]# netstat -anp|grep cgi
tcp 0 0 127.0.0.1:4000 0.0.0.0:* LISTEN 21614/php-cgi
tcp 0 0 127.0.0.1:4001 0.0.0.0:* LISTEN 21615/php-cgi
tcp 0 0 127.0.0.1:4002 0.0.0.0:* LISTEN 21616/php-cgi
tcp 0 0 127.0.0.1:4003 0.0.0.0:* LISTEN 21617/php-cgi

其中只有4个进程在监听,为什么会这样,还是看代码吧

                for (n = 0; n < da_ext->value->used; n++) {                    data_array *da_host = (data_array *)da_ext->value->data[n];                    fcgi_extension_host *host;                    config_values_t fcv[] = {                        { "host",              NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },       /* 0 主机*/                        { "docroot",           NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },       /* 1 端口*/                        { "mode",              NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },       /* 2 */                        { "socket",            NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },       /* 3 可以用unix套接字域去连接*/                        { "bin-path",          NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },       /* 4 cgi的位置*/                        { "check-local",       NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION },      /* 5 */                        { "port",              NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },        /* 6 */                        { "min-procs-not-working",         NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },        /* 7 this is broken for now */                        { "max-procs",         NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },        /* 8 最大cgi进程数*/                        { "max-load-per-proc", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },        /* 9 */                        { "idle-timeout",      NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },        /* 10 */                        { "disable-time",      NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },        /* 11 */                        { "bin-environment",   NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },        /* 12 这里是可以传给php-cgi的参数 */                        { "bin-copy-environment", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },     /* 13 */                        { "broken-scriptfilename", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION },  /* 14 */                        { "allow-x-send-file",  NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION },      /* 15 */                        { "strip-request-uri",  NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },      /* 16 */                        { "kill-signal",        NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },      /* 17 */                        { "fix-root-scriptname",   NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION },  /* 18 */                        { NULL,                NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }                    };                    if (da_host->type != TYPE_ARRAY) {                        log_error_write(srv, __FILE__, __LINE__, "ssSBS",                                "unexpected type for key:",                                "fastcgi.server",                                "[", da_host->key, "](string)");                        return HANDLER_ERROR;                    }            #初始化host结构                    host = fastcgi_host_init();                    buffer_copy_string_buffer(host->id, da_host->key);            #host默认值                    host->check_local  = 1;                    host->min_procs    = 4;                    host->max_procs    = 4;                    host->max_load_per_proc = 1;                    host->idle_timeout = 60;                    host->mode = FCGI_RESPONDER;                    host->disable_time = 60;                    host->break_scriptfilename_for_php = 0;                    host->allow_xsendfile = 0; /* handle X-LIGHTTPD-send-file */                    host->kill_signal = SIGTERM;                    host->fix_root_path_name = 0;                    fcv[0].destination = host->host;                    fcv[1].destination = host->docroot;                    fcv[2].destination = fcgi_mode;                    fcv[3].destination = host->unixsocket;                    fcv[4].destination = host->bin_path;                    fcv[5].destination = &(host->check_local);                    fcv[6].destination = &(host->port);                    fcv[7].destination = &(host->min_procs);                    fcv[8].destination = &(host->max_procs);                    fcv[9].destination = &(host->max_load_per_proc);                    fcv[10].destination = &(host->idle_timeout);                    fcv[11].destination = &(host->disable_time);                    fcv[12].destination = host->bin_env;                    fcv[13].destination = host->bin_env_copy;                    fcv[14].destination = &(host->break_scriptfilename_for_php);                    fcv[15].destination = &(host->allow_xsendfile);                    fcv[16].destination = host->strip_request_uri;                    fcv[17].destination = &(host->kill_signal);                    fcv[18].destination = &(host->fix_root_path_name);            #进行配置文件替换                    if (0 != config_insert_values_internal(srv, da_host->value, fcv)) {                        return HANDLER_ERROR;                    }          #判断有没有设置端口和socket                    if ((!buffer_is_empty(host->host) || host->port) &&                        !buffer_is_empty(host->unixsocket)) {                        log_error_write(srv, __FILE__, __LINE__, "sbsbsbs",                                "either host/port or socket have to be set in:",                                da->key, "= (",                                da_ext->key, " => (",                                da_host->key, " ( ...");                        return HANDLER_ERROR;                    }                      #使用unix domain socket                    if (!buffer_is_empty(host->unixsocket)) {                        /* unix domain socket */                        struct sockaddr_un un;                        if (host->unixsocket->used > sizeof(un.sun_path) - 2) {                            log_error_write(srv, __FILE__, __LINE__, "sbsbsbs",                                    "unixsocket is too long in:",                                    da->key, "= (",                                    da_ext->key, " => (",                                    da_host->key, " ( ...");                            return HANDLER_ERROR;                        }                    } else {                        /* tcp/ip */                        if (buffer_is_empty(host->host) &&                            buffer_is_empty(host->bin_path)) {                            log_error_write(srv, __FILE__, __LINE__, "sbsbsbs",                                    "host or binpath have to be set in:",                                    da->key, "= (",                                    da_ext->key, " => (",                                    da_host->key, " ( ...");                            return HANDLER_ERROR;                        } else if (host->port == 0) {                            log_error_write(srv, __FILE__, __LINE__, "sbsbsbs",                                    "port has to be set in:",                                    da->key, "= (",                                    da_ext->key, " => (",                                    da_host->key, " ( ...");                            return HANDLER_ERROR;                        }                    }                    if (!buffer_is_empty(host->bin_path)) {                        /* a local socket + self spawning */                        size_t pno;                        /* HACK:  just to make sure the adaptive spawing is disabled */                        host->min_procs = host->max_procs;                        if (host->min_procs > host->max_procs) host->max_procs = host->min_procs;                        if (host->max_load_per_proc < 1) host->max_load_per_proc = 0;                        if (s->debug) {                            log_error_write(srv, __FILE__, __LINE__, "ssbsdsbsdsd",                                    "--- fastcgi spawning local",                                    "\n\tproc:", host->bin_path,                                    "\n\tport:", host->port,                                    "\n\tsocket", host->unixsocket,                                    "\n\tmin-procs:", host->min_procs,                                    "\n\tmax-procs:", host->max_procs);                        }              #开始创建进程                        for (pno = 0; pno < host->min_procs; pno++) {                            fcgi_proc *proc;                            proc = fastcgi_process_init();                            proc->id = host->num_procs++;                            host->max_id++;                            if (buffer_is_empty(host->unixsocket)) {                                proc->port = host->port + pno;                            } else {                                buffer_copy_string_buffer(proc->unixsocket, host->unixsocket);                                buffer_append_string_len(proc->unixsocket, CONST_STR_LEN("-"));                                buffer_append_long(proc->unixsocket, pno);                            }                            if (s->debug) {                                log_error_write(srv, __FILE__, __LINE__, "ssdsbsdsd",                                        "--- fastcgi spawning",                                        "\n\tport:", host->port,                                        "\n\tsocket", host->unixsocket,                                        "\n\tcurrent:", pno, "/", host->min_procs);                            }                 #这个开始创建套接字                            if (fcgi_spawn_connection(srv, p, host, proc)) {                                log_error_write(srv, __FILE__, __LINE__, "s",                                        "[ERROR]: spawning fcgi failed.");                                return HANDLER_ERROR;                            }                            fastcgi_status_init(srv, p->statuskey, host, proc);                            proc->next = host->first;                            if (host->first)     host->first->prev = proc;                            host->first = proc;                        }                    } else {                        fcgi_proc *proc;                        proc = fastcgi_process_init();                        proc->id = host->num_procs++;                        host->max_id++;                        host->active_procs++;                        proc->state = PROC_STATE_RUNNING;                        if (buffer_is_empty(host->unixsocket)) {                            proc->port = host->port;                        } else {                            buffer_copy_string_buffer(proc->unixsocket, host->unixsocket);                        }                        fastcgi_status_init(srv, p->statuskey, host, proc);                        host->first = proc;                        host->min_procs = 1;                        host->max_procs = 1;                    }                    if (!buffer_is_empty(fcgi_mode)) {                        if (strcmp(fcgi_mode->ptr, "responder") == 0) {                            host->mode = FCGI_RESPONDER;                        } else if (strcmp(fcgi_mode->ptr, "authorizer") == 0) {                            host->mode = FCGI_AUTHORIZER;                            if (buffer_is_empty(host->docroot)) {                                log_error_write(srv, __FILE__, __LINE__, "s",                                        "ERROR: docroot is required for authorizer mode.");                                return HANDLER_ERROR;                            }                        } else {                            log_error_write(srv, __FILE__, __LINE__, "sbs",                                    "WARNING: unknown fastcgi mode:",                                    fcgi_mode, "(ignored, mode set to responder)");                        }                    }                    /* if extension already exists, take it */                    fastcgi_extension_insert(s->exts, da_ext->key, host);                }

 

ok,分析一下流程

1,读取配置文件

2,设置一下默认参数(如果我们某些属性没有设置的话,由系统参数去配置)例如 min-proc 系统默认为4个啊

3,创建套接字 根据fcgi_spawn_connection 这个函数

我们开始分析这个函数

  1 static int fcgi_spawn_connection(server *srv,  2                  plugin_data *p,  3                  fcgi_extension_host *host,  4                  fcgi_proc *proc) {  5     int fcgi_fd;  6     int socket_type, status;  7     struct timeval tv = { 0, 100 * 1000 };  8 #ifdef HAVE_SYS_UN_H  9     struct sockaddr_un fcgi_addr_un; 10 #endif 11     struct sockaddr_in fcgi_addr_in; 12     struct sockaddr *fcgi_addr; 13  14     socklen_t servlen; 15  16 #ifndef HAVE_FORK 17     return -1; 18 #endif 19  20     if (p->conf.debug) { 21         log_error_write(srv, __FILE__, __LINE__, "sdb", 22                 "new proc, socket:", proc->port, proc->unixsocket); 23     } 24      #如果unixsocket套接字不为空,也就是有设置啦 25     if (!buffer_is_empty(proc->unixsocket)) { 26         memset(&fcgi_addr, 0, sizeof(fcgi_addr)); 27  28 #ifdef HAVE_SYS_UN_H 29         fcgi_addr_un.sun_family = AF_UNIX; 30         strcpy(fcgi_addr_un.sun_path, proc->unixsocket->ptr); 31  32 #ifdef SUN_LEN 33         servlen = SUN_LEN(&fcgi_addr_un); 34 #else 35         /* stevens says: */ 36         servlen = proc->unixsocket->used + sizeof(fcgi_addr_un.sun_family); 37 #endif 38         socket_type = AF_UNIX; 39         fcgi_addr = (struct sockaddr *) &fcgi_addr_un; 40  41         buffer_copy_string_len(proc->connection_name, CONST_STR_LEN("unix:")); 42         buffer_append_string_buffer(proc->connection_name, proc->unixsocket); 43  44 #else 45         log_error_write(srv, __FILE__, __LINE__, "s", 46                 "ERROR: Unix Domain sockets are not supported."); 47         return -1; 48 #endif 49     } else { #这里就是走tcp模式 50         fcgi_addr_in.sin_family = AF_INET; 51  52         if (buffer_is_empty(host->host)) { 53             fcgi_addr_in.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 54         } else { 55             struct hostent *he; 56  57             /* set a useful default */ 58             fcgi_addr_in.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 59  60               #获取主机的真实地址 61             if (NULL == (he = gethostbyname(host->host->ptr))) { 62                 log_error_write(srv, __FILE__, __LINE__, 63                         "sdb", "gethostbyname failed: ", 64                         h_errno, host->host); 65                 return -1; 66             } 67  68             if (he->h_addrtype != AF_INET) { 69                 log_error_write(srv, __FILE__, __LINE__, "sd", "addr-type != AF_INET: ", he->h_addrtype); 70                 return -1; 71             } 72  73             if (he->h_length != sizeof(struct in_addr)) { 74                 log_error_write(srv, __FILE__, __LINE__, "sd", "addr-length != sizeof(in_addr): ", he->h_length); 75                 return -1; 76             } 77  78             memcpy(&(fcgi_addr_in.sin_addr.s_addr), he->h_addr_list[0], he->h_length); 79  80         }
#绑定端口
81 fcgi_addr_in.sin_port = htons(proc->port); 82 servlen = sizeof(fcgi_addr_in); 83 84 socket_type = AF_INET; 85 fcgi_addr = (struct sockaddr *) &fcgi_addr_in; 86 87 buffer_copy_string_len(proc->connection_name, CONST_STR_LEN("tcp:")); 88 if (!buffer_is_empty(host->host)) { 89 buffer_append_string_buffer(proc->connection_name, host->host); 90 } else { 91 buffer_append_string_len(proc->connection_name, CONST_STR_LEN("localhost")); 92 } 93 buffer_append_string_len(proc->connection_name, CONST_STR_LEN(":")); 94 buffer_append_long(proc->connection_name, proc->port); 95 } 96 #建立socket 97 if (-1 == (fcgi_fd = socket(socket_type, SOCK_STREAM, 0))) { 98 log_error_write(srv, __FILE__, __LINE__, "ss", 99 "failed:", strerror(errno));100 return -1;101 }102 #连接socket103 if (-1 == connect(fcgi_fd, fcgi_addr, servlen)) {104 /* server is not up, spawn it 连接失败,则说明服务器还没有起来 */105 pid_t child;106 int val;107 108 if (errno != ENOENT &&109 !buffer_is_empty(proc->unixsocket)) {110 unlink(proc->unixsocket->ptr);111 }112 113 close(fcgi_fd);114 115 /* reopen socket 重新打开socket */116 if (-1 == (fcgi_fd = socket(socket_type, SOCK_STREAM, 0))) {117 log_error_write(srv, __FILE__, __LINE__, "ss",118 "socket failed:", strerror(errno));119 return -1;120 }121 122 val = 1;123 if (setsockopt(fcgi_fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0) {124 log_error_write(srv, __FILE__, __LINE__, "ss",125 "socketsockopt failed:", strerror(errno));126 return -1;127 }128 129 /* create socket */130 if (-1 == bind(fcgi_fd, fcgi_addr, servlen)) {131 log_error_write(srv, __FILE__, __LINE__, "sbs",132 "bind failed for:",133 proc->connection_name,134 strerror(errno));135 return -1;136 }137 #监听socket138 if (-1 == listen(fcgi_fd, 1024)) {139 log_error_write(srv, __FILE__, __LINE__, "ss",140 "listen failed:", strerror(errno));141 return -1;142 }143 144 #ifdef HAVE_FORK 开始生产进程145 switch ((child = fork())) {146 case 0: {147 size_t i = 0;148 char *c;149 char_array env;150 char_array arg;151 152 /* create environment */153 env.ptr = NULL;154 env.size = 0;155 env.used = 0;156 157 arg.ptr = NULL;158 arg.size = 0;159 arg.used = 0;160 161 if(fcgi_fd != FCGI_LISTENSOCK_FILENO) {162 close(FCGI_LISTENSOCK_FILENO);163 dup2(fcgi_fd, FCGI_LISTENSOCK_FILENO);164 close(fcgi_fd);165 }166 167 openDevNull(STDERR_FILENO);168 169 /* we don‘t need the client socket */170 for (i = 3; i < 256; i++) {171 close(i);172 }173 174 /* build clean environment */175 if (host->bin_env_copy->used) {176 for (i = 0; i < host->bin_env_copy->used; i++) {177 data_string *ds = (data_string *)host->bin_env_copy->data[i];178 char *ge;179 180 if (NULL != (ge = getenv(ds->value->ptr))) {181 env_add(&env, CONST_BUF_LEN(ds->value), ge, strlen(ge));182 }183 }184 } else {185 for (i = 0; environ[i]; i++) {186 char *eq;187 188 if (NULL != (eq = strchr(environ[i], =))) {189 env_add(&env, environ[i], eq - environ[i], eq+1, strlen(eq+1));190 }191 }192 }193 194 /* create environment */195 for (i = 0; i < host->bin_env->used; i++) {196 data_string *ds = (data_string *)host->bin_env->data[i];197 198 env_add(&env, CONST_BUF_LEN(ds->key), CONST_BUF_LEN(ds->value));199 }200 201 for (i = 0; i < env.used; i++) {202 /* search for PHP_FCGI_CHILDREN */203 if (0 == strncmp(env.ptr[i], "PHP_FCGI_CHILDREN=", sizeof("PHP_FCGI_CHILDREN=") - 1)) break;204 }205 206 /* not found, add a default */207 if (i == env.used) {208 env_add(&env, CONST_STR_LEN("PHP_FCGI_CHILDREN"), CONST_STR_LEN("1"));209 }210 211 env.ptr[env.used] = NULL;212 #处理cgi程序名字213 parse_binpath(&arg, host->bin_path);214 215 /* chdir into the base of the bin-path,216 * search for the last / */217 if (NULL != (c = strrchr(arg.ptr[0], /))) {218 *c = \0;219 220 /* change to the physical directory */221 if (-1 == chdir(arg.ptr[0])) {222 *c = /;223 log_error_write(srv, __FILE__, __LINE__, "sss", "chdir failed:", strerror(errno), arg.ptr[0]);224 }225 *c = /;226 }227 228 229 /* exec the cgi 开始执行cgi */230 execve(arg.ptr[0], arg.ptr, env.ptr);231 232 /* log_error_write(srv, __FILE__, __LINE__, "sbs",233 "execve failed for:", host->bin_path, strerror(errno)); */234 235 exit(errno);236 237 break;238 }239 case -1:240 /* error */241 break;242 default:243 /* father */244 245 /* wait */246 select(0, NULL, NULL, NULL, &tv);247 248 switch (waitpid(child, &status, WNOHANG)) {249 case 0:250 /* child still running after timeout, good */251 break;252 case -1:253 /* no PID found ? should never happen */254 log_error_write(srv, __FILE__, __LINE__, "ss",255 "pid not found:", strerror(errno));256 return -1;257 default:258 log_error_write(srv, __FILE__, __LINE__, "sbs",259 "the fastcgi-backend", host->bin_path, "failed to start:");260 /* the child should not terminate at all */261 if (WIFEXITED(status)) {262 log_error_write(srv, __FILE__, __LINE__, "sdb",263 "child exited with status",264 WEXITSTATUS(status), host->bin_path);265 log_error_write(srv, __FILE__, __LINE__, "s",266 "If you‘re trying to run PHP as a FastCGI backend, make sure you‘re using the FastCGI-enabled version.\n"267 "You can find out if it is the right one by executing ‘php -v‘ and it should display ‘(cgi-fcgi)‘ "268 "in the output, NOT ‘(cgi)‘ NOR ‘(cli)‘.\n"269 "For more information, check http://trac.lighttpd.net/trac/wiki/Docs%3AModFastCGI#preparing-php-as-a-fastcgi-program"270 "If this is PHP on Gentoo, add ‘fastcgi‘ to the USE flags.");271 } else if (WIFSIGNALED(status)) {272 log_error_write(srv, __FILE__, __LINE__, "sd",273 "terminated by signal:",274 WTERMSIG(status));275 276 if (WTERMSIG(status) == 11) {277 log_error_write(srv, __FILE__, __LINE__, "s",278 "to be exact: it segfaulted, crashed, died, ... you get the idea." );279 log_error_write(srv, __FILE__, __LINE__, "s",280 "If this is PHP, try removing the bytecode caches for now and try again.");281 }282 } else {283 log_error_write(srv, __FILE__, __LINE__, "sd",284 "child died somehow:",285 status);286 }287 return -1;288 }289 290 /* register process */291 proc->pid = child;292 proc->last_used = srv->cur_ts;293 proc->is_local = 1;294 295 break;296 }297 #endif298 } else {299 proc->is_local = 0;300 proc->pid = 0;301 302 if (p->conf.debug) {303 log_error_write(srv, __FILE__, __LINE__, "sb",304 "(debug) socket is already used; won‘t spawn:",305 proc->connection_name);306 }307 }308 309 proc->state = PROC_STATE_RUNNING;310 host->active_procs++;311 312 close(fcgi_fd);313 314 return 0;315 }

上面的流程是这样的

1根据配置文件判断用哪一种方式通讯,有tcp,有unix自身套接字(这个有个局限性,要cgi在同一台机器才能用的

2,连接服务端,如果失败的话则为服务端创建套接字

if(fcgi_fd != FCGI_LISTENSOCK_FILENO) {
close(FCGI_LISTENSOCK_FILENO);
dup2(fcgi_fd, FCGI_LISTENSOCK_FILENO);
close(fcgi_fd);
}

FCGI_LISTENSOCK_FILENO 这个是什么来的

根据fast-cgi协议是这样说的

当应用开始执行时,Web服务器留下一个打开的文件描述符,FCGI_LISTENSOCK_FILENO。该描述符引用Web服务器创建的一个正在监听的socket。

FCGI_LISTENSOCK_FILENO等于STDIN_FILENO。当应用开始执行时,标准的描述符STDOUT_FILENO和STDERR_FILENO被关闭。一个用于应用确定它是用CGI调用的还是用FastCGI调用的可靠方法是调用getpeername(FCGI_LISTENSOCK_FILENO),对于FastCGI应用,它返回-1,并设置errno为ENOTCONN。

Web服务器对于可靠传输的选择,Unix流式管道(AF_UNIX)或TCP/IP(AF_INET),是内含于FCGI_LISTENSOCK_FILENO socket的内部状态中的

呵呵

我们可以看到, 代码把fcgi_fd重定向到FCGI_LISTENSOCK_FILENO 这个身上,这个值为0,巧妙的地方就在这里了

我一直很奇怪在这里找不到accept,实在太笨啦,lighttpd相对于cgi只不过是一个客户端而已,不可能在lighttpd自己身上accept

所以唯一的办法就是把刚刚的套接字传递给cgi

 

为了证明这个东西,我们翻看了php-cgi的代码

 1 int fcgi_fd = 0; 2  3 fastcgi = fcgi_is_fastcgi(); 4     if (bindpath) { 5         fcgi_fd = fcgi_listen(bindpath, 128); 6         if (fcgi_fd < 0) { 7             fprintf(stderr, "Couldn‘t create FastCGI listen socket on port %s\n", bindpath); 8 #ifdef ZTS 9             tsrm_shutdown();10 #endif11             return FAILURE;12         }13         fastcgi = fcgi_is_fastcgi();14     }15     if (fastcgi) {16         /* How many times to run PHP scripts before dying */17         if (getenv("PHP_FCGI_MAX_REQUESTS")) {18             max_requests = atoi(getenv("PHP_FCGI_MAX_REQUESTS"));19             if (max_requests < 0) {20                 fprintf(stderr, "PHP_FCGI_MAX_REQUESTS is not valid\n");21                 return FAILURE;22             }23         }24 25         /* make php call us to get _ENV vars */26         php_php_import_environment_variables = php_import_environment_variables;27         php_import_environment_variables = cgi_php_import_environment_variables;28 29         /* library is already initialized, now init our request */30         request = fcgi_init_request(fcgi_fd);

 

ok,bindpath是何方神圣,通常我们是这样启动cgi的

php-cgi -b 127.0.0.1:9000,我们在上面根本没有传递这个参数给php-cgi,所以判断不成立

在看看fastcgi这个变量是怎样判断的,我们看看这个函数

1 int fcgi_is_fastcgi(void)2 {3     if (!is_initialized) {4         return fcgi_init();5     } else {6         return is_fastcgi;7     }8 }

 

 1 int fcgi_init(void) 2 { 3     if (!is_initialized) { 4 #ifndef _WIN32 5         sa_t sa; 6         socklen_t len = sizeof(sa); 7 #endif 8         zend_hash_init(&fcgi_mgmt_vars, 0, NULL, fcgi_free_mgmt_var_cb, 1); 9         fcgi_set_mgmt_var("FCGI_MPXS_CONNS", sizeof("FCGI_MPXS_CONNS")-1, "0", sizeof("0")-1);10 11         is_initialized = 1;12 #ifdef _WIN3213 # if 014         /* TODO: Support for TCP sockets */15         WSADATA wsaData;16 17         if (WSAStartup(MAKEWORD(2,0), &wsaData)) {18             fprintf(stderr, "Error starting Windows Sockets.  Error: %d", WSAGetLastError());19             return 0;20         }21 # endif22         if ((GetStdHandle(STD_OUTPUT_HANDLE) == INVALID_HANDLE_VALUE) &&23             (GetStdHandle(STD_ERROR_HANDLE)  == INVALID_HANDLE_VALUE) &&24             (GetStdHandle(STD_INPUT_HANDLE)  != INVALID_HANDLE_VALUE)) {25             char *str;26             DWORD pipe_mode = PIPE_READMODE_BYTE | PIPE_WAIT;27             HANDLE pipe = GetStdHandle(STD_INPUT_HANDLE);28 29             SetNamedPipeHandleState(pipe, &pipe_mode, NULL, NULL);30 31             str = getenv("_FCGI_SHUTDOWN_EVENT_");32             if (str != NULL) {33                 HANDLE shutdown_event = (HANDLE) atoi(str);34                 if (!CreateThread(NULL, 0, fcgi_shutdown_thread,35                                   shutdown_event, 0, NULL)) {36                     return -1;37                 }38             }39             str = getenv("_FCGI_MUTEX_");40             if (str != NULL) {41                 fcgi_accept_mutex = (HANDLE) atoi(str);42             }43             return is_fastcgi = 1;44         } else {45             return is_fastcgi = 0;46         }47 #else48         errno = 0;49         if (getpeername(0, (struct sockaddr *)&sa, &len) != 0 && errno == ENOTCONN) {50             fcgi_setup_signals();51             return is_fastcgi = 1;52         } else {53             return is_fastcgi = 0;54         }55 #endif56     }57     return is_fastcgi;58 }

呵呵,注意49行用getpeername这个函数,传进去的是套接字是0,还记得我们刚才执行cgi的时候dup2吗,呵呵巧妙就在这里

也就是说如果我们fastcgi的话就直接采用fcgi_fd=0 这个套接字,谜底解开了吧

 

我们再次看看php-cgi是怎样accept的

 

  1 #ifndef PHP_WIN32  2     /* Pre-fork, if required */  3     if (getenv("PHP_FCGI_CHILDREN")) {  4         char * children_str = getenv("PHP_FCGI_CHILDREN");  5         children = atoi(children_str);  6         if (children < 0) {  7             fprintf(stderr, "PHP_FCGI_CHILDREN is not valid\n");  8             return FAILURE;  9         } 10         fcgi_set_mgmt_var("FCGI_MAX_CONNS", sizeof("FCGI_MAX_CONNS")-1, children_str, strlen(children_str)); 11         /* This is the number of concurrent requests, equals FCGI_MAX_CONNS */ 12         fcgi_set_mgmt_var("FCGI_MAX_REQS",  sizeof("FCGI_MAX_REQS")-1,  children_str, strlen(children_str)); 13     } else { 14         fcgi_set_mgmt_var("FCGI_MAX_CONNS", sizeof("FCGI_MAX_CONNS")-1, "1", sizeof("1")-1); 15         fcgi_set_mgmt_var("FCGI_MAX_REQS",  sizeof("FCGI_MAX_REQS")-1,  "1", sizeof("1")-1); 16     } 17  18     if (children) { 19         int running = 0; 20         pid_t pid; 21  22         /* Create a process group for ourself & children */ 23         setsid(); 24         pgroup = getpgrp(); 25 #ifdef DEBUG_FASTCGI 26         fprintf(stderr, "Process group %d\n", pgroup); 27 #endif 28  29         /* Set up handler to kill children upon exit */ 30         act.sa_flags = 0; 31         act.sa_handler = fastcgi_cleanup; 32         if (sigaction(SIGTERM, &act, &old_term) || 33             sigaction(SIGINT,  &act, &old_int)  || 34             sigaction(SIGQUIT, &act, &old_quit) 35         ) { 36             perror("Can‘t set signals"); 37             exit(1); 38         } 39  40         if (fcgi_in_shutdown()) { 41             goto parent_out; 42         } 43  44         while (parent) { 45             do { 46 #ifdef DEBUG_FASTCGI 47                 fprintf(stderr, "Forking, %d running\n", running); 48 #endif 49                 pid = fork(); 50                 switch (pid) { 51                 case 0: 52                     /* One of the children. 53                      * Make sure we don‘t go round the 54                      * fork loop any more 55                      */ 56                     parent = 0; 57  58                     /* don‘t catch our signals */ 59                     sigaction(SIGTERM, &old_term, 0); 60                     sigaction(SIGQUIT, &old_quit, 0); 61                     sigaction(SIGINT,  &old_int,  0); 62                     break; 63                 case -1: 64                     perror("php (pre-forking)"); 65                     exit(1); 66                     break; 67                 default: 68                     /* Fine */ 69                     running++; 70                     break; 71                 } 72             } while (parent && (running < children)); 73  74             if (parent) { 75 #ifdef DEBUG_FASTCGI 76                 fprintf(stderr, "Wait for kids, pid %d\n", getpid()); 77 #endif 78                 parent_waiting = 1; 79                 while (1) { 80                     if (wait(&status) >= 0) { 81                         running--; 82                         break; 83                     } else if (exit_signal) { 84                         break; 85                     } 86                 } 87                 if (exit_signal) { 88 #if 0 89                     while (running > 0) { 90                         while (wait(&status) < 0) { 91                         } 92                         running--; 93                     } 94 #endif 95                     goto parent_out; 96                 } 97             } 98         } 99     } else {100         parent = 0;101     }102 103 #endif /* WIN32 */104     }

呵呵cgi会先根据获取回来的 PHP_FCGI_CHILDREN 的设置去生成子进程,

/* not found, add a default */
if (i == env.used) {
env_add(&env, CONST_STR_LEN("PHP_FCGI_CHILDREN"), CONST_STR_LEN("1"));
}

lighttpd会默认设置一个啦

所谓的pre-fork模式啦

关于上次怎样区别父进程执行那些代码,子进程执行那些代码相信读者会很容易发现

父进程进行wait,然后做一个管理子进程的多小

子进程就开始accept啦

 

  1         while (!fastcgi || fcgi_accept_request(request) >= 0) {  2             SG(server_context) = fastcgi ? (void *) request : (void *) 1;  3             init_request_info(request TSRMLS_CC);  4             CG(interactive) = 0;  5   6             if (!cgi && !fastcgi) {  7                 while ((c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) {  8                     switch (c) {  9  10                         case a:    /* interactive mode */ 11                             printf("Interactive mode enabled\n\n"); 12                             CG(interactive) = 1; 13                             break; 14  15                         case C: /* don‘t chdir to the script directory */ 16                             SG(options) |= SAPI_OPTION_NO_CHDIR; 17                             break; 18  19                         case e: /* enable extended info output */ 20                             CG(compiler_options) |= ZEND_COMPILE_EXTENDED_INFO; 21                             break; 22  23                         case f: /* parse file */ 24                             if (script_file) { 25                                 efree(script_file); 26                             } 27                             script_file = estrdup(php_optarg); 28                             no_headers = 1; 29                             break; 30  31                         case i: /* php info & quit */ 32                             if (script_file) { 33                                 efree(script_file); 34                             } 35                             if (php_request_startup(TSRMLS_C) == FAILURE) { 36                                 SG(server_context) = NULL; 37                                 php_module_shutdown(TSRMLS_C); 38                                 return FAILURE; 39                             } 40                             if (no_headers) { 41                                 SG(headers_sent) = 1; 42                                 SG(request_info).no_headers = 1; 43                             } 44                             php_print_info(0xFFFFFFFF TSRMLS_CC); 45                             php_request_shutdown((void *) 0); 46                             fcgi_shutdown(); 47                             exit_status = 0; 48                             goto out; 49  50                         case l: /* syntax check mode */ 51                             no_headers = 1; 52                             behavior = PHP_MODE_LINT; 53                             break; 54  55                         case m: /* list compiled in modules */ 56                             if (script_file) { 57                                 efree(script_file); 58                             } 59                             SG(headers_sent) = 1; 60                             php_printf("[PHP Modules]\n"); 61                             print_modules(TSRMLS_C); 62                             php_printf("\n[Zend Modules]\n"); 63                             print_extensions(TSRMLS_C); 64                             php_printf("\n"); 65                             php_output_end_all(TSRMLS_C); 66                             fcgi_shutdown(); 67                             exit_status = 0; 68                             goto out; 69  70 #if 0 /* not yet operational, see also below ... */ 71                         case ‘‘: /* generate indented source mode*/ 72                             behavior=PHP_MODE_INDENT; 73                             break; 74 #endif 75  76                         case q: /* do not generate HTTP headers */ 77                             no_headers = 1; 78                             break; 79  80                         case v: /* show php version & quit */ 81                             if (script_file) { 82                                 efree(script_file); 83                             } 84                             no_headers = 1; 85                             if (php_request_startup(TSRMLS_C) == FAILURE) { 86                                 SG(server_context) = NULL; 87                                 php_module_shutdown(TSRMLS_C); 88                                 return FAILURE; 89                             } 90                             if (no_headers) { 91                                 SG(headers_sent) = 1; 92                                 SG(request_info).no_headers = 1; 93                             } 94 #if ZEND_DEBUG 95                             php_printf("PHP %s (%s) (built: %s %s) (DEBUG)\nCopyright (c) 1997-2013 The PHP Group\n%s", PHP_VERSION, sapi_module.name, __DATE__, __TIME__, get_zend_version()); 96 #else 97                             php_printf("PHP %s (%s) (built: %s %s)\nCopyright (c) 1997-2013 The PHP Group\n%s", PHP_VERSION, sapi_module.name, __DATE__, __TIME__, get_zend_version()); 98 #endif 99                             php_request_shutdown((void *) 0);100                             fcgi_shutdown();101                             exit_status = 0;102                             goto out;103 104                         case w:105                             behavior = PHP_MODE_STRIP;106                             break;107 108                         case z: /* load extension file */109                             zend_load_extension(php_optarg);110                             break;111 112                         default:113                             break;114                     }115                 }116 117                 if (script_file) {118                     /* override path_translated if -f on command line */119                     STR_FREE(SG(request_info).path_translated);120                     SG(request_info).path_translated = script_file;121                     /* before registering argv to module exchange the *new* argv[0] */122                     /* we can achieve this without allocating more memory */123                     SG(request_info).argc = argc - (php_optind - 1);124                     SG(request_info).argv = &argv[php_optind - 1];125                     SG(request_info).argv[0] = script_file;126                 } else if (argc > php_optind) {127                     /* file is on command line, but not in -f opt */128                     STR_FREE(SG(request_info).path_translated);129                     SG(request_info).path_translated = estrdup(argv[php_optind]);130                     /* arguments after the file are considered script args */131                     SG(request_info).argc = argc - php_optind;132                     SG(request_info).argv = &argv[php_optind];133                 }134 135                 if (no_headers) {136                     SG(headers_sent) = 1;137                     SG(request_info).no_headers = 1;138                 }139 140                 /* all remaining arguments are part of the query string141                  * this section of code concatenates all remaining arguments142                  * into a single string, seperating args with a &143                  * this allows command lines like:144                  *145                  *  test.php v1=test v2=hello+world!146                  *  test.php "v1=test&v2=hello world!"147                  *  test.php v1=test "v2=hello world!"148                 */149                 if (!SG(request_info).query_string && argc > php_optind) {150                     int slen = strlen(PG(arg_separator).input);151                     len = 0;152                     for (i = php_optind; i < argc; i++) {153                         if (i < (argc - 1)) {154                             len += strlen(argv[i]) + slen;155                         } else {156                             len += strlen(argv[i]);157                         }158                     }159 160                     len += 2;161                     s = malloc(len);162                     *s = \0;            /* we are pretending it came from the environment  */163                     for (i = php_optind; i < argc; i++) {164                         strlcat(s, argv[i], len);165                         if (i < (argc - 1)) {166                             strlcat(s, PG(arg_separator).input, len);167                         }168                     }169                     SG(request_info).query_string = s;170                     free_query_string = 1;171                 }172             } /* end !cgi && !fastcgi */173 174             /*175                 we never take stdin if we‘re (f)cgi, always176                 rely on the web server giving us the info177                 we need in the environment.178             */179             if (SG(request_info).path_translated || cgi || fastcgi) {180                 file_handle.type = ZEND_HANDLE_FILENAME;181                 file_handle.filename = SG(request_info).path_translated;182                 file_handle.handle.fp = NULL;183             } else {184                 file_handle.filename = "-";185                 file_handle.type = ZEND_HANDLE_FP;186                 file_handle.handle.fp = stdin;187             }188 189             file_handle.opened_path = NULL;190             file_handle.free_filename = 0;191 192             /* request startup only after we‘ve done all we can to193              * get path_translated */194             if (php_request_startup(TSRMLS_C) == FAILURE) {195                 if (fastcgi) {196                     fcgi_finish_request(request, 1);197                 }198                 SG(server_context) = NULL;199                 php_module_shutdown(TSRMLS_C);200                 return FAILURE;201             }202             if (no_headers) {203                 SG(headers_sent) = 1;204                 SG(request_info).no_headers = 1;205             }206 207             /*208                 at this point path_translated will be set if:209                 1. we are running from shell and got filename was there210                 2. we are running as cgi or fastcgi211             */212             if (cgi || fastcgi || SG(request_info).path_translated) {213                 if (php_fopen_primary_script(&file_handle TSRMLS_CC) == FAILURE) {214                     zend_try {215                         if (errno == EACCES) {216                             SG(sapi_headers).http_response_code = 403;217                             PUTS("Access denied.\n");218                         } else {219                             SG(sapi_headers).http_response_code = 404;220                             PUTS("No input file specified.\n");221                         }222                     } zend_catch {223                     } zend_end_try();224                     /* we want to serve more requests if this is fastcgi225                      * so cleanup and continue, request shutdown is226                      * handled later */227                     if (fastcgi) {228                         goto fastcgi_request_done;229                     }230 231                     STR_FREE(SG(request_info).path_translated);232 233                     if (free_query_string && SG(request_info).query_string) {234                         free(SG(request_info).query_string);235                         SG(request_info).query_string = NULL;236                     }237 238                     php_request_shutdown((void *) 0);239                     SG(server_context) = NULL;240                     php_module_shutdown(TSRMLS_C);241                     sapi_shutdown();242 #ifdef ZTS243                     tsrm_shutdown();244 #endif245                     return FAILURE;246                 }247             }248 249             if (CGIG(check_shebang_line)) {250                 /* #!php support */251                 switch (file_handle.type) {252                     case ZEND_HANDLE_FD:253                         if (file_handle.handle.fd < 0) {254                             break;255                         }256                         file_handle.type = ZEND_HANDLE_FP;257                         file_handle.handle.fp = fdopen(file_handle.handle.fd, "rb");258                         /* break missing intentionally */259                     case ZEND_HANDLE_FP:260                         if (!file_handle.handle.fp ||261                             (file_handle.handle.fp == stdin)) {262                             break;263                         }264                         c = fgetc(file_handle.handle.fp);265                         if (c == #) {266                             while (c != \n && c != \r && c != EOF) {267                                 c = fgetc(file_handle.handle.fp);    /* skip to end of line */268                             }269                             /* handle situations where line is terminated by \r\n */270                             if (c == \r) {271                                 if (fgetc(file_handle.handle.fp) != \n) {272                                     long pos = ftell(file_handle.handle.fp);273                                     fseek(file_handle.handle.fp, pos - 1, SEEK_SET);274                                 }275                             }276                             CG(start_lineno) = 2;277                         } else {278                             rewind(file_handle.handle.fp);279                         }280                         break;281                     case ZEND_HANDLE_STREAM:282                         c = php_stream_getc((php_stream*)file_handle.handle.stream.handle);283                         if (c == #) {284                             while (c != \n && c != \r && c != EOF) {285                                 c = php_stream_getc((php_stream*)file_handle.handle.stream.handle);    /* skip to end of line */286                             }287                             /* handle situations where line is terminated by \r\n */288                             if (c == \r) {289                                 if (php_stream_getc((php_stream*)file_handle.handle.stream.handle) != \n) {290                                     long pos = php_stream_tell((php_stream*)file_handle.handle.stream.handle);291                                     php_stream_seek((php_stream*)file_handle.handle.stream.handle, pos - 1, SEEK_SET);292                                 }293                             }294                             CG(start_lineno) = 2;295                         } else {296                             php_stream_rewind((php_stream*)file_handle.handle.stream.handle);297                         }298                         break;299                     case ZEND_HANDLE_MAPPED:300                         if (file_handle.handle.stream.mmap.buf[0] == #) {301                             int i = 1;302 303                             c = file_handle.handle.stream.mmap.buf[i++];304                             while (c != \n && c != \r && c != EOF) {305                                 c = file_handle.handle.stream.mmap.buf[i++];306                             }307                             if (c == \r) {308                                 if (file_handle.handle.stream.mmap.buf[i] == \n) {309                                     i++;310                                 }311                             }312                             file_handle.handle.stream.mmap.buf += i;313                             file_handle.handle.stream.mmap.len -= i;314                         }315                         break;316                     default:317                         break;318                 }319             }320 321             switch (behavior) {322                 case PHP_MODE_STANDARD:323                     php_execute_script(&file_handle TSRMLS_CC);324                     break;325                 case PHP_MODE_LINT:326                     PG(during_request_startup) = 0;327                     exit_status = php_lint_script(&file_handle TSRMLS_CC);328                     if (exit_status == SUCCESS) {329                         zend_printf("No syntax errors detected in %s\n", file_handle.filename);330                     } else {331                         zend_printf("Errors parsing %s\n", file_handle.filename);332                     }333                     break;334                 case PHP_MODE_STRIP:335                     if (open_file_for_scanning(&file_handle TSRMLS_CC) == SUCCESS) {336                         zend_strip(TSRMLS_C);337                         zend_file_handle_dtor(&file_handle TSRMLS_CC);338                         php_output_teardown();339                     }340                     return SUCCESS;341                     break;342                 case PHP_MODE_HIGHLIGHT:343                     {344                         zend_syntax_highlighter_ini syntax_highlighter_ini;345 346                         if (open_file_for_scanning(&file_handle TSRMLS_CC) == SUCCESS) {347                             php_get_highlight_struct(&syntax_highlighter_ini);348                             zend_highlight(&syntax_highlighter_ini TSRMLS_CC);349                             if (fastcgi) {350                                 goto fastcgi_request_done;351                             }352                             zend_file_handle_dtor(&file_handle TSRMLS_CC);353                             php_output_teardown();354                         }355                         return SUCCESS;356                     }357                     break;358 #if 0359                 /* Zeev might want to do something with this one day */360                 case PHP_MODE_INDENT:361                     open_file_for_scanning(&file_handle TSRMLS_CC);362                     zend_indent();363                     zend_file_handle_dtor(&file_handle TSRMLS_CC);364                     php_output_teardown();365                     return SUCCESS;366                     break;367 #endif368             }369 370 fastcgi_request_done:371             {372                 STR_FREE(SG(request_info).path_translated);373 374                 php_request_shutdown((void *) 0);375 376                 if (exit_status == 0) {377                     exit_status = EG(exit_status);378                 }379 380                 if (free_query_string && SG(request_info).query_string) {381                     free(SG(request_info).query_string);382                     SG(request_info).query_string = NULL;383                 }384             }385 386             if (!fastcgi) {387                 if (benchmark) {388                     repeats--;389                     if (repeats > 0) {390                         script_file = NULL;391                         php_optind = orig_optind;392                         php_optarg = orig_optarg;393                         continue;394                     }395                 }396                 break;397             }398 399             /* only fastcgi will get here */400             requests++;401             if (max_requests && (requests == max_requests)) {402                 fcgi_finish_request(request, 1);403                 if (bindpath) {404                     free(bindpath);405                 }406                 if (max_requests != 1) {407                     /* no need to return exit_status of the last request */408                     exit_status = 0;409                 }410                 break;411             }412             /* end of fastcgi loop */413         }

 

 

最终会通过 php_execute_script(&file_handle TSRMLS_CC);  这个函数执行php脚本,到了这里我们可以回答我们上面的那个问题

 

num-procs = max-procs * ( 1 + PHP_FCGI_CHILDREN )  = 8个

本文玩,大家有什么疑问可以留言啊

 参考文章  http://www.360doc.com/content/12/1027/22/834950_244161021.shtml

                http://www.cppblog.com/woaidongmao/archive/2011/06/21/149101.html   fast-cgi协议

                 http://blog.csdn.net/springfieldking/article/details/8204210   fast-cgi工作原理

 

lighttpd+fastcgi模块分析