缘起
学习fastcgi的时候,多个fcgi进程的启动和管理是个无法回避的问题;有一个叫做spawn-fcgi的程序,可以启动多个php-cgi进程,并且同时accept同一个socket,因为spawn-fcgi和php-cgi是两个项目中的程序,那么他们是如何配合的如此默契的呢?
分析
从代码来看,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,代码片段如下:
#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
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;
DWORDpipe_mode=PIPE_READMODE_BYTE|PIPE_WAIT;
HANDLEpipe=GetStdHandle(STD_INPUT_HANDLE);
SetNamedPipeHandleState(pipe,&pipe_mode, NULL, NULL);
str=getenv("_FCGI_SHUTDOWN_EVENT_");
if(str!=NULL){
HANDLEshutdown_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){
HANDLEshutdown_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);
}
returnis_fastcgi=1;
}else{
returnis_fastcgi=0;
}
#else
errno=0;
if(getpeername(0,(structsockaddr*)&sa, &len) != 0 && errno == ENOTCONN) {
fcgi_setup_signals();
returnis_fastcgi=1;
}else{
returnis_fastcgi=0;
}
#endif
注意其中 getpeername(…)的使用,如果 errno == ENOTCONN 才可能认为是fastcgi,那么,如果刚好该socket处于连接状态不就不能视为fastcgi环境了吗? 或许吧