[摘要]在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. Eclipse报错:An internal error has occurred. Widget is disposed

    win10家庭版报错. 右键Eclipse的快捷方式,在兼容性窗口的兼容模式中,将“以兼容模式运行这个程序”选项打对勾.选择win8就可以解决问题.

  2. vue2 mint-ui loadmore(下拉刷新)

    <template> <div class="page-loadmore"> <h1 class="page-title"> ...

  3. git reset 版本回退的三种用法总结

    git reset (–mixed) HEAD~1 回退一个版本,且会将暂存区的内容和本地已提交的内容全部恢复到未暂存的状态,不影响原来本地文件(未提交的也不受影响) git reset –soft ...

  4. MySQL启动错误---发生系统错误/系统找不到指定的文件。

    今天启动mysql时,突然报错发生系统错误,系统找不到指定的文件.当时有点懵,安装mysql 之后,一直就没有修改过,怎么会报错呢?上网搜索了一下,重新安装一下mysql服务就可以了,现在也不知道什么 ...

  5. Base64 总结

    Base64编码是解决一些无法打印的字符无法显示的问题,将8位的ascii编码转换为6位的表示64个可见字符的算法. 具体而言,首先将编码每三个分成一组,将三个字符转换为总长为24位的二进制 数字,将 ...

  6. 实验吧 WEB 头有点大

    看到了良心的提示,http header,之后看到了要求.NET framework 9.9 英国 IE,我想想.NET好像还没有更新到9.9,就无视了这重要的提示. 我就看了一眼题解,发现burps ...

  7. Android View相关知识问答

    Android View相关核心知识问答 Activity Window View之间的三角关系 你真的了解View的坐标吗? 在渲染前获取 View 的宽高 5种手势工具类 浅析Android的窗口

  8. Java自定义线程池-记录每个线程执行耗时

    ThreadPoolExecutor是可扩展的,其提供了几个可在子类化中改写的方法,如下: protected void beforeExecute(Thread t, Runnable r) { } ...

  9. Collect devices information

    Collect devices information root@vpx-test# kenv > kenv.txt root@vpx-test# sysctl -a > sysctl_a ...

  10. mac centos linux 安装PHP扩展 INTL(国际化) ———— error: 'ext/standard/php_smart_str.h'

    PHP简单源码安装扩展 五个步骤: 详细说明下: cd /fujieace/php7.0/ext/intl:#进入INTL扩展目录? 在编译扩展时候需要phpize准备环境,准备程序需要获取这个目录的 ...