Reputation: 193
This is my code, ignoring SIGCONT
:
int main() {
signal(SIGCONT, SIG_IGN);
while(1);
}
This is what happens:
> ./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
It seems that SIGCONT
still made my process continue. Does it mean that a handler of SIGCONT
is just a "side-effect"?
I wonder at what time SIGCONT
makes the process run again? At what time is the process put into the dispatch queue again? Is it when the kill
syscall is performed or when the process is going to be dispatched? (I read a passage about Linux signals that indicates that dispatch code doesn't treat SIGCONT
specially. The code segment is showed below.)
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(¤t->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;
Upvotes: 2
Views: 385
Reputation: 69326
This is the intended behavior of SIGCONT
according to the POSIX standard. Quoting from POSIX.1-2017 chapter 2, section 2.4 "Signal Concepts", subsection 2.4.1:
When
SIGCONT
is generated for a process that is stopped, the process shall be continued, even if theSIGCONT
signal is ignored by the process or is blocked by all threads within the process and there are no threads in a call to asigwait()
function selectingSIGCONT
. IfSIGCONT
is blocked by all threads within the process, there are no threads in a call to asigwait()
function selectingSIGCONT
, andSIGCONT
is not ignored by the process, theSIGCONT
signal shall remain pending on the process until it is either unblocked by a thread or a thread calls asigwait()
function selectingSIGCONT
, or a stop signal is generated for the process or any of the threads within the process.
You cannot prevent SIGCONT
from resuming execution of your process. The most you can do is block its delivery, meaning that if you add SIGCONT
to the set of blocked signals your process will not "notice" it (registered handlers will not run until unblocked), but it will nonetheless resume execution.
In Linux, the "continuing" action of SIGCONT
is performed right away on signal generation, i.e. during the kill
syscall. This is done before even checking if the signal is blocked or ignored. The code responsible for this is in 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);
}
// ...
}
Upvotes: 6