[摘要]在windows下搭建nginx+php环境时,php-cgi.exe会经常性的自动关闭退出,本文介绍通过使用xxfpm进程管理器管理php-cgi.exe。

php-cgi.exe在windows+nginx平台下经常自动退出,网上搜到的大部分解决方法都是类似上面的批处理(代码如下)文件临时解决一下,但如果用户在网站登录的话,用户就会突然挂掉。

@echo off
:main
set jinchengshuliang=0
set jinchengshuliangxiaxian=2
for /f %%i in ('tasklist /nh^|findstr /i /s /c:"php-cgi.exe"') do set /a jinchengshuliang+=1 if %jinchengshuliang% lss %jinchengshuliangxiaxian% (
goto youwenti
) else (
goto meiwenti
) :youwenti
echo 进程丢失,现在添加5个进程
RunHiddenConsole.exe php\php-cgi.exe -b 127.0.0.1:9000 -c php\php.ini
RunHiddenConsole.exe php\php-cgi.exe -b 127.0.0.1:9000 -c php\php.ini
RunHiddenConsole.exe php\php-cgi.exe -b 127.0.0.1:9000 -c php\php.ini
RunHiddenConsole.exe php\php-cgi.exe -b 127.0.0.1:9000 -c php\php.ini
RunHiddenConsole.exe php\php-cgi.exe -b 127.0.0.1:9000 -c php\php.ini
ping 127.1 -n 8
goto main :meiwenti
echo 正常运行中!
ping 127.1 -n 8
goto main

 

最好的解决办法是用windows下的php-cgi进程管理器,该进程管理器需要用到pthreadGC2.dll。源码和编译文件在本文结尾提供下载。经测试,支持Win32和Linux-x86平台。对于用php的人,有了这个东西来维护一定数量的进程,就能制服经常崩溃退出的php-cgi啦!

下载地址:http://down.chinaz.com/soft/31157.htm

以下是xxfpm进程管理器的操作参数:

 Usage: xxfpm path [-n number] [-i ip] [-p port]
Manage FastCGI processes. -n, --number number of processes to keep
-i, --ip ip address to bind
-p, --port port to bind, default is
-u, --user start processes using specified linux user
-g, --group start processes using specified linux group
-r, --root change root direcotry for the processes
-h, --help output usage information and exit
-v, --version output version information and exit

第一个写得比较标准的终端应用程序,我是看了cygwin的里的一些源代码,然后学会了如何使用getopt,算是写得比较标准的,但是代码也不短。

使用例子:

xxfpm z:/php5/php-cgi.exe -n 5 -p 8080

  有人问,如何给程序加入参数?这个不难,使用双引号即可,路径要用"/"而不用"\"。例如要指定php.ini的路径,可以用下面例子:

xxfpm "z:/php5/php-cgi.exe -c z:/php5/php.ini" -n 5 -i 127.0.0.1 -p 8080

维护进程原理:

  Windows上使用CreateProcess创建进程,使用Wait For Single Object等待进程结束;Linux上使用fork和execl创建进程,使用waitpid等待进程结束。Linux的版本多了在创建子进程的时候可以设置进程限制,能够以受限用户方式来运行。

  当进程管理器被关闭的时候,它所创建的所有子进程也必须被关闭。Windows上使用JobObject这个东西来把子进程与管理器的进程产生关联,感谢iceboy提供的资料!Linux上通过捕捉关闭信号,然后给所有子进程发送SIGTERM来结束子进程。详见源代码:

 #ifdef __WIN32__

 #ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0500
#endif //_WIN32_WINNT #include <windows.h>
#include <winsock.h>
#include <wininet.h>
#define SHUT_RDWR SD_BOTH #ifndef JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
#define JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE (0x2000)
#endif
HANDLE FcpJobObject; #else #include <sys/socket.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <grp.h>
#include <pwd.h>
#include <unistd.h>
#define closesocket close #endif //__WIN32__ #include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <string.h>
#include <pthread.h>
#include <errno.h> #define MAX_PROCESSES 1024
static const char version[] = "$Revision: 0.01 $";
static char* prog_name;
int number = ;
int port = ;
char *ip = "127.0.0.1";
char *user = "";
char *root = "";
char *path = "";
char *group = "";
int listen_fd;
struct sockaddr_in listen_addr;
int process_fp[MAX_PROCESSES];
int process_idx = ;
pthread_t threads[MAX_PROCESSES]; static struct option longopts[] =
{
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'v'},
{"number", required_argument, NULL, 'n'},
{"ip", required_argument, NULL, 'i'},
{"port", required_argument, NULL, 'p'},
{"user", required_argument, NULL, 'u'},
{"group", required_argument, NULL, 'g'},
{"root", required_argument, NULL, 'r'},
{NULL, , NULL, }
}; static char opts[] = "hvnipugr"; static void usage(FILE* where)
{
fprintf(where, ""
"Usage: %s path [-n number] [-i ip] [-p port]\n"
"Manage FastCGI processes.\n"
"\n"
" -n, --number number of processes to keep\n"
" -i, --ip ip address to bind\n"
" -p, --port port to bind, default is 8000\n"
" -u, --user start processes using specified linux user\n"
" -g, --group start processes using specified linux group\n"
" -r, --root change root direcotry for the processes\n"
" -h, --help output usage information and exit\n"
" -v, --version output version information and exit\n"
"", prog_name);
exit(where == stderr ? :);
} static void print_version()
{
printf("%s %s\n\
FastCGI Process Manager\n\
Copyright Xiaoxia.org\n\
Compiled on %s\n\
", prog_name, version, __DATE__);
exit();
} static int try_to_bind()
{
listen_addr.sin_family = PF_INET;
listen_addr.sin_addr.s_addr = inet_addr( ip );
listen_addr.sin_port = htons( port );
listen_fd = socket(AF_INET, SOCK_STREAM, ); if (- == bind(listen_fd, (struct sockaddr*)&listen_addr, sizeof(struct sockaddr_in)) ) {
fprintf(stderr, "failed to bind %s:%d\n", ip, port );
return -;
} listen(listen_fd, MAX_PROCESSES);
return ;
} static void* spawn_process(void* arg)
{
int idx = process_idx ++, ret;
while(){
#ifdef __WIN32__
STARTUPINFO si={};
PROCESS_INFORMATION pi={};
ZeroMemory(&si,sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdInput = (HANDLE)listen_fd;
si.hStdOutput = INVALID_HANDLE_VALUE;
si.hStdError = INVALID_HANDLE_VALUE;
if( == (ret=CreateProcess(NULL, path,
NULL,NULL,
TRUE, CREATE_NO_WINDOW | CREATE_SUSPENDED | CREATE_BREAKAWAY_FROM_JOB,
NULL,NULL,
&si,&pi)) ){
fprintf(stderr, "failed to create process %s, ret=%d\n", path, ret);
return NULL;
} /* Use Job Control System */
if(!AssignProcessToJobObject(FcpJobObject, pi.hProcess)){
TerminateProcess(pi.hProcess, );
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return NULL;
} if(!ResumeThread(pi.hThread)){
TerminateProcess(pi.hProcess, );
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return NULL;
} process_fp[idx] = (int)pi.hProcess;
WaitForSingleObject(pi.hProcess, INFINITE);
process_fp[idx] = ;
CloseHandle(pi.hThread);
#else
ret = fork();
switch(ret){
case :{ //child
/* change uid from root to other user */
if(getuid()==){
struct group *grp = NULL;
struct passwd *pwd = NULL;
if (*user) {
if (NULL == (pwd = getpwnam(user))) {
fprintf(stderr, "[fcgi] %s %s\n", "can't find username", user);
exit(-);
} if (pwd->pw_uid == ) {
fprintf(stderr, "[fcgi] %s\n", "what? dest uid == 0?" );
exit(-);
}
} if (*group) {
if (NULL == (grp = getgrnam(group))) {
fprintf(stderr, "[fcgi] %s %s\n", "can't find groupname", group);
exit();
} if (grp->gr_gid == ) {
fprintf(stderr, "[fcgi] %s\n", "what? dest gid == 0?" );
exit();
}
/* do the change before we do the chroot() */
setgid(grp->gr_gid);
setgroups(, NULL); if (user) {
initgroups(user, grp->gr_gid);
}
}
if (*root) {
if (- == chroot(root)) {
fprintf(stderr, "[fcgi] %s %s\n", "can't change root", root);
exit();
}
if (- == chdir("/")) {
fprintf(stderr, "[fcgi] %s %s\n", "can't change dir to", root);
exit();
}
} /* drop root privs */
if (*user) {
setuid(pwd->pw_uid);
}
} int max_fd = , i=;
// Set stdin to listen_fd
close(STDIN_FILENO);
dup2(listen_fd, STDIN_FILENO);
close(listen_fd);
// Set stdout and stderr to dummy fd
max_fd = open("/dev/null", O_RDWR);
close(STDERR_FILENO);
dup2(max_fd, STDERR_FILENO);
close(max_fd);
max_fd = open("/dev/null", O_RDWR);
close(STDOUT_FILENO);
dup2(max_fd, STDOUT_FILENO);
close(max_fd);
// close other handles
for(i=; i<max_fd; i++)
close(i);
char *b = malloc(strlen("exec ") + strlen(path) + );
strcpy(b, "exec ");
strcat(b, path); /* exec the cgi */
execl("/bin/sh", "sh", "-c", b, (char *)NULL);
exit(errno);
break;
}
case -:
fprintf(stderr, "[fcgi] fork failed\n");
return NULL;
default:{
struct timeval tv = { , * };
int status;
select(, NULL, NULL, NULL, &tv);
switch(waitpid(ret, &status, WNOHANG)){
case :
printf("[fcg] spawned process %s: %d\n", path, ret);
break;
case -:
fprintf(stderr, "[fcgi] waitpid failed\n");
return NULL;
default:
if (WIFEXITED(status)) {
fprintf(stderr, "[fcgi] child exited with: %d\n", WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
fprintf(stderr, "[fcgi] child signaled: %d\n", WTERMSIG(status));
} else {
fprintf(stderr, "[fcgi] child died somehow: %d\n", status);
}
return NULL;
}
//wait for child process to exit
process_fp[idx] = ret;
waitpid(ret, &status, );
process_fp[idx] = ;
}
}
#endif
}
} static int start_processes()
{
int i;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setstacksize(&attr, *); //64KB
for(i=; i<number; i++){
if( pthread_create( &threads, &attr, spawn_process, NULL ) == - ){
fprintf(stderr, "failed to create thread %d\n", i);
}
} for(i=; i<number; i++){
pthread_join(threads, NULL);
}
return ;
} #ifdef __WIN32__
void init_win32()
{
/* init win32 socket */
static WSADATA wsa_data;
if(WSAStartup((WORD)(<<|), &wsa_data) != )
exit();
JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit;
FcpJobObject = (HANDLE)CreateJobObject(NULL, NULL);
if(FcpJobObject == NULL)
exit(); /* let all processes assigned to this job object
* being killed when the job object closed */
if (!QueryInformationJobObject(FcpJobObject, JobObjectExtendedLimitInformation, &limit, sizeof(limit), NULL)) {
CloseHandle(FcpJobObject);
exit();
} limit.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; if (!SetInformationJobObject(FcpJobObject, JobObjectExtendedLimitInformation, &limit, sizeof(limit))) {
CloseHandle(FcpJobObject);
exit();
}
}
#endif //__WIN32__ #ifndef __WIN32__
void before_exit(int sig)
{
signal(SIGTERM, SIG_DFL);
/* call child processes to exit */
kill(, SIGTERM);
}
#endif int main(int argc, char **argv)
{
prog_name = strrchr(argv[], '/');
if(prog_name == NULL)
prog_name = strrchr(argv[], '\\');
if(prog_name == NULL)
prog_name = argv[];
else
prog_name++; if(argc == )
usage(stderr); path = argv[]; opterr = ; char* p; for(;;){
int ch;
if((ch = getopt_long(argc, argv, opts, longopts, NULL)) == EOF)
break;
char *av = argv[optind];
switch(ch){
case 'h':
usage(stdout);
break;
case 'v':
print_version();
break;
case 'n':
number = atoi(av);
if(number > MAX_PROCESSES){
fprintf(stderr, "exceeds MAX_PROCESSES!\n");
number = MAX_PROCESSES;
}
break;
case 'u':
user = av;
break;
case 'r':
root = av;
break;
case 'g':
group = av;
break;
case 'i':
ip = av;
break;
case 'p':
port = atoi(av);
break;
default:
usage(stderr);
break;
}
} #ifdef __WIN32__
init_win32();
#else
/* call child processes to exit */
signal(SIGTERM, before_exit);
signal(SIGINT, before_exit);
signal(SIGABRT, before_exit);
#endif int ret;
ret = try_to_bind();
if(ret != )
return ret;
ret = start_processes();
if(ret !=)
return ret; #ifdef __WIN32__
CloseHandle(FcpJobObject);
WSACleanup();
#endif
return ;
}

来源:http://down.chinaz.com/server/201111/1334_1.htm

完美解决windows+ngnix+phpcgi自动退出的问题的更多相关文章

  1. paip. 解决java程序不能自动退出

    paip. 解决java程序不能自动退出 原因:有东西在运行,所以,不能自动退出.. 作者Attilax  艾龙,  EMAIL:1466519819@qq.com 来源:attilax的专栏 地址: ...

  2. 【记录】解决windows中nginx明明退出了,为什么还能反向代理?CMD强制杀死进程命令

    博主今天遇到一个很奇怪的问题,nginx在windows中明明已经退出了,而且在任务管理器中也没发现nginx进程, 为什么还能反向代理呢? 找了半天资料终于解决,现记录如下,希望能帮助到你. 步骤一 ...

  3. 解决windows 10关机自动重启的问题

    自从windows 10推出来没多久,就给台式机安装了.可是,有点悲剧的是:每次关机,都会自动重启(restart). 之后也在网上找了一些解决方式,但还是没用.前天通过搜索”Windows 10 c ...

  4. DBImport V3.7版本发布及软件稳定性(自动退出问题)解决过程分享

    DBImport V3.7介绍: 1:先上图,再介绍亮点功能: 主要的升级功能为: 1:增加(Truncate Table)清表再插入功能: 清掉再插,可以保证两个库的数据一致,自己很喜欢这个功能. ...

  5. 解决Ecshop因为动态ip问题登录后台自动退出

    解决Ecshop因为动态ip问题登录后台自动退出 PHP  铁匠  2年前 (2013-07-21)  1130℃  0评论 修改lib_base.php文件real_ip()函数,添加以下代码即可解 ...

  6. 解决windows下MySQL表名大写自动变小写的问题

    解决windows下MySQL表名大写自动变小写的问题   有些人可能会遇到在windows下,表名不能用大写字母, 即使使用了大写字母的建表语句,还是会被自动转成小写.   解决方法:  打开 My ...

  7. (转)完美解决 Android WebView 文本框获取焦点后自动放大有关问题

    完美解决 Android WebView 文本框获取焦点后自动放大问题 前几天在写一个项目时,要求在项目中嵌入一个WebView 本来很快就完成了,测试也没有问题.但发给新加坡时,他们测试都会出现文本 ...

  8. ecshop后台登录频繁自动退出问题终极解决方法集锦

    ecshop后台登录后,有时候会自动退出,而且还会很频繁,有的是后台操作两下就莫名退出了,有的是恰好三分钟左右登出.这让管理员很恼火,严重影响了后台使用.对于这一问题,网络上可给的解决方法各有不同.千 ...

  9. ecmall用户登录后自动退出解决方法

    国内私募机构九鼎控股打造APP,来就送 20元现金领取地址:http://jdb.jiudingcapital.com/phone.html内部邀请码:C8E245J (不写邀请码,没有现金送)国内私 ...

随机推荐

  1. Wpf ViewModel中 ObservableCollection不支持从调度程序线程以外的线程对其 SourceCollection 进行的更改

    Wpf中ViewModel类里面经常会需要用到ObservableCollection来管理列表数据,在做异步通信的时候也会碰到“不支持从调度程序线程以外的线程对其 SourceCollection ...

  2. 学习 Spring (八) 注解之 Bean 的定义及作用域

    Spring入门篇 学习笔记 Classpath 扫描与组件管理 从 Spring 3.0 开始,Spring JavaConfig 项目提供了很多特性,包括使用 java 而不是 XML 定义 be ...

  3. vuex2.0 基本使用(3) --- getter

    有的组件中获取到 store 中的state,  需要对进行加工才能使用,computed 属性中就需要写操作函数,如果有多个组件中都需要进行这个操作,那么在各个组件中都写相同的函数,那就非常麻烦,这 ...

  4. Nginx 热部署最版本

    L10 进入nginx里的sbin目录 拷贝原先的做备份 cp nginx nginx.old 然后将已经编译好的nginx二进制文件复制到sbin目录下并覆盖原有的二进制文件 kill -USR2 ...

  5. 3、springframe常用注解

    1.@controller 控制器(注入服务) 2.@service 服务(注入dao) 3.@repository dao(实现dao访问) 4.@component (把普通pojo实例化到spr ...

  6. 贝叶斯定理推导(Bayes' Theorem Induction)

    这里用Venn diagram来不严谨地推导一下贝叶斯定理. 假设A和B为两个不相互独立的事件. 交集(intersection):  上图红色部分即为事件A和事件B的交集. 并集(union):  ...

  7. Matplotlib学习---用matplotlib画箱线图(boxplot)

    箱线图通过数据的四分位数来展示数据的分布情况.例如:数据的中心位置,数据间的离散程度,是否有异常值等. 把数据从小到大进行排列并等分成四份,第一分位数(Q1),第二分位数(Q2)和第三分位数(Q3)分 ...

  8. GCD HDU - 2588

    输入 N 和 M (2<=N<=1000000000, 1<=M<=N), 找出所有满足1<=X<=N 且 gcd(X,N)>=M 的 X 的数量. Inpu ...

  9. CF1106F Lunar New Year and a Recursive Sequence

    题目链接:CF1106F Lunar New Year and a Recursive Sequence 大意:已知\(f_1,f_2,\cdots,f_{k-1}\)和\(b_1,b_2,\cdot ...

  10. SpringMVC 拦截器使用说明

    spring-content.xml <!-- 配置用于session验证的拦截器 --> <!-- 如果有多个拦截器满足拦截处理的要求,则依据配置的先后顺序来执行 --> & ...