最近在工作中遇到在服务端程序中需要执行shell命令,并获取执行结果的问题。 由于在服务端程序中一般会设置忽略子进程的信号处理:signal(SIGCHLD, SIG_IGN);
所以如果直接使用system()执行,并获取返回值作为命令执行成功与否的判断,是不合理的,这样会经常返回-1,但是命令实际上已经成功执行了。
网上搜索了一下处理方法:
signal(SIGCHLD, SIG_DFL);
ret = system(...);
signal(SIGCHLD, SIG_IGN);
先设置为默认,执行完命令,再设置为SIG_IGN, 但是程序运行中,发现还是会返回-1,所以这种方法还是不合理的。
后来,又试了下使用popen()来处理:
int ret = -1;
FILE* fp = popen(shell_cmd.c_str(), "r");
if (!fp) {
return -1;
}
int rc = pclose(fp);
ret = WEXITSTATUS(rc);
获取WEXITSTATUS()的返回值,作为命令执行的结果。 测试过程中发现,命令实际执行成功了,但是返回的值却是255。因此,这种方法还是不可行的。
这个问题困扰了我很久,始终找不到正确的方法,后面用了一种方法,经过验证,能正确得到命令的执行结果,但是当需要执行很多命令的时候,发现程序的进程数一直增加,最终导致系统宕掉。下面先说下这种方法的实现。
创建一个pipe, 再fork(), 在子进程中设置信号处理signal(SIGCHLD, SIG_DFL), 然后执行system()获取返回值,把返回值通过pipe()返回给父进程。
int system_ex(std::string const& shell_cmd)
{
int fd[2], retcode = 0;
pid_t pid;
int result = pipe(fd);
if (result == -1) {
return -1;
}
pid = fork();
if (-1 == pid) {
return -1;
} else if (0 == pid) {
signal(SIGCHLD, SIG_DFL);
close(fd[0]);
int ret = system(shell_cmd.c_str());
//int ret = WEXITSTATUS(status);
write(fd[1], &ret, sizeof(ret));
} else {
close(fd[1]);
read(fd[0], &retcode, sizeof(retcode));
}
return retcode;
}
使用上面的代码,会导致程序挂掉,试了很久,后来通过重新popen()的实现过程,终于解决了进程不断增加的问题。
int popen_ex(std::string const& shell_cmd)
{
int ret = 0;
int pid, status;
#ifdef SIGCHLD
signal(SIGCHLD, SIG_DFL);
#endif
switch (pid = vfork()) {
case -1:
return -1;
case 0:
execl("/bin/sh", "sh", "-c", shell_cmd.c_str(), NULL);
_exit(127);
}
waitpid(pid, &status, 0);
ret = WEXITSTATUS(status);
#ifdef SIGCHLD
signal(SIGCHLD, SIG_IGN);
#endif
return ret;
}