缘起
学习fastcgi的时候,多个fcgi进程的启动和管理是个无法回避的问题;有一个叫做spawn-fcgi的程序,可以启动多个php-cgi进程,并且同时accept同一个socket,因为spawn-fcgi和php-cgi是两个项目中的程序,那么他们是如何配合的如此默契的呢?
资料: http://redmine.lighttpd.net/projects/spawn-fcgi 其中主要是: http://redmine.lighttpd.net/projects/spawn-fcgi/repository/entry/trunk/src/spawn-fcgi.c
分析
从代码来看,spawn-fcgi负责lisent一个socket,然后开始fork指定数量的子进程,每个子进程中先把socket的fd复制到FCGI使用的fd(似乎是约定好的一个fd),然后就把后续的事情通过exec交给外部的进程(如:php-cgi)了
下面看一下PHP中fastcgi的相关逻辑, 文件 sapi/cgi/fastcgi.c 中的 int fcgi_init(void);函数体现了如何识别是否fastcgi的逻辑,大致意思为: 如果标准输入为一个socket(不是fileno),则认为是fastcgi,代码片段如下:
427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 |
#ifdef _WIN32 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, 0, fcgi_shutdown_thread, shutdown_event, 0, NULL)) { return -1; } } str = getenv("_FCGI_MUTEX_"); if (str != NULL) { if (str != NULL) { HANDLE shutdown_event = (HANDLE) atoi(str); if (!CreateThread(NULL, 0, fcgi_shutdown_thread, shutdown_event, 0, NULL)) { return -1; } } str = getenv("_FCGI_MUTEX_"); if (str != NULL) { fcgi_accept_mutex = (HANDLE) atoi(str); } return is_fastcgi = 1; } else { return is_fastcgi = 0; } #else errno = 0; if (getpeername(0, (struct sockaddr *)&sa, &len) != 0 && errno == ENOTCONN) { fcgi_setup_signals(); return is_fastcgi = 1; } else { return is_fastcgi = 0; } #endif |
注意其中 getpeername(…)的使用,如果 errno == ENOTCONN 才可能认为是fastcgi,那么,如果刚好该socket处于连接状态不就不能视为fastcgi环境了吗? 或许吧