执行外部命令的函数因耗时过长遭SIGALRM中断,求技术帮助
问题描述
我正在为完成作业编写一个执行外部命令的函数,以下是我的代码。目前遇到的问题是程序运行耗时过长,被SIGALRM信号中断。恳请各位提供帮助!
volatile sig_atomic_t sig = 0; void ext(int signum){ if(signum==SIGINT || signum==SIGTERM) sig=1; } int extern(char **line){ pid_t p; int status; struct sigaction as = {0}; as.sa_handler=ext; if (sigaction(SIGINT, &as, 0)==-1||sigaction(SIGTERM, &as, 0)==-1){ perror("sigaction"); exit(1); } switch(p=fork()){ case -1: perror("fork"); exit(1); case 0 : if(execvp(line[0], line)<0) {perror("exec"); exit(1);} break; default : //EDIT if(waitpid(p, &status, 0)>=0){ if (WIFEXITED(status)) return WEXITSTATUS(status); } else { perror("wait"); exit(1); } break; } return 0; }
解决方案
你的代码核心问题是未处理SIGALRM信号,同时waitpid处于无限阻塞状态,一旦系统触发SIGALRM就会直接中断程序。以下是针对性的修改方案:
关键修改点
- 新增SIGALRM信号处理:让信号处理函数响应SIGALRM,触发时标记全局变量
sig。 - 修复关键字冲突:
extern是C语言关键字,不能用作函数名,改为extern_cmd。 - 子进程信号重置:子进程会继承父进程的信号处理规则,需要重置为默认,避免信号干扰外部命令执行。
- 超时控制逻辑:用
alarm()设置超时时间,配合waitpid(WNOHANG)循环等待,避免无限阻塞,超时后主动终止子进程并回收资源。
修改后的完整代码
#include <signal.h> #include <unistd.h> #include <sys/wait.h> #include <stdio.h> #include <stdlib.h> volatile sig_atomic_t sig = 0; void ext(int signum){ if(signum == SIGINT || signum == SIGTERM || signum == SIGALRM) { sig = 1; } } int extern_cmd(char **line) { pid_t p; int status; struct sigaction as = {0}; as.sa_handler = ext; // 注册所有需要处理的信号 if (sigaction(SIGINT, &as, NULL) == -1 || sigaction(SIGTERM, &as, NULL) == -1 || sigaction(SIGALRM, &as, NULL) == -1) { perror("sigaction"); exit(1); } switch(p = fork()) { case -1: perror("fork"); exit(1); case 0: // 子进程重置信号处理为默认,避免父进程的信号规则干扰外部命令 signal(SIGINT, SIG_DFL); signal(SIGTERM, SIG_DFL); signal(SIGALRM, SIG_DFL); if(execvp(line[0], line) < 0) { perror("exec"); exit(1); } break; default: // 设置超时时间,这里设为10秒,可根据需求调整 alarm(10); // 循环等待子进程,直到子进程结束或收到中断信号 while (!sig) { int ret = waitpid(p, &status, WNOHANG); if (ret > 0) { alarm(0); // 取消未触发的闹钟 if (WIFEXITED(status)) { return WEXITSTATUS(status); } break; } else if (ret == -1) { perror("waitpid"); alarm(0); exit(1); } usleep(100000); // 短暂休眠,降低CPU占用 } // 若因SIGALRM超时,终止子进程并回收 if (sig) { kill(p, SIGKILL); waitpid(p, &status, 0); fprintf(stderr, "命令执行超时,已终止\n"); return -1; } break; } return 0; }
说明
- 超时时间通过
alarm(10)设置为10秒,可根据作业要求调整数值。 - 子进程重置信号是为了保证外部命令能正常响应自身的信号(比如用户给子进程发SIGINT),不受父进程处理逻辑影响。
- 循环中用
usleep减少空转带来的CPU消耗,同时WNOHANG让waitpid非阻塞,方便检查信号状态。
内容的提问来源于stack exchange,提问作者Evelyne
相关产品推荐
相关产品推荐

