[摘要]在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. qtp 自动货测试桌面程序-笔记(使用函数)

    新建-function 写入函数 rem 关闭出现错误窗口Function checkExist() If Window("出现错误").WinObject("确定&qu ...

  2. mobile deeplearning

    框架: 腾讯ncnn https://github.com/Tencent/ncnn 百度mobile-deep-learning https://github.com/baidu/mobile-de ...

  3. bzoj-1191(二分图最大匹配)

    解题思路:比较裸的一道题,直接跑匈牙利就行了,但是要注意一点,这个兔崽子是在闯关,一道题回答不出来就没了,直接在题目循环那里加一个else break;就行了!!!; #include<iost ...

  4. 【C/C++】实现数据结构广义表

    1. 广义表的定义     每个元素可以为Atom,原子,也可以为线性表.      线性表的推广.线性表元素有唯一的前驱和后继,为线性表,而广义表是多层次的线性表      表头:第一个元素,可能是 ...

  5. .net core Include问题

    本文章为原创文章,转载请注明出处 当时不知道为什么这样写,可能是突然间脑子停止了转动,既然犯过这样的错误,就记录下来吧 错误示例 ).Include(a=>a.User).Select(a =& ...

  6. mybatis查询修改同时操作

    update dic_purchase set state =0 where purchase_number in (select tmp.* from (select purchase_number ...

  7. Redis——windows下如何连接Linux(centos7.x)虚拟机的Redis——【二】

    我的虚拟网络使用的是桥接网络和windows主机IP为同一网段,做下面步骤之前请确保网络通畅. 使用cmd的ping来测试 软件 https://redisdesktop.com/download 下 ...

  8. MT【249】离心率两题

    椭圆$\dfrac{x^2}{a^2}+\dfrac{y^2}{b^2}=1,(a>b>0)$的一个焦点为$F$,过$F$的直线交椭圆于$A,B$两点,$M$是点$A$关于原点的对称点.若 ...

  9. NOIP2018退役记(记事)

    希望还是要有的,万一退役了呢? Day1 听说300是大众分? 不会T2,不会T3,再别OI. Day2 听说200是大众分? T1FST掉,不会T2,不会T3,再别OI. 茶馆小人书 那些让我们聊以 ...

  10. MVC接收列表参数

    ASP.NET  MVC 表单参数如果有列表时要怎么写呢. 虽然很久不用MVC了,但几乎每次遇到一次就要研究一下.然后又忘了. 其实也明白这是未完全弄清楚表单参数的传递形式,如果明白了,就知道MVC为 ...