为什么忽略 SIGCONT 仍然会使进程继续?

Mar*_*ity 2 c linux posix signals linux-kernel

这是我的代码,忽略SIGCONT

int main() {
    signal(SIGCONT, SIG_IGN);
    while(1);
}
Run Code Online (Sandbox Code Playgroud)

发生的情况是这样的:

> ./main &
[1] 25093
> kill -STOP 25093

[1]+  Stopped                 ./main
> ps -aux | grep 25093
xxx    25093 98.6  0.0   2488   872 pts/8    T    18:23   0:20 ./main
> kill -CONT 25093
> ps -aux | grep 25093
xxx    25093 52.1  0.0   2488   872 pts/8    R    18:23   0:28 ./main
Run Code Online (Sandbox Code Playgroud)
  1. 看来这SIGCONT仍然让我的过程继续下去。这是否意味着 的处理程序SIGCONT只是一个“副作用”?

  2. 我想知道什么时候SIGCONT该进程会再次运行?进程什么时候再次放入调度队列?是在kill执行系统调用时还是在调度进程时?(我读到一篇关于Linux信号的文章,指出调度代码没有SIGCONT特殊对待。代码段如下所示。)

> ./main &
[1] 25093
> kill -STOP 25093

[1]+  Stopped                 ./main
> ps -aux | grep 25093
xxx    25093 98.6  0.0   2488   872 pts/8    T    18:23   0:20 ./main
> kill -CONT 25093
> ps -aux | grep 25093
xxx    25093 52.1  0.0   2488   872 pts/8    R    18:23   0:28 ./main
Run Code Online (Sandbox Code Playgroud)

Mar*_*lli 6

SIGCONT这是POSIX 标准的预期行为。引用POSIX.1-2017 第 2 章第 2.4 节“信号概念”第 2.4.1 小节:

SIGCONT为停止的进程生成 时,进程应继续,即使该SIGCONT信号被进程忽略或被进程内的所有线程阻塞并且没有线程调用函数sigwait()选择SIGCONT如果SIGCONT被进程内的所有线程阻塞,并且没有线程在调用函数sigwait()选择SIGCONT,并且SIGCONT未被进程忽略,则SIGCONT信号应在进程上保持挂起状态,直到它被线程解除阻塞或线程调用为进程或进程内的任何线程生成一个sigwait()函数选择或停止信号。SIGCONT

您无法阻止SIGCONT流程的恢复执行。您最多能做的就是阻止它的传递,这意味着如果您添加SIGCONT到阻止的信号集中,您的进程将不会“注意到”它(注册的处理程序在解除阻止之前不会运行),但它仍然会恢复执行。


在Linux 中,“继续”操作SIGCONT在信号生成时立即执行,即在kill系统调用期间执行。这是在检查信号是否被阻止或忽略之前完成的。负责此操作的代码位于prepare_signal()

/*
 * Handle magic process-wide effects of stop/continue signals. Unlike
 * the signal actions, these happen immediately at signal-generation
 * time regardless of blocking, ignoring, or handling.  This does the
 * actual continuing for SIGCONT, but not the actual stopping for stop
 * signals. The process stop is done as a signal action for SIG_DFL.
 *
 * Returns true if the signal should be actually delivered, otherwise
 * it should be dropped.
 */
static bool prepare_signal(int sig, struct task_struct *p, bool force)
{
    // ...

    } else if (sig == SIGCONT) {
        unsigned int why;
        /*
         * Remove all stop signals from all queues, wake all threads.
         */
        siginitset(&flush, SIG_KERNEL_STOP_MASK);
        flush_sigqueue_mask(&flush, &signal->shared_pending);
        for_each_thread(p, t) {
            flush_sigqueue_mask(&flush, &t->pending);
            task_clear_jobctl_pending(t, JOBCTL_STOP_PENDING);
            if (likely(!(t->ptrace & PT_SEIZED))) {
                t->jobctl &= ~JOBCTL_STOPPED;
                wake_up_state(t, __TASK_STOPPED);
            } else
                ptrace_trap_notify(t);
        }

    // ...
}
Run Code Online (Sandbox Code Playgroud)