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

Shell程序信号处理代码Bug排查技术求助

Got it, let's break down what might be going wrong with your signal handling code here. I've worked through similar headaches when building custom shells, so let's walk through this step by step.

核心问题分析

First, let's clarify what your code is trying to do: it looks like you're blocking SIGINT (Ctrl+C) and SIGTSTP (Ctrl+Z) before forking a child, then trying to restore the parent's signal mask afterward. But there are a few critical missteps here that are likely causing your bugs.

可能的问题点与排查方向

  • 子进程继承了阻塞的信号掩码,exec后无法响应终端信号
    When you call sigprocmask(SIG_BLOCK, ...) before fork(), the child process inherits this exact signal mask. The problem? exec doesn't reset signal masks—so after your child runs exec, it'll still have SIGINT and SIGTSTP blocked. That means users can't stop or pause the child process with keyboard shortcuts, which is a huge issue for a shell.

    • 排查方法: Test if your child process ignores Ctrl+C. If yes, this is definitely the problem. You can also write a small helper function to print the sigset_t mask in the child before exec to confirm the blocked signals.
  • 父进程的信号掩码恢复逻辑冗余且易引发竞态
    Your parent process code does:

    sigprocmask(SIG_UNBLOCK, &new, &old); 
    sigprocmask(SIG_SETMASK, &old, NULL);
    

    This is unnecessary—SIG_SETMASK with old already restores the original mask (since old was saved before you blocked the signals). Doing an unblock first creates a tiny window where signals could be delivered unexpectedly, which might cause race conditions if your parent is handling job control or other signal-sensitive logic.

    • 排查方法: Check if you're running these lines before or after fork(). If it's before fork(), it's completely useless (you're blocking then immediately restoring, wasting cycles). If it's after, just remove the SIG_UNBLOCK line—SIG_SETMASK alone is enough.
  • 缺少子进程的信号掩码恢复步骤
    Even if you block signals in the parent to avoid race conditions during fork, the child must reset its signal mask before calling exec. Otherwise, as I mentioned earlier, the child will be deaf to keyboard signals.

修正后的示例代码

Here's how to adjust your code to fix these issues:

sigset_t new, old; 
sigemptyset(&new); 
sigaddset(&new, SIGINT); 
sigaddset(&new, SIGTSTP); 

// Block signals BEFORE forking to prevent race conditions during process creation
sigprocmask(SIG_BLOCK, &new, &old); 

pid_t pid = fork();
if (pid == 0) {
    // Child process: Restore original signal mask so exec'd program can respond to signals
    sigprocmask(SIG_SETMASK, &old, NULL);

    // Do any pre-exec setup here (I/O redirection, etc.)
    execvp("your_command", args); // Replace with your actual exec call

    // If exec fails, clean up and exit
    perror("exec failed");
    exit(EXIT_FAILURE);
} else if (pid > 0) {
    // Parent process: Restore original signal mask immediately after forking
    sigprocmask(SIG_SETMASK, &old, NULL);

    // Handle parent logic here (wait for child, job control, etc.)
    int status;
    waitpid(pid, &status, 0);
} else {
    // Fork failed: Don't forget to restore the signal mask!
    perror("fork failed");
    sigprocmask(SIG_SETMASK, &old, NULL);
}

额外排查 Tips

  • Use sigpending(&pending_set) in parent/child to check if any signals are pending (could help diagnose if signals are being blocked unexpectedly).
  • If you have custom signal handlers in the parent, note that exec will reset them to default behavior (which is usually what you want for child processes).

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 09:33:58