Nginx+PHP内存占用分析
2011-11-16 10:07
465 查看
http://www.opensoce.com/?p=1488
php-cgi内存占用netstat -anop | grep "php" | grep -v "grep" | wc -l #check running php-cgi #!/bin/sh while [ 1 ] do for ps1 in `ps -eo pid,fname,rss|grep php-cgi|grep -v grep|awk '{if($3>=32000) print $1}'`; do kill -9 $ps1 done done
php-cgi会在每个请求结束的时候回收脚本使用的全部内存,但是并不会释放给操作系统,而是继续持有以应对下一次PHP请求。这样做大概是为了减少内存碎片化或者解决从系统申请内存之后又释放回操作系统所需要的时间不可控问题。可是如果偶然一次PHP请求使用了诸如ftp或者zlib这样的大内存操作,那么将导致一大块系统内存被php-cgi持续占有,不能被利用。
解决这个问题的办法是在web服务器配置中降低PHP_FCGI_MAX_REQUESTS 的值。这个参数决定了一个php-cgi进程被创建出来之后,最多接受的PHP请求数,在lighttpd中默认配置是10000。也就是说这个php- cgi进程每接受10000次PHP请求后会终止,释放所有内存,并重新被管理进程启动。如果把它降低,比如改成100,那么php-cgi重启的周期会大大缩短,偶然的高内存操作造成的问题影响时间也会缩短。
另一个办法则是写一个crontab脚本,定时发现高内存占用的php-cgi进程并向它传送kill指令。
Nginx内存占用
1 | worker_connections 65535;将会占用25M左右 |
2 | worker_connections 4096;将会占用4M左右 |
static void* spawn_process( void* data ) { FILE* fp; fcgi_program* fcgi = (fcgi_program*)data; int idx = fcgi->spawn_count ++, ret; while( !end ){ #ifdef __WIN32__ STARTUPINFO si={0}; PROCESS_INFORMATION pi={0}; ZeroMemory(&si,sizeof(STARTUPINFO)); si.cb = sizeof(STARTUPINFO); si.dwFlags = STARTF_USESTDHANDLES; si.hStdOutput = INVALID_HANDLE_VALUE; si.hStdInput = (HANDLE)fcgi->listen_fd; si.hStdError = INVALID_HANDLE_VALUE; if( 0 == (ret=CreateProcess(NULL, fcgi->shell, NULL,NULL, TRUE, CREATE_NO_WINDOW , NULL,NULL, &si,&pi)) ){ printf("[fcgi] failed to create process %s, ret=%d\n", fcgi->shell, ret); return NULL; } fcgi->process_fp[idx] = (int)pi.hProcess; printf("[fcgi] starting process %s\n", fcgi->shell ); WaitForSingleObject(pi.hProcess, INFINITE); fcgi->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 (*fcgi->username) { if (NULL == (pwd = getpwnam(fcgi->username))) { printf("[fcgi] %s %s\n", "can't find username", fcgi->username); exit(-1); } if (pwd->pw_uid == 0) { printf("[fcgi] %s\n", "what? dest uid == 0?" ); exit(-1); } } if (*fcgi->groupname) { if (NULL == (grp = getgrnam(fcgi->groupname))) { printf("[fcgi] %s %s\n", "can't find groupname", fcgi->groupname); exit(1); } if (grp->gr_gid == 0) { printf("[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 (fcgi->username) { initgroups(fcgi->username, grp->gr_gid); } } if (*fcgi->changeroot) { if (-1 == chroot(fcgi->changeroot)) { printf("[fcgi] %s %s\n", "can't change root", fcgi->changeroot); exit(1); } if (-1 == chdir("/")) { printf("[fcgi] %s %s\n", "can't change dir to", fcgi->changeroot); exit(1); } } /* drop root privs */ if (*fcgi->username) { printf("[fcgi] setuid to %s\n", fcgi->username); setuid(pwd->pw_uid); } } int max_fd = 0, i=0; // Set stdin to listen_fd close(STDIN_FILENO); dup2(fcgi->listen_fd, STDIN_FILENO); close(fcgi->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(fcgi->shell) + 1); strcpy(b, "exec "); strcat(b, fcgi->shell); /* exec the cgi */ execl("/bin/sh", "sh", "-c", b, (char *)NULL); exit(errno); break; } case -1: printf("[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", fcgi->shell, ret); break; case -1: printf("[fcgi] waitpid failed\n"); return NULL; default: if (WIFEXITED(status)) { printf("[fcgi] child exited with: %d\n", WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) { printf("[fcgi] child signaled: %d\n", WTERMSIG(status)); } else { printf("[fcgi] child died somehow: %d\n", status); } return NULL; } //wait for child process to exit fcgi->process_fp[idx] = ret; waitpid(ret, &status, 0); fcgi->process_fp[idx] = 0; } } #endif } if(!end){ printf("[fcgi] failed to create process %s\n", fcgi->shell ); } return NULL; } static void delete_program( const void* v ) { fcgi_program * fp = (fcgi_program*)v; int i; for( i=0; i<fp->spawn_count; i++ ){ if( fp->process_fp[i] ){ int h = fp->process_fp[i]; printf("[fcgi] terminating %s: %x\n", fp->shell, h ); #ifdef __WIN32__ TerminateProcess((HANDLE)h, 0); #else kill(fp->process_fp[i], SIGKILL); #endif // pthread_join( fp->threads[i], (void**)0 ); } } closesocket( fp->listen_fd ); free( fp ); } #ifdef __WIN32__ #define EXPORT __declspec(dllexport) __stdcall #else #define EXPORT #endif int EXPORT plugin_entry( webserver* srv ) { pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setstacksize(&attr, 64*1024); //64KB //load fcgi program information loop_create( &loop_program, MAX_FCGI_PROGRAM, delete_program ); if( xml_redirect( srv->config, "/fastCGI/program", 0 ) ){ do{ int spawn_count, i; fcgi_program * fcgip; fcgip = (fcgi_program*) malloc( sizeof(fcgi_program) ); memset( fcgip, 0, sizeof(fcgi_program) ); loop_push_to_head( &loop_program, (void*)fcgip ); strncpy( fcgip->extension, xml_readstr(srv->config, ":extension"), 63 ); strncpy( fcgip->username, xml_readstr( srv->config, "spawnProcess:user" ), 31 ); strncpy( fcgip->groupname, xml_readstr( srv->config, "spawnProcess:group" ), 31 ); strncpy( fcgip->changeroot, xml_readstr( srv->config, "spawnProcess:root" ), 127 ); strncpy( fcgip->shell, xml_readstr( srv->config, "spawnProcess:path" ), 127 ); spawn_count = xml_readnum(srv->config, "spawnProcess:number"); strncpy( fcgip->server_addr, xml_readstr( srv->config, "serverAddress" ), 63 ); fcgip->server_port = xml_readnum(srv->config, "serverPort"); printf("[fcgi] fcgi for %s:%d extension:%s shell:%s\n", fcgip->server_addr, fcgip->server_port, fcgip->extension, fcgip->shell ); fcgip->dest_addr.sin_family = PF_INET; fcgip->dest_addr.sin_addr.s_addr = inet_addr( fcgip->server_addr ); fcgip->dest_addr.sin_port = htons( fcgip->server_port ); /* Check If we need to start the fastcgi process */ if( fcgip->shell[0] ){ fcgip->listen_fd = socket(AF_INET, SOCK_STREAM, 0); if( connect( fcgip->listen_fd, (struct sockaddr*)&fcgip->dest_addr, sizeof(struct sockaddr_in)) != -1 ){ printf("[fcgi] %s:%d is already in use. failed to spawn in.\n", fcgip->server_addr, fcgip->server_port ); closesocket( fcgip->listen_fd ); return -1; } closesocket( fcgip->listen_fd ); //reopen socket fcgip->listen_fd = socket(AF_INET, SOCK_STREAM, 0); i = 1; setsockopt( fcgip->listen_fd, SOL_SOCKET, SO_REUSEADDR, (void*)&i, sizeof(i)); if (-1 == bind( fcgip->listen_fd, (struct sockaddr*)&fcgip->dest_addr, sizeof(struct sockaddr_in)) ) { printf("[fcgi] failed to bind %s:%d\n", fcgip->server_addr, fcgip->server_port ); return -1; } listen(fcgip->listen_fd, srv->max_threads); if( spawn_count < 1 || spawn_count > 64 ) spawn_count = 5; for( i=0; i<spawn_count; i++ ) if( pthread_create( &fcgip->threads[i], &attr, (void*)spawn_process, (void*)fcgip ) == -1 ){ printf("[fcgi] failed to create thread: %d\n", errno); } } }while( xml_movenext( srv->config ) ); } srv->vdir_create( srv, "/", (vdir_handler)fcgi_run ); return 0; } int EXPORT plugin_cleanup( webserver* srv ) { end = 1; loop_cleanup( &loop_program ); return 0; }
相关文章推荐
- PHP数组实际占用内存大小的分析
- PHP查询MySQL大量数据的内存占用分析 【转】
- PHP数组占用很大内存的分析
- 【代码】PHP 分析查询MySQL大量数据的内存占用情况
- PHP数组实际占用内存大小的分析
- PHP查询MySQL大量数据的内存占用分析
- PHP数组实际占用内存大小的分析
- PHP数组实际占用内存大小的分析
- PHP查询MySQL大量数据的内存占用分析
- PHP查询MySQL大量数据的内存占用分析
- PHP查询MySQL大量数据的内存占用分析
- PHP数组实际占用内存大小的分析
- PHP查询MySQL大量数据的时候内存占用分析
- PHP查询MySQL大量数据的内存占用分析
- PHP数组实际占用内存大小的分析
- PHP数组实际占用内存大小的分析与弱语言
- PHP数组实际占用内存大小的分析
- PHP数组实际占用内存大小的分析
- PHP查询MySQL大量数据的内存占用分析
- PHP查询MySQL大量数据的内存占用分析