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 = ; 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 = ;
host->min_procs = ;
host->max_procs = ;
host->max_load_per_proc = ;
host->idle_timeout = ;
host->mode = FCGI_RESPONDER;
host->disable_time = ;
host->break_scriptfilename_for_php = ;
host->allow_xsendfile = ; /* handle X-LIGHTTPD-send-file */
host->kill_signal = SIGTERM;
host->fix_root_path_name = ; fcv[].destination = host->host;
fcv[].destination = host->docroot;
fcv[].destination = fcgi_mode;
fcv[].destination = host->unixsocket;
fcv[].destination = host->bin_path; fcv[].destination = &(host->check_local);
fcv[].destination = &(host->port);
fcv[].destination = &(host->min_procs);
fcv[].destination = &(host->max_procs);
fcv[].destination = &(host->max_load_per_proc);
fcv[].destination = &(host->idle_timeout);
fcv[].destination = &(host->disable_time); fcv[].destination = host->bin_env;
fcv[].destination = host->bin_env_copy;
fcv[].destination = &(host->break_scriptfilename_for_php);
fcv[].destination = &(host->allow_xsendfile);
fcv[].destination = host->strip_request_uri;
fcv[].destination = &(host->kill_signal);
fcv[].destination = &(host->fix_root_path_name);
#进行配置文件替换
if ( != 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) - ) {
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 == ) {
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 < ) host->max_load_per_proc = ; 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 = ; 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 = ;
host->max_procs = ;
} if (!buffer_is_empty(fcgi_mode)) {
if (strcmp(fcgi_mode->ptr, "responder") == ) {
host->mode = FCGI_RESPONDER;
} else if (strcmp(fcgi_mode->ptr, "authorizer") == ) {
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 这个函数
我们开始分析这个函数
static int fcgi_spawn_connection(server *srv,
plugin_data *p,
fcgi_extension_host *host,
fcgi_proc *proc) {
int fcgi_fd;
int socket_type, status;
struct timeval tv = { , * };
#ifdef HAVE_SYS_UN_H
struct sockaddr_un fcgi_addr_un;
#endif
struct sockaddr_in fcgi_addr_in;
struct sockaddr *fcgi_addr; socklen_t servlen; #ifndef HAVE_FORK
return -;
#endif if (p->conf.debug) {
log_error_write(srv, __FILE__, __LINE__, "sdb",
"new proc, socket:", proc->port, proc->unixsocket);
}
#如果unixsocket套接字不为空,也就是有设置啦
if (!buffer_is_empty(proc->unixsocket)) {
memset(&fcgi_addr, , sizeof(fcgi_addr)); #ifdef HAVE_SYS_UN_H
fcgi_addr_un.sun_family = AF_UNIX;
strcpy(fcgi_addr_un.sun_path, proc->unixsocket->ptr); #ifdef SUN_LEN
servlen = SUN_LEN(&fcgi_addr_un);
#else
/* stevens says: */
servlen = proc->unixsocket->used + sizeof(fcgi_addr_un.sun_family);
#endif
socket_type = AF_UNIX;
fcgi_addr = (struct sockaddr *) &fcgi_addr_un; buffer_copy_string_len(proc->connection_name, CONST_STR_LEN("unix:"));
buffer_append_string_buffer(proc->connection_name, proc->unixsocket); #else
log_error_write(srv, __FILE__, __LINE__, "s",
"ERROR: Unix Domain sockets are not supported.");
return -;
#endif
} else { #这里就是走tcp模式
fcgi_addr_in.sin_family = AF_INET; if (buffer_is_empty(host->host)) {
fcgi_addr_in.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
} else {
struct hostent *he; /* set a useful default */
fcgi_addr_in.sin_addr.s_addr = htonl(INADDR_LOOPBACK); #获取主机的真实地址
if (NULL == (he = gethostbyname(host->host->ptr))) {
log_error_write(srv, __FILE__, __LINE__,
"sdb", "gethostbyname failed: ",
h_errno, host->host);
return -;
} if (he->h_addrtype != AF_INET) {
log_error_write(srv, __FILE__, __LINE__, "sd", "addr-type != AF_INET: ", he->h_addrtype);
return -;
} if (he->h_length != sizeof(struct in_addr)) {
log_error_write(srv, __FILE__, __LINE__, "sd", "addr-length != sizeof(in_addr): ", he->h_length);
return -;
} memcpy(&(fcgi_addr_in.sin_addr.s_addr), he->h_addr_list[], he->h_length); }
#绑定端口
fcgi_addr_in.sin_port = htons(proc->port);
servlen = sizeof(fcgi_addr_in); socket_type = AF_INET;
fcgi_addr = (struct sockaddr *) &fcgi_addr_in; buffer_copy_string_len(proc->connection_name, CONST_STR_LEN("tcp:"));
if (!buffer_is_empty(host->host)) {
buffer_append_string_buffer(proc->connection_name, host->host);
} else {
buffer_append_string_len(proc->connection_name, CONST_STR_LEN("localhost"));
}
buffer_append_string_len(proc->connection_name, CONST_STR_LEN(":"));
buffer_append_long(proc->connection_name, proc->port);
}
#建立socket
if (- == (fcgi_fd = socket(socket_type, SOCK_STREAM, ))) {
log_error_write(srv, __FILE__, __LINE__, "ss",
"failed:", strerror(errno));
return -;
}
#连接socket
if (- == connect(fcgi_fd, fcgi_addr, servlen)) {
/* server is not up, spawn it 连接失败,则说明服务器还没有起来 */
pid_t child;
int val; if (errno != ENOENT &&
!buffer_is_empty(proc->unixsocket)) {
unlink(proc->unixsocket->ptr);
} close(fcgi_fd); /* reopen socket 重新打开socket */
if (- == (fcgi_fd = socket(socket_type, SOCK_STREAM, ))) {
log_error_write(srv, __FILE__, __LINE__, "ss",
"socket failed:", strerror(errno));
return -;
} val = ;
if (setsockopt(fcgi_fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < ) {
log_error_write(srv, __FILE__, __LINE__, "ss",
"socketsockopt failed:", strerror(errno));
return -;
} /* create socket */
if (- == bind(fcgi_fd, fcgi_addr, servlen)) {
log_error_write(srv, __FILE__, __LINE__, "sbs",
"bind failed for:",
proc->connection_name,
strerror(errno));
return -;
}
#监听socket
if (- == listen(fcgi_fd, )) {
log_error_write(srv, __FILE__, __LINE__, "ss",
"listen failed:", strerror(errno));
return -;
} #ifdef HAVE_FORK 开始生产进程
switch ((child = fork())) {
case : {
size_t i = ;
char *c;
char_array env;
char_array arg; /* create environment */
env.ptr = NULL;
env.size = ;
env.used = ; arg.ptr = NULL;
arg.size = ;
arg.used = ; if(fcgi_fd != FCGI_LISTENSOCK_FILENO) {
close(FCGI_LISTENSOCK_FILENO);
dup2(fcgi_fd, FCGI_LISTENSOCK_FILENO);
close(fcgi_fd);
} openDevNull(STDERR_FILENO); /* we don't need the client socket */
for (i = ; i < ; i++) {
close(i);
} /* build clean environment */
if (host->bin_env_copy->used) {
for (i = ; i < host->bin_env_copy->used; i++) {
data_string *ds = (data_string *)host->bin_env_copy->data[i];
char *ge; if (NULL != (ge = getenv(ds->value->ptr))) {
env_add(&env, CONST_BUF_LEN(ds->value), ge, strlen(ge));
}
}
} else {
for (i = ; environ[i]; i++) {
char *eq; if (NULL != (eq = strchr(environ[i], '='))) {
env_add(&env, environ[i], eq - environ[i], eq+, strlen(eq+));
}
}
} /* create environment */
for (i = ; i < host->bin_env->used; i++) {
data_string *ds = (data_string *)host->bin_env->data[i]; env_add(&env, CONST_BUF_LEN(ds->key), CONST_BUF_LEN(ds->value));
} for (i = ; i < env.used; i++) {
/* search for PHP_FCGI_CHILDREN */
if ( == strncmp(env.ptr[i], "PHP_FCGI_CHILDREN=", sizeof("PHP_FCGI_CHILDREN=") - )) break;
} /* not found, add a default */
if (i == env.used) {
env_add(&env, CONST_STR_LEN("PHP_FCGI_CHILDREN"), CONST_STR_LEN(""));
} env.ptr[env.used] = NULL;
#处理cgi程序名字
parse_binpath(&arg, host->bin_path); /* chdir into the base of the bin-path,
* search for the last / */
if (NULL != (c = strrchr(arg.ptr[], '/'))) {
*c = '\0'; /* change to the physical directory */
if (- == chdir(arg.ptr[])) {
*c = '/';
log_error_write(srv, __FILE__, __LINE__, "sss", "chdir failed:", strerror(errno), arg.ptr[]);
}
*c = '/';
} /* exec the cgi 开始执行cgi */
execve(arg.ptr[], arg.ptr, env.ptr); /* log_error_write(srv, __FILE__, __LINE__, "sbs",
"execve failed for:", host->bin_path, strerror(errno)); */ exit(errno); break;
}
case -:
/* error */
break;
default:
/* father */ /* wait */
select(, NULL, NULL, NULL, &tv); switch (waitpid(child, &status, WNOHANG)) {
case :
/* child still running after timeout, good */
break;
case -:
/* no PID found ? should never happen */
log_error_write(srv, __FILE__, __LINE__, "ss",
"pid not found:", strerror(errno));
return -;
default:
log_error_write(srv, __FILE__, __LINE__, "sbs",
"the fastcgi-backend", host->bin_path, "failed to start:");
/* the child should not terminate at all */
if (WIFEXITED(status)) {
log_error_write(srv, __FILE__, __LINE__, "sdb",
"child exited with status",
WEXITSTATUS(status), host->bin_path);
log_error_write(srv, __FILE__, __LINE__, "s",
"If you're trying to run PHP as a FastCGI backend, make sure you're using the FastCGI-enabled version.\n"
"You can find out if it is the right one by executing 'php -v' and it should display '(cgi-fcgi)' "
"in the output, NOT '(cgi)' NOR '(cli)'.\n"
"For more information, check http://trac.lighttpd.net/trac/wiki/Docs%3AModFastCGI#preparing-php-as-a-fastcgi-program"
"If this is PHP on Gentoo, add 'fastcgi' to the USE flags.");
} else if (WIFSIGNALED(status)) {
log_error_write(srv, __FILE__, __LINE__, "sd",
"terminated by signal:",
WTERMSIG(status)); if (WTERMSIG(status) == ) {
log_error_write(srv, __FILE__, __LINE__, "s",
"to be exact: it segfaulted, crashed, died, ... you get the idea." );
log_error_write(srv, __FILE__, __LINE__, "s",
"If this is PHP, try removing the bytecode caches for now and try again.");
}
} else {
log_error_write(srv, __FILE__, __LINE__, "sd",
"child died somehow:",
status);
}
return -;
} /* register process */
proc->pid = child;
proc->last_used = srv->cur_ts;
proc->is_local = ; break;
}
#endif
} else {
proc->is_local = ;
proc->pid = ; if (p->conf.debug) {
log_error_write(srv, __FILE__, __LINE__, "sb",
"(debug) socket is already used; won't spawn:",
proc->connection_name);
}
} proc->state = PROC_STATE_RUNNING;
host->active_procs++; close(fcgi_fd); return ;
}
上面的流程是这样的
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的代码
int fcgi_fd = ; fastcgi = fcgi_is_fastcgi();
if (bindpath) {
fcgi_fd = fcgi_listen(bindpath, );
if (fcgi_fd < ) {
fprintf(stderr, "Couldn't create FastCGI listen socket on port %s\n", bindpath);
#ifdef ZTS
tsrm_shutdown();
#endif
return FAILURE;
}
fastcgi = fcgi_is_fastcgi();
}
if (fastcgi) {
/* How many times to run PHP scripts before dying */
if (getenv("PHP_FCGI_MAX_REQUESTS")) {
max_requests = atoi(getenv("PHP_FCGI_MAX_REQUESTS"));
if (max_requests < ) {
fprintf(stderr, "PHP_FCGI_MAX_REQUESTS is not valid\n");
return FAILURE;
}
} /* make php call us to get _ENV vars */
php_php_import_environment_variables = php_import_environment_variables;
php_import_environment_variables = cgi_php_import_environment_variables; /* library is already initialized, now init our request */
request = fcgi_init_request(fcgi_fd);
ok,bindpath是何方神圣,通常我们是这样启动cgi的
php-cgi -b 127.0.0.1:9000,我们在上面根本没有传递这个参数给php-cgi,所以判断不成立
在看看fastcgi这个变量是怎样判断的,我们看看这个函数
int fcgi_is_fastcgi(void)
{
if (!is_initialized) {
return fcgi_init();
} else {
return is_fastcgi;
}
}
int fcgi_init(void)
{
if (!is_initialized) {
#ifndef _WIN32
sa_t sa;
socklen_t len = sizeof(sa);
#endif
zend_hash_init(&fcgi_mgmt_vars, , NULL, fcgi_free_mgmt_var_cb, );
fcgi_set_mgmt_var("FCGI_MPXS_CONNS", sizeof("FCGI_MPXS_CONNS")-, "", sizeof("")-); is_initialized = ;
#ifdef _WIN32
# if
/* TODO: Support for TCP sockets */
WSADATA wsaData; if (WSAStartup(MAKEWORD(,), &wsaData)) {
fprintf(stderr, "Error starting Windows Sockets. Error: %d", WSAGetLastError());
return ;
}
# endif
if ((GetStdHandle(STD_OUTPUT_HANDLE) == INVALID_HANDLE_VALUE) &&
(GetStdHandle(STD_ERROR_HANDLE) == INVALID_HANDLE_VALUE) &&
(GetStdHandle(STD_INPUT_HANDLE) != INVALID_HANDLE_VALUE)) {
char *str;
DWORD pipe_mode = PIPE_READMODE_BYTE | PIPE_WAIT;
HANDLE pipe = GetStdHandle(STD_INPUT_HANDLE); SetNamedPipeHandleState(pipe, &pipe_mode, NULL, NULL); str = getenv("_FCGI_SHUTDOWN_EVENT_");
if (str != NULL) {
HANDLE shutdown_event = (HANDLE) atoi(str);
if (!CreateThread(NULL, , fcgi_shutdown_thread,
shutdown_event, , NULL)) {
return -;
}
}
str = getenv("_FCGI_MUTEX_");
if (str != NULL) {
fcgi_accept_mutex = (HANDLE) atoi(str);
}
return is_fastcgi = ;
} else {
return is_fastcgi = ;
}
#else
errno = ;
if (getpeername(, (struct sockaddr *)&sa, &len) != && errno == ENOTCONN) {
fcgi_setup_signals();
return is_fastcgi = ;
} else {
return is_fastcgi = ;
}
#endif
}
return is_fastcgi;
}
呵呵,注意49行用getpeername这个函数,传进去的是套接字是0,还记得我们刚才执行cgi的时候dup2吗,呵呵巧妙就在这里
也就是说如果我们fastcgi的话就直接采用fcgi_fd=0 这个套接字,谜底解开了吧
我们再次看看php-cgi是怎样accept的
#ifndef PHP_WIN32
/* Pre-fork, if required */
if (getenv("PHP_FCGI_CHILDREN")) {
char * children_str = getenv("PHP_FCGI_CHILDREN");
children = atoi(children_str);
if (children < ) {
fprintf(stderr, "PHP_FCGI_CHILDREN is not valid\n");
return FAILURE;
}
fcgi_set_mgmt_var("FCGI_MAX_CONNS", sizeof("FCGI_MAX_CONNS")-, children_str, strlen(children_str));
/* This is the number of concurrent requests, equals FCGI_MAX_CONNS */
fcgi_set_mgmt_var("FCGI_MAX_REQS", sizeof("FCGI_MAX_REQS")-, children_str, strlen(children_str));
} else {
fcgi_set_mgmt_var("FCGI_MAX_CONNS", sizeof("FCGI_MAX_CONNS")-, "", sizeof("")-);
fcgi_set_mgmt_var("FCGI_MAX_REQS", sizeof("FCGI_MAX_REQS")-, "", sizeof("")-);
} if (children) {
int running = ;
pid_t pid; /* Create a process group for ourself & children */
setsid();
pgroup = getpgrp();
#ifdef DEBUG_FASTCGI
fprintf(stderr, "Process group %d\n", pgroup);
#endif /* Set up handler to kill children upon exit */
act.sa_flags = ;
act.sa_handler = fastcgi_cleanup;
if (sigaction(SIGTERM, &act, &old_term) ||
sigaction(SIGINT, &act, &old_int) ||
sigaction(SIGQUIT, &act, &old_quit)
) {
perror("Can't set signals");
exit();
} if (fcgi_in_shutdown()) {
goto parent_out;
} while (parent) {
do {
#ifdef DEBUG_FASTCGI
fprintf(stderr, "Forking, %d running\n", running);
#endif
pid = fork();
switch (pid) {
case :
/* One of the children.
* Make sure we don't go round the
* fork loop any more
*/
parent = ; /* don't catch our signals */
sigaction(SIGTERM, &old_term, );
sigaction(SIGQUIT, &old_quit, );
sigaction(SIGINT, &old_int, );
break;
case -:
perror("php (pre-forking)");
exit();
break;
default:
/* Fine */
running++;
break;
}
} while (parent && (running < children)); if (parent) {
#ifdef DEBUG_FASTCGI
fprintf(stderr, "Wait for kids, pid %d\n", getpid());
#endif
parent_waiting = ;
while () {
if (wait(&status) >= ) {
running--;
break;
} else if (exit_signal) {
break;
}
}
if (exit_signal) {
#if 0
while (running > ) {
while (wait(&status) < ) {
}
running--;
}
#endif
goto parent_out;
}
}
}
} else {
parent = ;
} #endif /* WIN32 */
}
呵呵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啦
while (!fastcgi || fcgi_accept_request(request) >= ) {
SG(server_context) = fastcgi ? (void *) request : (void *) ;
init_request_info(request TSRMLS_CC);
CG(interactive) = ; if (!cgi && !fastcgi) {
while ((c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, , )) != -) {
switch (c) { case 'a': /* interactive mode */
printf("Interactive mode enabled\n\n");
CG(interactive) = ;
break; case 'C': /* don't chdir to the script directory */
SG(options) |= SAPI_OPTION_NO_CHDIR;
break; case 'e': /* enable extended info output */
CG(compiler_options) |= ZEND_COMPILE_EXTENDED_INFO;
break; case 'f': /* parse file */
if (script_file) {
efree(script_file);
}
script_file = estrdup(php_optarg);
no_headers = ;
break; case 'i': /* php info & quit */
if (script_file) {
efree(script_file);
}
if (php_request_startup(TSRMLS_C) == FAILURE) {
SG(server_context) = NULL;
php_module_shutdown(TSRMLS_C);
return FAILURE;
}
if (no_headers) {
SG(headers_sent) = ;
SG(request_info).no_headers = ;
}
php_print_info(0xFFFFFFFF TSRMLS_CC);
php_request_shutdown((void *) );
fcgi_shutdown();
exit_status = ;
goto out; case 'l': /* syntax check mode */
no_headers = ;
behavior = PHP_MODE_LINT;
break; case 'm': /* list compiled in modules */
if (script_file) {
efree(script_file);
}
SG(headers_sent) = ;
php_printf("[PHP Modules]\n");
print_modules(TSRMLS_C);
php_printf("\n[Zend Modules]\n");
print_extensions(TSRMLS_C);
php_printf("\n");
php_output_end_all(TSRMLS_C);
fcgi_shutdown();
exit_status = ;
goto out; #if 0 /* not yet operational, see also below ... */
case '': /* generate indented source mode*/
behavior=PHP_MODE_INDENT;
break;
#endif case 'q': /* do not generate HTTP headers */
no_headers = ;
break; case 'v': /* show php version & quit */
if (script_file) {
efree(script_file);
}
no_headers = ;
if (php_request_startup(TSRMLS_C) == FAILURE) {
SG(server_context) = NULL;
php_module_shutdown(TSRMLS_C);
return FAILURE;
}
if (no_headers) {
SG(headers_sent) = ;
SG(request_info).no_headers = ;
}
#if ZEND_DEBUG
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());
#else
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());
#endif
php_request_shutdown((void *) );
fcgi_shutdown();
exit_status = ;
goto out; case 'w':
behavior = PHP_MODE_STRIP;
break; case 'z': /* load extension file */
zend_load_extension(php_optarg);
break; default:
break;
}
} if (script_file) {
/* override path_translated if -f on command line */
STR_FREE(SG(request_info).path_translated);
SG(request_info).path_translated = script_file;
/* before registering argv to module exchange the *new* argv[0] */
/* we can achieve this without allocating more memory */
SG(request_info).argc = argc - (php_optind - );
SG(request_info).argv = &argv[php_optind - ];
SG(request_info).argv[] = script_file;
} else if (argc > php_optind) {
/* file is on command line, but not in -f opt */
STR_FREE(SG(request_info).path_translated);
SG(request_info).path_translated = estrdup(argv[php_optind]);
/* arguments after the file are considered script args */
SG(request_info).argc = argc - php_optind;
SG(request_info).argv = &argv[php_optind];
} if (no_headers) {
SG(headers_sent) = ;
SG(request_info).no_headers = ;
} /* all remaining arguments are part of the query string
* this section of code concatenates all remaining arguments
* into a single string, seperating args with a &
* this allows command lines like:
*
* test.php v1=test v2=hello+world!
* test.php "v1=test&v2=hello world!"
* test.php v1=test "v2=hello world!"
*/
if (!SG(request_info).query_string && argc > php_optind) {
int slen = strlen(PG(arg_separator).input);
len = ;
for (i = php_optind; i < argc; i++) {
if (i < (argc - )) {
len += strlen(argv[i]) + slen;
} else {
len += strlen(argv[i]);
}
} len += ;
s = malloc(len);
*s = '\0'; /* we are pretending it came from the environment */
for (i = php_optind; i < argc; i++) {
strlcat(s, argv[i], len);
if (i < (argc - )) {
strlcat(s, PG(arg_separator).input, len);
}
}
SG(request_info).query_string = s;
free_query_string = ;
}
} /* end !cgi && !fastcgi */ /*
we never take stdin if we're (f)cgi, always
rely on the web server giving us the info
we need in the environment.
*/
if (SG(request_info).path_translated || cgi || fastcgi) {
file_handle.type = ZEND_HANDLE_FILENAME;
file_handle.filename = SG(request_info).path_translated;
file_handle.handle.fp = NULL;
} else {
file_handle.filename = "-";
file_handle.type = ZEND_HANDLE_FP;
file_handle.handle.fp = stdin;
} file_handle.opened_path = NULL;
file_handle.free_filename = ; /* request startup only after we've done all we can to
* get path_translated */
if (php_request_startup(TSRMLS_C) == FAILURE) {
if (fastcgi) {
fcgi_finish_request(request, );
}
SG(server_context) = NULL;
php_module_shutdown(TSRMLS_C);
return FAILURE;
}
if (no_headers) {
SG(headers_sent) = ;
SG(request_info).no_headers = ;
} /*
at this point path_translated will be set if:
1. we are running from shell and got filename was there
2. we are running as cgi or fastcgi
*/
if (cgi || fastcgi || SG(request_info).path_translated) {
if (php_fopen_primary_script(&file_handle TSRMLS_CC) == FAILURE) {
zend_try {
if (errno == EACCES) {
SG(sapi_headers).http_response_code = ;
PUTS("Access denied.\n");
} else {
SG(sapi_headers).http_response_code = ;
PUTS("No input file specified.\n");
}
} zend_catch {
} zend_end_try();
/* we want to serve more requests if this is fastcgi
* so cleanup and continue, request shutdown is
* handled later */
if (fastcgi) {
goto fastcgi_request_done;
} STR_FREE(SG(request_info).path_translated); if (free_query_string && SG(request_info).query_string) {
free(SG(request_info).query_string);
SG(request_info).query_string = NULL;
} php_request_shutdown((void *) );
SG(server_context) = NULL;
php_module_shutdown(TSRMLS_C);
sapi_shutdown();
#ifdef ZTS
tsrm_shutdown();
#endif
return FAILURE;
}
} if (CGIG(check_shebang_line)) {
/* #!php support */
switch (file_handle.type) {
case ZEND_HANDLE_FD:
if (file_handle.handle.fd < ) {
break;
}
file_handle.type = ZEND_HANDLE_FP;
file_handle.handle.fp = fdopen(file_handle.handle.fd, "rb");
/* break missing intentionally */
case ZEND_HANDLE_FP:
if (!file_handle.handle.fp ||
(file_handle.handle.fp == stdin)) {
break;
}
c = fgetc(file_handle.handle.fp);
if (c == '#') {
while (c != '\n' && c != '\r' && c != EOF) {
c = fgetc(file_handle.handle.fp); /* skip to end of line */
}
/* handle situations where line is terminated by \r\n */
if (c == '\r') {
if (fgetc(file_handle.handle.fp) != '\n') {
long pos = ftell(file_handle.handle.fp);
fseek(file_handle.handle.fp, pos - , SEEK_SET);
}
}
CG(start_lineno) = ;
} else {
rewind(file_handle.handle.fp);
}
break;
case ZEND_HANDLE_STREAM:
c = php_stream_getc((php_stream*)file_handle.handle.stream.handle);
if (c == '#') {
while (c != '\n' && c != '\r' && c != EOF) {
c = php_stream_getc((php_stream*)file_handle.handle.stream.handle); /* skip to end of line */
}
/* handle situations where line is terminated by \r\n */
if (c == '\r') {
if (php_stream_getc((php_stream*)file_handle.handle.stream.handle) != '\n') {
long pos = php_stream_tell((php_stream*)file_handle.handle.stream.handle);
php_stream_seek((php_stream*)file_handle.handle.stream.handle, pos - , SEEK_SET);
}
}
CG(start_lineno) = ;
} else {
php_stream_rewind((php_stream*)file_handle.handle.stream.handle);
}
break;
case ZEND_HANDLE_MAPPED:
if (file_handle.handle.stream.mmap.buf[] == '#') {
int i = ; c = file_handle.handle.stream.mmap.buf[i++];
while (c != '\n' && c != '\r' && c != EOF) {
c = file_handle.handle.stream.mmap.buf[i++];
}
if (c == '\r') {
if (file_handle.handle.stream.mmap.buf[i] == '\n') {
i++;
}
}
file_handle.handle.stream.mmap.buf += i;
file_handle.handle.stream.mmap.len -= i;
}
break;
default:
break;
}
} switch (behavior) {
case PHP_MODE_STANDARD:
php_execute_script(&file_handle TSRMLS_CC);
break;
case PHP_MODE_LINT:
PG(during_request_startup) = ;
exit_status = php_lint_script(&file_handle TSRMLS_CC);
if (exit_status == SUCCESS) {
zend_printf("No syntax errors detected in %s\n", file_handle.filename);
} else {
zend_printf("Errors parsing %s\n", file_handle.filename);
}
break;
case PHP_MODE_STRIP:
if (open_file_for_scanning(&file_handle TSRMLS_CC) == SUCCESS) {
zend_strip(TSRMLS_C);
zend_file_handle_dtor(&file_handle TSRMLS_CC);
php_output_teardown();
}
return SUCCESS;
break;
case PHP_MODE_HIGHLIGHT:
{
zend_syntax_highlighter_ini syntax_highlighter_ini; if (open_file_for_scanning(&file_handle TSRMLS_CC) == SUCCESS) {
php_get_highlight_struct(&syntax_highlighter_ini);
zend_highlight(&syntax_highlighter_ini TSRMLS_CC);
if (fastcgi) {
goto fastcgi_request_done;
}
zend_file_handle_dtor(&file_handle TSRMLS_CC);
php_output_teardown();
}
return SUCCESS;
}
break;
#if 0
/* Zeev might want to do something with this one day */
case PHP_MODE_INDENT:
open_file_for_scanning(&file_handle TSRMLS_CC);
zend_indent();
zend_file_handle_dtor(&file_handle TSRMLS_CC);
php_output_teardown();
return SUCCESS;
break;
#endif
} fastcgi_request_done:
{
STR_FREE(SG(request_info).path_translated); php_request_shutdown((void *) ); if (exit_status == ) {
exit_status = EG(exit_status);
} if (free_query_string && SG(request_info).query_string) {
free(SG(request_info).query_string);
SG(request_info).query_string = NULL;
}
} if (!fastcgi) {
if (benchmark) {
repeats--;
if (repeats > ) {
script_file = NULL;
php_optind = orig_optind;
php_optarg = orig_optarg;
continue;
}
}
break;
} /* only fastcgi will get here */
requests++;
if (max_requests && (requests == max_requests)) {
fcgi_finish_request(request, );
if (bindpath) {
free(bindpath);
}
if (max_requests != ) {
/* no need to return exit_status of the last request */
exit_status = ;
}
break;
}
/* end of fastcgi loop */
}
最终会通过 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模块分析的更多相关文章
- nginx事件模块分析(一)
nginx ngx_events_module模块分析 ngx_events_module模块是核心模块之一,它是其它所有事件模块的代理模块.nginx在启动时只与events模块打交道,而由even ...
- 游戏模块分析总结(2)之UI、操作篇
转自:http://www.gameres.com/309812.html 游戏模块分析总结(2)之UI.操作篇 发布者: wuye | 发布时间: 2014-12-12 15:03| 评论数: 0 ...
- css扁平化博客学习总结(一)模块分析
一.模块分析 1.每开发一个项目之前,首先要对项目进行一个大致规划,它到底要做什么功能,它有什么具体需求. 2.所以需要进行模块化分析,把这些东西具象化,把一个问题模块化,对需求有一个宏观的了解. 3 ...
- OpenRisc-43-or1200的IF模块分析
引言 “喂饱饥饿的CPU”,是计算机体系结构设计者时刻要考虑的问题.要解决这个问题,方法大体可分为两部分,第一就是利用principle of locality而引进的cache技术,缩短取指时间,第 ...
- OpenRisc-41-or1200的cache模块分析
引言 为CPU提供足够的,稳定的指令流和数据流是计算机体系结构设计中两个永恒的话题.为了给CPU提供指令流,需要设计分支预测机构,为了给CPU提供数据流,就需要设计cache了.其实,无论是insn还 ...
- OpenRisc-45-or1200的ID模块分析
引言 之前,我们分析了or1200流水线的整体结构,也分析了流水线中IF级,EX级,本小节我们来分析ID(insn decode)级的一些细节. 1,基础 or1200的pipeline的ID阶段包含 ...
- 【转】python模块分析之collections(六)
[转]python模块分析之collections(六) collections是Python内建的一个集合模块,提供了许多有用的集合类. 系列文章 python模块分析之random(一) pyth ...
- 【转】python模块分析之unittest测试(五)
[转]python模块分析之unittest测试(五) 系列文章 python模块分析之random(一) python模块分析之hashlib加密(二) python模块分析之typing(三) p ...
- 【转】python模块分析之typing(三)
[转]python模块分析之typing(三) 前言:很多人在写完代码一段时间后回过头看代码,很可能忘记了自己写的函数需要传什么参数,返回什么类型的结果,就不得不去阅读代码的具体内容,降低了阅读的速度 ...
随机推荐
- 247. Strobogrammatic Number II
题目: A strobogrammatic number is a number that looks the same when rotated 180 degrees (looked at ups ...
- 使用Retrofit时出现 java.lang.IllegalArgumentException: URL query string "t={type}&p={page}&size={count}" must not have replace block. For dynamic query parameters use @Query.异常原因
/** * Created by leo on 16/4/30. */ public interface GanchaiService { @GET("digest?t={type}& ...
- Maven远程仓库
根据Apache Maven声明:"只有当在本地和中心仓库找不到所需的依赖文件,才会到远程仓库去下载". 当在Maven里声明了一个本地和中心仓库都没有的库文件时,Maven会停止 ...
- 语言基础:C#运算符
运算符 分类 符号 解释 优先级 算术运算符 ++ -- 加加 减减 由高到低,即执行顺序由上到下.(圆括号的优先级最高) * / % 乘 除 取余 + - 加 减 关系运 ...
- unity3d5.2.3中 调整视角
按住alt键不放,然后左边的手的图标会变成一个眼睛,在Scene中移动.就会发现可以调整视角了
- Oracle学习之集合运算
一.集合运算操作符 UNION:(并集)返回两个集合去掉重复值的所有的记录 UNION ALL:(并集)返回两个集合去掉重复值的所有的记录 INTERSECT:(交集)返回两个集合的所有记录,重复 ...
- SetCapture、ReleaseCapture、GetCapture
正常情况下,鼠标指针位于哪个窗口区域内,鼠标消息就自动发给哪个窗口.如果调用了SetCapture,之后无论鼠标的位置在哪,鼠标消息都发给指定的这个窗口,直到调用ReleaseCapture或者调用S ...
- poj3307
可以证明,每个符合的数都由2,3,5,7相乘得到. 依据猜想:下一个出现的数是由前面某个数乘上这几个数之一得到的新的数. 假设之前的数均满足序列,则因为下一个数必有2,3,5,7相乘得到,而这个数之前 ...
- 函数buf_pool_get
根据space ,offset 获取 buff pool的实例 下标 /**************************************************************** ...
- fancybox 无效 失效 直接打开页面, ajax 之后 fancybox对更新的数据无效,Jquery失效 无效
案例:做个聊天室项目,数据都是通过ajax刷新出来的,而对新数据绑定的fancybox均无效,点击直接打开到了新页面而不是弹窗,解决方法其实很简单 简单分析:ajax加载内容是在$(documen ...