完美解决windows+ngnix+phpcgi自动退出的问题

 

[摘要]在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进程管理器的操作参数:

 1 Usage: xxfpm path [-n number] [-i ip] [-p port]
 2 Manage FastCGI processes.
 3 
 4 -n, --number number of processes to keep
 5 -i, --ip ip address to bind
 6 -p, --port port to bind, default is 8000
 7 -u, --user start processes using specified linux user
 8 -g, --group start processes using specified linux group
 9 -r, --root change root direcotry for the processes
10 -h, --help output usage information and exit
11 -v, --version output version information and exit
View Code

第一个写得比较标准的终端应用程序,我是看了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来结束子进程。详见源代码:

  1 #ifdef __WIN32__
  2 
  3 #ifndef _WIN32_WINNT
  4 #define _WIN32_WINNT 0x0500
  5 #endif //_WIN32_WINNT
  6 
  7 #include <windows.h>
  8 #include <winsock.h>
  9 #include <wininet.h>
 10 #define SHUT_RDWR SD_BOTH
 11 
 12 #ifndef JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
 13 #define JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE (0x2000)
 14 #endif
 15 HANDLE FcpJobObject;
 16 
 17 #else
 18 
 19 #include <sys/socket.h>
 20 #include <sys/wait.h>
 21 #include <fcntl.h>
 22 #include <arpa/inet.h>
 23 #include <grp.h>
 24 #include <pwd.h>
 25 #include <unistd.h>
 26 #define closesocket close
 27 
 28 #endif //__WIN32__
 29 
 30 
 31 #include <stdio.h>
 32 #include <stdlib.h>
 33 #include <getopt.h>
 34 #include <string.h>
 35 #include <pthread.h>
 36 #include <errno.h>
 37 
 38 #define MAX_PROCESSES 1024
 39 static const char version[] = "$Revision: 0.01 $";
 40 static char* prog_name;
 41 int number = 1;
 42 int port = 8000;
 43 char *ip = "127.0.0.1";
 44 char *user = "";
 45 char *root = "";
 46 char *path = "";
 47 char *group = "";
 48 int listen_fd;
 49 struct sockaddr_in listen_addr;
 50 int process_fp[MAX_PROCESSES];
 51 int process_idx = 0;
 52 pthread_t threads[MAX_PROCESSES];
 53 
 54 static struct option longopts[] =
 55 {
 56  {"help", no_argument, NULL, 'h'},
 57  {"version", no_argument, NULL, 'v'},
 58  {"number", required_argument, NULL, 'n'},
 59  {"ip", required_argument, NULL, 'i'},
 60  {"port", required_argument, NULL, 'p'},
 61  {"user", required_argument, NULL, 'u'},
 62  {"group", required_argument, NULL, 'g'},
 63  {"root", required_argument, NULL, 'r'},
 64  {NULL, 0, NULL, 0}
 65 };
 66 
 67 static char opts[] = "hvnipugr";
 68 
 69 static void usage(FILE* where)
 70 {
 71  fprintf(where, ""
 72   "Usage: %s path [-n number] [-i ip] [-p port]\n"
 73   "Manage FastCGI processes.\n"
 74   "\n"
 75   " -n, --number  number of processes to keep\n"
 76   " -i, --ip      ip address to bind\n"
 77   " -p, --port    port to bind, default is 8000\n"
 78   " -u, --user    start processes using specified linux user\n"
 79   " -g, --group   start processes using specified linux group\n"
 80   " -r, --root    change root direcotry for the processes\n"
 81   " -h, --help    output usage information and exit\n"
 82   " -v, --version output version information and exit\n"
 83   "", prog_name);
 84  exit(where == stderr ? 1:0);
 85 }
 86 
 87 static void print_version()
 88 {
 89  printf("%s %s\n\
 90 FastCGI Process Manager\n\
 91 Copyright 2010 Xiaoxia.org\n\
 92 Compiled on %s\n\
 93 ", prog_name, version, __DATE__);
 94  exit(0);
 95 }
 96 
 97 static int try_to_bind()
 98 {
 99  listen_addr.sin_family = PF_INET;
100  listen_addr.sin_addr.s_addr = inet_addr( ip );
101  listen_addr.sin_port = htons( port );
102  listen_fd = socket(AF_INET, SOCK_STREAM, 0);
103  
104  if (-1 == bind(listen_fd, (struct sockaddr*)&listen_addr, sizeof(struct sockaddr_in)) ) {
105   fprintf(stderr, "failed to bind %s:%d\n", ip, port );
106   return -1;
107  }
108  
109  listen(listen_fd, MAX_PROCESSES);
110  return 0;
111 }
112 
113 static void* spawn_process(void* arg)
114 {
115  int idx = process_idx ++, ret;
116  while(1){
117 #ifdef __WIN32__
118   STARTUPINFO si={0};
119   PROCESS_INFORMATION pi={0};
120   ZeroMemory(&si,sizeof(STARTUPINFO));
121   si.cb = sizeof(STARTUPINFO);
122   si.dwFlags = STARTF_USESTDHANDLES;
123   si.hStdInput  = (HANDLE)listen_fd;
124   si.hStdOutput = INVALID_HANDLE_VALUE;
125   si.hStdError  = INVALID_HANDLE_VALUE;
126   if(0 == (ret=CreateProcess(NULL, path,
127    NULL,NULL,
128    TRUE, CREATE_NO_WINDOW | CREATE_SUSPENDED | CREATE_BREAKAWAY_FROM_JOB,
129    NULL,NULL,
130    &si,&pi)) ){
131    fprintf(stderr, "failed to create process %s, ret=%d\n", path, ret);
132    return NULL;
133   }
134   
135   /* Use Job Control System */
136   if(!AssignProcessToJobObject(FcpJobObject, pi.hProcess)){
137    TerminateProcess(pi.hProcess, 1);
138    CloseHandle(pi.hProcess);
139    CloseHandle(pi.hThread);
140    return NULL;
141   }
142   
143   if(!ResumeThread(pi.hThread)){
144    TerminateProcess(pi.hProcess, 1);
145    CloseHandle(pi.hProcess);
146    CloseHandle(pi.hThread);
147    return NULL;
148   }
149   
150   process_fp[idx] = (int)pi.hProcess;
151   WaitForSingleObject(pi.hProcess, INFINITE);
152   process_fp[idx] = 0;
153   CloseHandle(pi.hThread);
154 #else
155   ret = fork();
156   switch(ret){
157   case 0:{ //child
158    /* change uid from root to other user */
159    if(getuid()==0){ 
160                 struct group *grp = NULL;
161                 struct passwd *pwd = NULL;
162     if (*user) {
163      if (NULL == (pwd = getpwnam(user))) {
164       fprintf(stderr, "[fcgi] %s %s\n", "can't find username", user);
165       exit(-1);
166      }
167 
168      if (pwd->pw_uid == 0) {
169       fprintf(stderr, "[fcgi] %s\n", "what? dest uid == 0?" );
170       exit(-1);
171      }
172     }
173 
174     if (*group) {
175      if (NULL == (grp = getgrnam(group))) {
176       fprintf(stderr, "[fcgi] %s %s\n", "can't find groupname", group);
177       exit(1);
178      }
179 
180      if (grp->gr_gid == 0) {
181       fprintf(stderr, "[fcgi] %s\n", "what? dest gid == 0?" );
182       exit(1);
183      }
184      /* do the change before we do the chroot() */
185      setgid(grp->gr_gid);
186      setgroups(0, NULL);
187 
188      if (user) {
189       initgroups(user, grp->gr_gid);
190      }
191     }
192     if (*root) {
193      if (-1 == chroot(root)) {
194       fprintf(stderr, "[fcgi] %s %s\n", "can't change root", root);
195       exit(1);
196      }
197      if (-1 == chdir("/")) {
198       fprintf(stderr, "[fcgi] %s %s\n", "can't change dir to", root);
199       exit(1);
200      }
201     }
202 
203     /* drop root privs */
204     if (*user) {
205      setuid(pwd->pw_uid);
206     }
207    }
208    
209    int max_fd = 0, i=0;
210    // Set stdin to listen_fd
211    close(STDIN_FILENO);
212    dup2(listen_fd, STDIN_FILENO);
213    close(listen_fd);
214    // Set stdout and stderr to dummy fd
215    max_fd = open("/dev/null", O_RDWR);
216    close(STDERR_FILENO);
217    dup2(max_fd, STDERR_FILENO);
218    close(max_fd);
219    max_fd = open("/dev/null", O_RDWR);
220    close(STDOUT_FILENO);
221    dup2(max_fd, STDOUT_FILENO);
222    close(max_fd);
223    // close other handles
224    for(i=3; i<max_fd; i++)
225     close(i);
226    char *b = malloc(strlen("exec ") + strlen(path) + 1);
227    strcpy(b, "exec ");
228    strcat(b, path);
229    
230    /* exec the cgi */
231    execl("/bin/sh", "sh", "-c", b, (char *)NULL);
232    exit(errno);
233    break;
234   }
235   case -1:
236    fprintf(stderr, "[fcgi] fork failed\n");
237    return NULL;
238   default:{
239    struct timeval tv = { 0, 100 * 1000 };
240    int status;
241    select(0, NULL, NULL, NULL, &tv);
242    switch(waitpid(ret, &status, WNOHANG)){
243    case 0:
244     printf("[fcg] spawned process %s: %d\n", path, ret);
245     break;
246    case -1:
247     fprintf(stderr, "[fcgi] waitpid failed\n");
248     return NULL;
249    default:
250     if (WIFEXITED(status)) {
251       fprintf(stderr, "[fcgi] child exited with: %d\n", WEXITSTATUS(status));
252     } else if (WIFSIGNALED(status)) {
253       fprintf(stderr, "[fcgi] child signaled: %d\n", WTERMSIG(status));
254     } else {
255       fprintf(stderr, "[fcgi] child died somehow: %d\n", status);
256     }
257     return NULL;
258    }
259    //wait for child process to exit
260    process_fp[idx] = ret;
261    waitpid(ret, &status, 0);
262    process_fp[idx] = 0;
263   }
264   }
265 #endif
266  }
267 }
268 
269 static int start_processes()
270 {
271  int i;
272  pthread_attr_t attr;
273  pthread_attr_init(&attr);
274  pthread_attr_setstacksize(&attr, 64*1024); //64KB
275  for(i=0; i<number; i++){
276   if( pthread_create( &threads, &attr, spawn_process, NULL ) == -1 ){
277    fprintf(stderr, "failed to create thread %d\n", i);
278   }
279  }
280  
281  for(i=0; i<number; i++){
282   pthread_join(threads, NULL);
283  }
284  return 0;
285 }
286 
287 #ifdef __WIN32__
288 void init_win32()
289 {
290  /* init win32 socket */
291  static WSADATA wsa_data; 
292  if(WSAStartup((WORD)(1<<8|1), &wsa_data) != 0)
293   exit(1);
294  JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit;
295  FcpJobObject = (HANDLE)CreateJobObject(NULL, NULL);
296  if(FcpJobObject == NULL) 
297   exit(1);
298  
299  /* let all processes assigned to this job object
300   * being killed when the job object closed */
301  if (!QueryInformationJobObject(FcpJobObject, JobObjectExtendedLimitInformation, &limit, sizeof(limit), NULL)) {
302   CloseHandle(FcpJobObject);
303   exit(1);
304  }
305 
306  limit.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
307 
308  if (!SetInformationJobObject(FcpJobObject, JobObjectExtendedLimitInformation, &limit, sizeof(limit))) {
309   CloseHandle(FcpJobObject);
310   exit(1);
311  }
312 }
313 #endif //__WIN32__
314 
315 #ifndef __WIN32__
316 void before_exit(int sig)
317 {
318  signal(SIGTERM, SIG_DFL);
319  /* call child processes to exit */
320  kill(0, SIGTERM);
321 }
322 #endif
323 
324 int main(int argc, char **argv)
325 {
326  prog_name = strrchr(argv[0], '/');
327  if(prog_name == NULL)
328   prog_name = strrchr(argv[0], '\\');
329  if(prog_name == NULL)
330   prog_name = argv[0];
331  else
332   prog_name++;
333  
334  if(argc == 1)
335   usage(stderr);
336  
337  path = argv[1];
338  
339  opterr = 0;
340  
341  char* p;
342 
343  for(;;){
344   int ch;
345   if((ch = getopt_long(argc, argv, opts, longopts, NULL)) == EOF)
346    break;
347   char *av = argv[optind];
348   switch(ch){
349   case 'h':
350    usage(stdout);
351    break;
352   case 'v':
353    print_version();
354    break;
355   case 'n':
356    number = atoi(av);
357    if(number > MAX_PROCESSES){
358     fprintf(stderr, "exceeds MAX_PROCESSES!\n");
359     number = MAX_PROCESSES;
360    }
361    break;
362   case 'u':
363    user = av;
364    break;
365   case 'r':
366    root = av;
367    break;
368   case 'g':
369    group = av;
370    break;
371   case 'i':
372    ip = av;
373    break;
374   case 'p':
375    port = atoi(av);
376    break;
377   default:
378    usage(stderr);
379    break;
380   }
381  }
382 
383 #ifdef __WIN32__
384  init_win32();
385 #else
386  /* call child processes to exit */
387  signal(SIGTERM, before_exit);
388  signal(SIGINT, before_exit);
389  signal(SIGABRT, before_exit);
390 #endif
391  
392  int ret;
393  ret = try_to_bind();
394  if(ret != 0)
395   return ret;
396  ret = start_processes();
397  if(ret !=0)
398   return ret;
399  
400  
401 #ifdef __WIN32__
402  CloseHandle(FcpJobObject);
403  WSACleanup();
404 #endif
405  return 0;
406 }
View Code

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

posted @ 2016-05-19 09:37  fogwu  阅读(7260)  评论(0编辑  收藏  举报