×

关注微信公众号

免备案网站空间虚拟主机双线空间域名查询PS数码后期
photoshop互助课堂数百G视频教程下载英语培训机构初中英语如何学随时随地聆听大师开讲/课堂
路径专辑手绘教程抠图教程酷素材photoshop cs3视频教程
查看: 15746|回复: 27
打印 上一主题 下一主题

[PHP] windows下解决Nginx和php搭配 php-cgi.exe自动关闭退出的问题

  [复制链接]
跳转到指定楼层
楼主
发表于 2012-11-8 14:16:33 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 寅生 于 2012-11-8 14:18 编辑

  php-cgi.exe在windows+nginx平台下经常自动退出,网上搜到的大部分解决方法都是类似上面的批处理(代码如下)文件临时解决一下,但如果用户在网站登录的话,用户就会突然挂掉。
  1. @echo off
  2. :main
  3. set jinchengshuliang=0
  4. set jinchengshuliangxiaxian=2
  5. for /f %%i in ('tasklist /nh^|findstr /i /s /c:"php-cgi.exe"') do set /a jinchengshuliang+=1

  6. if %jinchengshuliang% lss %jinchengshuliangxiaxian% (   
  7. goto youwenti
  8. ) else (
  9. goto meiwenti
  10. )  

  11. :youwenti
  12. echo 进程丢失,现在添加5个进程
  13. RunHiddenConsole.exe  php\php-cgi.exe -b 127.0.0.1:9000 -c php\php.ini
  14. RunHiddenConsole.exe  php\php-cgi.exe -b 127.0.0.1:9000 -c php\php.ini
  15. RunHiddenConsole.exe  php\php-cgi.exe -b 127.0.0.1:9000 -c php\php.ini
  16. RunHiddenConsole.exe  php\php-cgi.exe -b 127.0.0.1:9000 -c php\php.ini
  17. RunHiddenConsole.exe  php\php-cgi.exe -b 127.0.0.1:9000 -c php\php.ini
  18. ping 127.1 -n 8
  19. goto main

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

以下是xxfpm进程管理器的操作参数:
  1. Usage: xxfpm path [-n number] [-i ip] [-p port]
  2. Manage FastCGI processes.

  3. -n, --number number of processes to keep
  4. -i, --ip ip address to bind
  5. -p, --port port to bind, default is 8000
  6. -u, --user start processes using specified linux user
  7. -g, --group start processes using specified linux group
  8. -r, --root change root direcotry for the processes
  9. -h, --help output usage information and exit
  10. -v, --version output version information and exit
复制代码
第一个写得比较标准的终端应用程序,我是看了cygwin的里的一些源代码,然后学会了如何使用getopt,算是写得比较标准的,但是代码也不短。

使用例子:
  1. xxfpm z:/php5/php-cgi.exe -n 5 -p 8080
复制代码
有人问,如何给程序加入参数?这个不难,使用双引号即可,路径要用"/"而不用"\"。例如要指定php.ini的路径,可以用下面例子:
  1. 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. #ifndef _WIN32_WINNT
  3. #define _WIN32_WINNT 0x0500
  4. #endif //_WIN32_WINNT

  5. #include <windows.h>
  6. #include <winsock.h>
  7. #include <wininet.h>
  8. #define SHUT_RDWR SD_BOTH

  9. #ifndef JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
  10. #define JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE (0x2000)
  11. #endif
  12. HANDLE FcpJobObject;

  13. #else

  14. #include <sys/socket.h>
  15. #include <sys/wait.h>
  16. #include <fcntl.h>
  17. #include <arpa/inet.h>
  18. #include <grp.h>
  19. #include <pwd.h>
  20. #include <unistd.h>
  21. #define closesocket close

  22. #endif //__WIN32__


  23. #include <stdio.h>
  24. #include <stdlib.h>
  25. #include <getopt.h>
  26. #include <string.h>
  27. #include <pthread.h>
  28. #include <errno.h>

  29. #define MAX_PROCESSES 1024
  30. static const char version[] = "$Revision: 0.01 $";
  31. static char* prog_name;
  32. int number = 1;
  33. int port = 8000;
  34. char *ip = "127.0.0.1";
  35. char *user = "";
  36. char *root = "";
  37. char *path = "";
  38. char *group = "";
  39. int listen_fd;
  40. struct sockaddr_in listen_addr;
  41. int process_fp[MAX_PROCESSES];
  42. int process_idx = 0;
  43. pthread_t threads[MAX_PROCESSES];

  44. static struct option longopts[] =
  45. {
  46. {"help", no_argument, NULL, 'h'},
  47. {"version", no_argument, NULL, 'v'},
  48. {"number", required_argument, NULL, 'n'},
  49. {"ip", required_argument, NULL, 'i'},
  50. {"port", required_argument, NULL, 'p'},
  51. {"user", required_argument, NULL, 'u'},
  52. {"group", required_argument, NULL, 'g'},
  53. {"root", required_argument, NULL, 'r'},
  54. {NULL, 0, NULL, 0}
  55. };

  56. static char opts[] = "hvnipugr";

  57. static void usage(FILE* where)
  58. {
  59. fprintf(where, ""
  60.   "Usage: %s path [-n number] [-i ip] [-p port]\n"
  61.   "Manage FastCGI processes.\n"
  62.   "\n"
  63.   " -n, --number  number of processes to keep\n"
  64.   " -i, --ip      ip address to bind\n"
  65.   " -p, --port    port to bind, default is 8000\n"
  66.   " -u, --user    start processes using specified linux user\n"
  67.   " -g, --group   start processes using specified linux group\n"
  68.   " -r, --root    change root direcotry for the processes\n"
  69.   " -h, --help    output usage information and exit\n"
  70.   " -v, --version output version information and exit\n"
  71.   "", prog_name);
  72. exit(where == stderr ? 1:0);
  73. }

  74. static void print_version()
  75. {
  76. printf("%s %s\n\
  77. FastCGI Process Manager\n\
  78. Copyright 2010 Xiaoxia.org\n\
  79. Compiled on %s\n\
  80. ", prog_name, version, __DATE__);
  81. exit(0);
  82. }

  83. static int try_to_bind()
  84. {
  85. listen_addr.sin_family = PF_INET;
  86. listen_addr.sin_addr.s_addr = inet_addr( ip );
  87. listen_addr.sin_port = htons( port );
  88. listen_fd = socket(AF_INET, SOCK_STREAM, 0);

  89. if (-1 == bind(listen_fd, (struct sockaddr*)&listen_addr, sizeof(struct sockaddr_in)) ) {
  90.   fprintf(stderr, "failed to bind %s:%d\n", ip, port );
  91.   return -1;
  92. }

  93. listen(listen_fd, MAX_PROCESSES);
  94. return 0;
  95. }

  96. static void* spawn_process(void* arg)
  97. {
  98. int idx = process_idx ++, ret;
  99. while(1){
  100. #ifdef __WIN32__
  101.   STARTUPINFO si={0};
  102.   PROCESS_INFORMATION pi={0};
  103.   ZeroMemory(&si,sizeof(STARTUPINFO));
  104.   si.cb = sizeof(STARTUPINFO);
  105.   si.dwFlags = STARTF_USESTDHANDLES;
  106.   si.hStdInput  = (HANDLE)listen_fd;
  107.   si.hStdOutput = INVALID_HANDLE_VALUE;
  108.   si.hStdError  = INVALID_HANDLE_VALUE;
  109.   if(0 == (ret=CreateProcess(NULL, path,
  110.    NULL,NULL,
  111.    TRUE, CREATE_NO_WINDOW | CREATE_SUSPENDED | CREATE_BREAKAWAY_FROM_JOB,
  112.    NULL,NULL,
  113.    &si,&pi)) ){
  114.    fprintf(stderr, "failed to create process %s, ret=%d\n", path, ret);
  115.    return NULL;
  116.   }
  117.   
  118.   /* Use Job Control System */
  119.   if(!AssignProcessToJobObject(FcpJobObject, pi.hProcess)){
  120.    TerminateProcess(pi.hProcess, 1);
  121.    CloseHandle(pi.hProcess);
  122.    CloseHandle(pi.hThread);
  123.    return NULL;
  124.   }
  125.   
  126.   if(!ResumeThread(pi.hThread)){
  127.    TerminateProcess(pi.hProcess, 1);
  128.    CloseHandle(pi.hProcess);
  129.    CloseHandle(pi.hThread);
  130.    return NULL;
  131.   }
  132.   
  133.   process_fp[idx] = (int)pi.hProcess;
  134.   WaitForSingleObject(pi.hProcess, INFINITE);
  135.   process_fp[idx] = 0;
  136.   CloseHandle(pi.hThread);
  137. #else
  138.   ret = fork();
  139.   switch(ret){
  140.   case 0:{ //child
  141.    /* change uid from root to other user */
  142.    if(getuid()==0){
  143.                 struct group *grp = NULL;
  144.                 struct passwd *pwd = NULL;
  145.     if (*user) {
  146.      if (NULL == (pwd = getpwnam(user))) {
  147.       fprintf(stderr, "[fcgi] %s %s\n", "can't find username", user);
  148.       exit(-1);
  149.      }

  150.      if (pwd->pw_uid == 0) {
  151.       fprintf(stderr, "[fcgi] %s\n", "what? dest uid == 0?" );
  152.       exit(-1);
  153.      }
  154.     }

  155.     if (*group) {
  156.      if (NULL == (grp = getgrnam(group))) {
  157.       fprintf(stderr, "[fcgi] %s %s\n", "can't find groupname", group);
  158.       exit(1);
  159.      }

  160.      if (grp->gr_gid == 0) {
  161.       fprintf(stderr, "[fcgi] %s\n", "what? dest gid == 0?" );
  162.       exit(1);
  163.      }
  164.      /* do the change before we do the chroot() */
  165.      setgid(grp->gr_gid);
  166.      setgroups(0, NULL);

  167.      if (user) {
  168.       initgroups(user, grp->gr_gid);
  169.      }
  170.     }
  171.     if (*root) {
  172.      if (-1 == chroot(root)) {
  173.       fprintf(stderr, "[fcgi] %s %s\n", "can't change root", root);
  174.       exit(1);
  175.      }
  176.      if (-1 == chdir("/")) {
  177.       fprintf(stderr, "[fcgi] %s %s\n", "can't change dir to", root);
  178.       exit(1);
  179.      }
  180.     }

  181.     /* drop root privs */
  182.     if (*user) {
  183.      setuid(pwd->pw_uid);
  184.     }
  185.    }
  186.    
  187.    int max_fd = 0, i=0;
  188.    // Set stdin to listen_fd
  189.    close(STDIN_FILENO);
  190.    dup2(listen_fd, STDIN_FILENO);
  191.    close(listen_fd);
  192.    // Set stdout and stderr to dummy fd
  193.    max_fd = open("/dev/null", O_RDWR);
  194.    close(STDERR_FILENO);
  195.    dup2(max_fd, STDERR_FILENO);
  196.    close(max_fd);
  197.    max_fd = open("/dev/null", O_RDWR);
  198.    close(STDOUT_FILENO);
  199.    dup2(max_fd, STDOUT_FILENO);
  200.    close(max_fd);
  201.    // close other handles
  202.    for(i=3; i<max_fd; i++)
  203.     close(i);
  204.    char *b = malloc(strlen("exec ") + strlen(path) + 1);
  205.    strcpy(b, "exec ");
  206.    strcat(b, path);
  207.    
  208.    /* exec the cgi */
  209.    execl("/bin/sh", "sh", "-c", b, (char *)NULL);
  210.    exit(errno);
  211.    break;
  212.   }
  213.   case -1:
  214.    fprintf(stderr, "[fcgi] fork failed\n");
  215.    return NULL;
  216.   default:{
  217.    struct timeval tv = { 0, 100 * 1000 };
  218.    int status;
  219.    select(0, NULL, NULL, NULL, &tv);
  220.    switch(waitpid(ret, &status, WNOHANG)){
  221.    case 0:
  222.     printf("[fcg] spawned process %s: %d\n", path, ret);
  223.     break;
  224.    case -1:
  225.     fprintf(stderr, "[fcgi] waitpid failed\n");
  226.     return NULL;
  227.    default:
  228.     if (WIFEXITED(status)) {
  229.       fprintf(stderr, "[fcgi] child exited with: %d\n", WEXITSTATUS(status));
  230.     } else if (WIFSIGNALED(status)) {
  231.       fprintf(stderr, "[fcgi] child signaled: %d\n", WTERMSIG(status));
  232.     } else {
  233.       fprintf(stderr, "[fcgi] child died somehow: %d\n", status);
  234.     }
  235.     return NULL;
  236.    }
  237.    //wait for child process to exit
  238.    process_fp[idx] = ret;
  239.    waitpid(ret, &status, 0);
  240.    process_fp[idx] = 0;
  241.   }
  242.   }
  243. #endif
  244. }
  245. }

  246. static int start_processes()
  247. {
  248. int i;
  249. pthread_attr_t attr;
  250. pthread_attr_init(&attr);
  251. pthread_attr_setstacksize(&attr, 64*1024); //64KB
  252. for(i=0; i<number; i++){
  253.   if( pthread_create( &threads, &attr, spawn_process, NULL ) == -1 ){
  254.    fprintf(stderr, "failed to create thread %d\n", i);
  255.   }
  256. }

  257. for(i=0; i<number; i++){
  258.   pthread_join(threads, NULL);
  259. }
  260. return 0;
  261. }

  262. #ifdef __WIN32__
  263. void init_win32()
  264. {
  265. /* init win32 socket */
  266. static WSADATA wsa_data;
  267. if(WSAStartup((WORD)(1<<8|1), &wsa_data) != 0)
  268.   exit(1);
  269. JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit;
  270. FcpJobObject = (HANDLE)CreateJobObject(NULL, NULL);
  271. if(FcpJobObject == NULL)
  272.   exit(1);

  273. /* let all processes assigned to this job object
  274.   * being killed when the job object closed */
  275. if (!QueryInformationJobObject(FcpJobObject, JobObjectExtendedLimitInformation, &limit, sizeof(limit), NULL)) {
  276.   CloseHandle(FcpJobObject);
  277.   exit(1);
  278. }

  279. limit.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;

  280. if (!SetInformationJobObject(FcpJobObject, JobObjectExtendedLimitInformation, &limit, sizeof(limit))) {
  281.   CloseHandle(FcpJobObject);
  282.   exit(1);
  283. }
  284. }
  285. #endif //__WIN32__

  286. #ifndef __WIN32__
  287. void before_exit(int sig)
  288. {
  289. signal(SIGTERM, SIG_DFL);
  290. /* call child processes to exit */
  291. kill(0, SIGTERM);
  292. }
  293. #endif

  294. int main(int argc, char **argv)
  295. {
  296. prog_name = strrchr(argv[0], '/');
  297. if(prog_name == NULL)
  298.   prog_name = strrchr(argv[0], '\\');
  299. if(prog_name == NULL)
  300.   prog_name = argv[0];
  301. else
  302.   prog_name++;

  303. if(argc == 1)
  304.   usage(stderr);

  305. path = argv[1];

  306. opterr = 0;

  307. char* p;

  308. for(;;){
  309.   int ch;
  310.   if((ch = getopt_long(argc, argv, opts, longopts, NULL)) == EOF)
  311.    break;
  312.   char *av = argv[optind];
  313.   switch(ch){
  314.   case 'h':
  315.    usage(stdout);
  316.    break;
  317.   case 'v':
  318.    print_version();
  319.    break;
  320.   case 'n':
  321.    number = atoi(av);
  322.    if(number > MAX_PROCESSES){
  323.     fprintf(stderr, "exceeds MAX_PROCESSES!\n");
  324.     number = MAX_PROCESSES;
  325.    }
  326.    break;
  327.   case 'u':
  328.    user = av;
  329.    break;
  330.   case 'r':
  331.    root = av;
  332.    break;
  333.   case 'g':
  334.    group = av;
  335.    break;
  336.   case 'i':
  337.    ip = av;
  338.    break;
  339.   case 'p':
  340.    port = atoi(av);
  341.    break;
  342.   default:
  343.    usage(stderr);
  344.    break;
  345.   }
  346. }

  347. #ifdef __WIN32__
  348. init_win32();
  349. #else
  350. /* call child processes to exit */
  351. signal(SIGTERM, before_exit);
  352. signal(SIGINT, before_exit);
  353. signal(SIGABRT, before_exit);
  354. #endif

  355. int ret;
  356. ret = try_to_bind();
  357. if(ret != 0)
  358.   return ret;
  359. ret = start_processes();
  360. if(ret !=0)
  361.   return ret;


  362. #ifdef __WIN32__
  363. CloseHandle(FcpJobObject);
  364. WSACleanup();
  365. #endif
  366. return 0;
  367. }
复制代码
本帖的地址:http://www.jcwcn.com/forum.php?mod=viewthread&tid=517215
跟着教程做一遍,做完的图要到这里评论交作业,教程有看不懂的地方,可以在贴子下面评论

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?[立即注册]

x
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友 微信微信
收藏收藏1 分享分享 顶1 踩4
沙发
发表于 2012-11-22 08:53:14 | 只看该作者
酷素材
非常感谢楼主了

非常感谢楼主了
回复 支持 反对

使用道具 举报

板凳
发表于 2012-11-29 08:35:24 | 只看该作者
看看学习
回复 支持 反对

使用道具 举报

地板
发表于 2012-12-31 19:21:05 | 只看该作者
   谢咯。   哈哈。
回复 支持 反对

使用道具 举报

5
发表于 2013-1-4 18:19:13 | 只看该作者
酷素材
拿分走人
回复 支持 反对

使用道具 举报

6
发表于 2013-1-4 19:45:04 | 只看该作者
酷素材
回复 支持 反对

使用道具 举报

7
发表于 2013-8-4 11:24:47 | 只看该作者
挺好的,有帮助。谢谢!
回复 支持 反对

使用道具 举报

8
发表于 2013-10-5 09:54:09 | 只看该作者
非常感谢楼主了
回复 支持 反对

使用道具 举报

9
发表于 2013-10-5 09:54:38 | 只看该作者
南国紫藤 发表于 2012-11-22 08:53
非常感谢楼主了

非常感谢楼主了

非常感谢楼主了
回复 支持 反对

使用道具 举报

10
发表于 2013-12-13 17:26:59 | 只看该作者
〉〉〉〉〉〉

前端群 HTML[5] + CSS + JS (12519289),期待你的加入,大家一起成长,共同进步!

《〈〈〈〈〈
回复 支持 反对

使用道具 举报

11
发表于 2014-2-2 08:26:59 | 只看该作者
非常感谢楼主了
回复 支持 反对

使用道具 举报

12
发表于 2014-10-15 01:37:52 | 只看该作者
楼主加油,我代表教程网支持你。。。
回复 支持 反对

使用道具 举报

13
发表于 2014-10-16 18:40:29 | 只看该作者
楼主加油,我代表教程网支持你。。。
回复 支持 反对

使用道具 举报

14
发表于 2014-10-20 05:12:36 | 只看该作者
楼主加油,我代表教程网支持你。。。
回复 支持 反对

使用道具 举报

15
发表于 2014-10-22 06:05:01 | 只看该作者
今个儿真高兴,真嘛真高兴.
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | [立即注册]

本版积分规则

2345