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

wljmcqd8  于 2023-01-12  发布在  其他
关注(0)|答案(1)|浏览(110)

这是我的代码,忽略SIGCONT

int main() {
    signal(SIGCONT, SIG_IGN);
    while(1);
}

这是发生了什么:

> ./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

1.看起来SIGCONT仍然让我的进程继续,这是否意味着SIGCONT的句柄只是一个“副作用”?
1.不知道SIGCONT什么时候让进程重新运行,什么时候又把进程放到调度队列中,是kill系统调用的时候,还是要调度的时候(我看过一篇关于Linux信号的文章,上面说调度代码并没有特别对待SIGCONT,代码段如下)。

if (ka->sa.sa_handler == SIG_DFL) { 
  int exit_code = signr; 
 
  /* Init gets no signals it doesn't want.  */ 
  if (current->pid == 1) 
   continue; 
 
  switch (signr) { 
  case SIGCONT: case SIGCHLD: case SIGWINCH: 
   continue; 
 
  case SIGTSTP: case SIGTTIN: case SIGTTOU: 
   if (is_orphaned_pgrp(current->pgrp)) 
    continue; 
   /* FALLTHRU */ 
 
  case SIGSTOP: 
   current->state = TASK_STOPPED; 
   current->exit_code = signr; 
   if (!(current->p_pptr->sig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP)) 
    notify_parent(current, SIGCHLD); 
   schedule(); 
   continue; 
 
  case SIGQUIT: case SIGILL: case SIGTRAP: 
  case SIGABRT: case SIGFPE: case SIGSEGV: 
  case SIGBUS: case SIGSYS: case SIGXCPU: case SIGXFSZ: 
   if (do_coredump(signr, regs)) 
    exit_code |= 0x80; 
   /* FALLTHRU */ 
 
  default: 
   sigaddset(&current->pending.signal, signr); 
   recalc_sigpending(current); 
   current->flags |= PF_SIGNALED; 
   do_exit(exit_code); 
   /* NOTREACHED */ 
  } 
 } 
 ... 
 handle_signal(signr, ka, &info, oldset, regs); 
 return 1; 
} 
... 
return 0;
ahy6op9u

ahy6op9u1#

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

**当为停止的进程生成SIGCONT时,即使SIGCONT信号被进程忽略或被进程内的所有线程阻塞,并且在对sigwait()函数的调用中没有线程选择SIGCONT,进程也应继续。**如果SIGCONT被进程内的所有线程阻塞,在对选择SIGCONTsigwait()函数的调用中没有线程,并且SIGCONT未被进程忽略,则SIGCONT信号将在进程上保持未决直到其被线程解除阻塞或者线程调用选择SIGCONTsigwait()函数,或者为进程或进程内的任何线程生成停止信号。

你不能阻止SIGCONT继续执行你的进程,你最多只能阻止它的传递,这意味着如果你把SIGCONT添加到被阻止的信号集合中,你的进程将不会“注意到”它(注册的处理程序将不会运行,直到解除阻止),但它仍然会继续执行。
在Linux中,SIGCONT的“continuing”操作在信号生成时立即执行,即在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);
        }

    // ...
}

相关问题