You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

执行外部命令的函数因耗时过长遭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;
}

说明

  1. 超时时间通过alarm(10)设置为10秒,可根据作业要求调整数值。
  2. 子进程重置信号是为了保证外部命令能正常响应自身的信号(比如用户给子进程发SIGINT),不受父进程处理逻辑影响。
  3. 循环中用usleep减少空转带来的CPU消耗,同时WNOHANG让waitpid非阻塞,方便检查信号状态。

内容的提问来源于stack exchange,提问作者Evelyne

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 08:35:20