一开始不怎么明白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 #连接socket 103 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 #监听socket 138 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 #endif 298 } 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 #endif 11 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 _WIN32 13 # if 0 14 /* 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 # endif 22 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 #else 48 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 #endif 56 } 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 string 141 * this section of code concatenates all remaining arguments 142 * 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, always 176 rely on the web server giving us the info 177 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 to 193 * 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 there 210 2. we are running as cgi or fastcgi 211 */ 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 fastcgi 225 * so cleanup and continue, request shutdown is 226 * 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 ZTS 243 tsrm_shutdown(); 244 #endif 245 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 0 359 /* 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 #endif 368 } 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工作原理