×

关注微信公众号

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

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

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

  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啦!

    以下是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 8000
  • -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 = 1;
  • int port = 8000;
  • 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 = 0;
  • 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, 0, NULL, 0}
  • };

  • 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 ? 1:0);
  • }

  • static void print_version()
  • {
  • printf("%s %s\n\
  • FastCGI Process Manager\n\
  • Copyright 2010 Xiaoxia.org\n\
  • Compiled on %s\n\
  • ", prog_name, version, __DATE__);
  • exit(0);
  • }

  • 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, 0);

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

  • listen(listen_fd, MAX_PROCESSES);
  • return 0;
  • }

  • static void* spawn_process(void* arg)
  • {
  • int idx = process_idx ++, ret;
  • while(1){
  • #ifdef __WIN32__
  •   STARTUPINFO si={0};
  •   PROCESS_INFORMATION pi={0};
  •   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(0 == (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, 1);
  •    CloseHandle(pi.hProcess);
  •    CloseHandle(pi.hThread);
  •    return NULL;
  •   }
  •   
  •   if(!ResumeThread(pi.hThread)){
  •    TerminateProcess(pi.hProcess, 1);
  •    CloseHandle(pi.hProcess);
  •    CloseHandle(pi.hThread);
  •    return NULL;
  •   }
  •   
  •   process_fp[idx] = (int)pi.hProcess;
  •   WaitForSingleObject(pi.hProcess, INFINITE);
  •   process_fp[idx] = 0;
  •   CloseHandle(pi.hThread);
  • #else
  •   ret = fork();
  •   switch(ret){
  •   case 0:{ //child
  •    /* change uid from root to other user */
  •    if(getuid()==0){
  •                 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(-1);
  •      }

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

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

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

  •      if (user) {
  •       initgroups(user, grp->gr_gid);
  •      }
  •     }
  •     if (*root) {
  •      if (-1 == chroot(root)) {
  •       fprintf(stderr, "[fcgi] %s %s\n", "can't change root", root);
  •       exit(1);
  •      }
  •      if (-1 == chdir("/")) {
  •       fprintf(stderr, "[fcgi] %s %s\n", "can't change dir to", root);
  •       exit(1);
  •      }
  •     }

  •     /* drop root privs */
  •     if (*user) {
  •      setuid(pwd->pw_uid);
  •     }
  •    }
  •    
  •    int max_fd = 0, i=0;
  •    // 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=3; i<max_fd; i++)
  •     close(i);
  •    char *b = malloc(strlen("exec ") + strlen(path) + 1);
  •    strcpy(b, "exec ");
  •    strcat(b, path);
  •    
  •    /* exec the cgi */
  •    execl("/bin/sh", "sh", "-c", b, (char *)NULL);
  •    exit(errno);
  •    break;
  •   }
  •   case -1:
  •    fprintf(stderr, "[fcgi] fork failed\n");
  •    return NULL;
  •   default:{
  •    struct timeval tv = { 0, 100 * 1000 };
  •    int status;
  •    select(0, NULL, NULL, NULL, &tv);
  •    switch(waitpid(ret, &status, WNOHANG)){
  •    case 0:
  •     printf("[fcg] spawned process %s: %d\n", path, ret);
  •     break;
  •    case -1:
  •     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, 0);
  •    process_fp[idx] = 0;
  •   }
  •   }
  • #endif
  • }
  • }

  • static int start_processes()
  • {
  • int i;
  • pthread_attr_t attr;
  • pthread_attr_init(&attr);
  • pthread_attr_setstacksize(&attr, 64*1024); //64KB
  • for(i=0; i<number; i++){
  •   if( pthread_create( &threads, &attr, spawn_process, NULL ) == -1 ){
  •    fprintf(stderr, "failed to create thread %d\n", i);
  •   }
  • }

  • for(i=0; i<number; i++){
  •   pthread_join(threads, NULL);
  • }
  • return 0;
  • }

  • #ifdef __WIN32__
  • void init_win32()
  • {
  • /* init win32 socket */
  • static WSADATA wsa_data;
  • if(WSAStartup((WORD)(1<<8|1), &wsa_data) != 0)
  •   exit(1);
  • JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit;
  • FcpJobObject = (HANDLE)CreateJobObject(NULL, NULL);
  • if(FcpJobObject == NULL)
  •   exit(1);

  • /* 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(1);
  • }

  • limit.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;

  • if (!SetInformationJobObject(FcpJobObject, JobObjectExtendedLimitInformation, &limit, sizeof(limit))) {
  •   CloseHandle(FcpJobObject);
  •   exit(1);
  • }
  • }
  • #endif //__WIN32__

  • #ifndef __WIN32__
  • void before_exit(int sig)
  • {
  • signal(SIGTERM, SIG_DFL);
  • /* call child processes to exit */
  • kill(0, SIGTERM);
  • }
  • #endif

  • int main(int argc, char **argv)
  • {
  • prog_name = strrchr(argv[0], '/');
  • if(prog_name == NULL)
  •   prog_name = strrchr(argv[0], '\\');
  • if(prog_name == NULL)
  •   prog_name = argv[0];
  • else
  •   prog_name++;

  • if(argc == 1)
  •   usage(stderr);

  • path = argv[1];

  • opterr = 0;

  • 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 != 0)
  •   return ret;
  • ret = start_processes();
  • if(ret !=0)
  •   return ret;


  • #ifdef __WIN32__
  • CloseHandle(FcpJobObject);
  • WSACleanup();
  • #endif
  • return 0;
  • }
  • 复制代码

    本帖子中包含更多资源

    您需要 登录 才可以下载或查看,没有帐号?[立即注册]
    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 | 只看该作者
    今个儿真高兴,真嘛真高兴.
    回复 支持 反对 使用道具 举报
    您需要登录后才可以回帖 登录 | [立即注册]
    本版积分规则
    mr007