window下通过cmd运行命令

1
2
3
4
5
#后台运行,关闭cmd窗口后程序退出
start /b xxx.exe

#隐藏窗口运行命令
%windir%\system32\mshta.exe vbscript:createobject("wscript.shell").run("D:\\xxx.exe",0)(window.close)

vbs文件实现

1
2
#创建一个vbs文件,内容如下,把该vbs放入php-cgi.exe的上一级目录,双击即可在后台启动php-cgi.exe。
set wscriptObj = CreateObject("Wscript.Shell") wscriptObj.run "php\php-cgi -b 127.0.0.1:9000",0

有些程序在命令行下运行的时候,当我们关闭命令行窗口以后,程序也关闭了。
比如php-cgi的进程,我们在命令行下运行D:\php\php-cgi.exe -b 127.0.0.1:9000,命令行窗口一直开着,当我们关闭命令行窗口或按CTRL+C的时候,进程php-cgi也就关了。

有些人通过RunHiddenConsole.exe来让php-cgi.exe在后台运行,命令如下:php\RunHiddenConsole.exe php\php-cgi.exe -b 127.0.0.1:9000 -c php\php.ini

有些人用nircmd.exe。运行的命令如下:nircmd.exe exec hide ....\php\php-cgi.exe -b 127.0.0.1:9000 -c ....\php\php.ini

windows下的Nginx和php搭配 php-cgi.exe自动关闭退出解决方法

php-cgi.exe在windows+nginx平台下经常自动退出,网上搜到的大部分解决方法都是类似上面的批处理(代码如下)文件临时解决一下,但如果用户在网站登录的话,用户就会突然挂掉。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#一个批处理文件
@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进程管理器xxfpm,该进程管理器需要用到pthreadGC2.dll。源码和编译文件在本文结尾提供下载。经测试,支持Win32和Linux-x86平台。对于用php的人,有了这个东西来维护一定数量的进程,就能制服经常崩溃退出的php-cgi啦!
xxfpm的github地址

报错缺少pthreadgc2.dll

下载里找到 pthreadGC2.dll

将这个文件复制到C:/Windows/SysWOW64目录(如果系统是32位的,将dll文件复制到C:\Windows\System32下);

然后打开”开始-运行-输入regsvr32 pthreadgc2.dll,回车,如果报错,用下面的方法。

新建一个文件 registe.bat,和pthreadgc2.dll同目录

1
2
3
4
5
@echo 开始注册
copy pthreadgc2.dll %windir%\SysWOW64\
regsvr32 %windir%\SysWOW64\pthreadgc2.dll /s
@echo pthreadgc2.dll注册成功
@pause

运行registe.bat,便会完成pthreadgc2.dll注册(如果32位的系统,请将system32替换为system32)。完了就可以将这个文件删了(不是删除windows目录下哈)

1
2
3
4
5
6
7
8
9
10
11
12
# 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,算是写得比较标准的,但是代码也不短。

使用例子:

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
#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;
}