From da334d91ff7001d234863fc7692de1ff90bed57a Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Fri, 4 Apr 2003 04:12:17 -0800 Subject: [PATCH] linux-2.5.66-signal-cleanup.patch Here is the cleanup patch I promised back in February. Sorry it took a while. The effects should be purely cosmetic in 2.5.66. However, the new interface for the proper way to send thread-specific of process-global signals from inside the kernel is needed for correct implementation of some fixes to timer stuff that Ulrich told me about. This cleans up some obsolete comments and macros in kernel/signal.c, restores send_sig_info to its original behavior, and adds a global entry point send_group_sig_info. I checked all the uses of send_sig and send_sig_info and changed a few to send_group_sig_info. I think it would be cleanest if the whole mess of *_sig* entry points were reduced to two or three, but I did the change that minimized the number of callers I had to fix up. There should be no discernible difference, since the 2.5.66 send_sig_info function did group semantics for those signals by number already. The only exception to that is pdeath_signal, which I guess can be any signal number but I deemed ought to be process-wide. I did not change any of the calls using SIGKILL, though that does have process-wide semantics. There is no need to change it since SIGKILL always kills the whole group, though the code path for send_sig(SIGKILL,...) calls in multithreaded processes will be different now. --- kernel/exit.c | 2 +- kernel/itimer.c | 2 +- kernel/signal.c | 171 ++++++++++++++++++++++++++++++-------------------------- 3 files changed, 95 insertions(+), 80 deletions(-) (limited to 'kernel') diff --git a/kernel/exit.c b/kernel/exit.c index ffc0973ab074..b393a46247c4 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -488,7 +488,7 @@ static inline void reparent_thread(task_t *p, task_t *father, int traced) p->self_exec_id++; if (p->pdeath_signal) - send_sig(p->pdeath_signal, p, 0); + send_group_sig_info(p->pdeath_signal, 0, p); /* Move the child from its dying parent to the new one. */ if (unlikely(traced)) { diff --git a/kernel/itimer.c b/kernel/itimer.c index 4fc29f3de5e8..4db3b24ec41e 100644 --- a/kernel/itimer.c +++ b/kernel/itimer.c @@ -67,7 +67,7 @@ void it_real_fn(unsigned long __data) struct task_struct * p = (struct task_struct *) __data; unsigned long interval; - send_sig(SIGALRM, p, 1); + send_group_sig_info(SIGALRM, SEND_SIG_PRIV, p); interval = p->it_real_incr; if (interval) { if (interval > (unsigned long) LONG_MAX) diff --git a/kernel/signal.c b/kernel/signal.c index 488134eec0e2..096d58ad5237 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -33,64 +33,79 @@ static kmem_cache_t *sigqueue_cachep; atomic_t nr_queued_signals; int max_queued_signals = 1024; -/********************************************************* - - POSIX thread group signal behavior: - ----------------------------------------------------------- -| | userspace | kernel | ----------------------------------------------------------- -| SIGHUP | load-balance | kill-all | -| SIGINT | load-balance | kill-all | -| SIGQUIT | load-balance | kill-all+core | -| SIGILL | specific | kill-all+core | -| SIGTRAP | specific | kill-all+core | -| SIGABRT/SIGIOT | specific | kill-all+core | -| SIGBUS | specific | kill-all+core | -| SIGFPE | specific | kill-all+core | -| SIGKILL | n/a | kill-all | -| SIGUSR1 | load-balance | kill-all | -| SIGSEGV | specific | kill-all+core | -| SIGUSR2 | load-balance | kill-all | -| SIGPIPE | specific | kill-all | -| SIGALRM | load-balance | kill-all | -| SIGTERM | load-balance | kill-all | -| SIGCHLD | load-balance | ignore | -| SIGCONT | load-balance | ignore | -| SIGSTOP | n/a | stop-all | -| SIGTSTP | load-balance | stop-all | -| SIGTTIN | load-balance | stop-all | -| SIGTTOU | load-balance | stop-all | -| SIGURG | load-balance | ignore | -| SIGXCPU | specific | kill-all+core | -| SIGXFSZ | specific | kill-all+core | -| SIGVTALRM | load-balance | kill-all | -| SIGPROF | specific | kill-all | -| SIGPOLL/SIGIO | load-balance | kill-all | -| SIGSYS/SIGUNUSED | specific | kill-all+core | -| SIGSTKFLT | specific | kill-all | -| SIGWINCH | load-balance | ignore | -| SIGPWR | load-balance | kill-all | -| SIGRTMIN-SIGRTMAX | load-balance | kill-all | ----------------------------------------------------------- - - non-POSIX signal thread group behavior: - ----------------------------------------------------------- -| | userspace | kernel | ----------------------------------------------------------- -| SIGEMT | specific | kill-all+core | ----------------------------------------------------------- -*/ - -/* Some systems do not have a SIGSTKFLT and the kernel never - * generates such signals anyways. +/* + * In POSIX a signal is sent either to a specific thread (Linux task) + * or to the process as a whole (Linux thread group). How the signal + * is sent determines whether it's to one thread or the whole group, + * which determines which signal mask(s) are involved in blocking it + * from being delivered until later. When the signal is delivered, + * either it's caught or ignored by a user handler or it has a default + * effect that applies to the whole thread group (POSIX process). + * + * The possible effects an unblocked signal set to SIG_DFL can have are: + * ignore - Nothing Happens + * terminate - kill the process, i.e. all threads in the group, + * similar to exit_group. The group leader (only) reports + * WIFSIGNALED status to its parent. + * coredump - write a core dump file describing all threads using + * the same mm and then kill all those threads + * stop - stop all the threads in the group, i.e. TASK_STOPPED state + * + * SIGKILL and SIGSTOP cannot be caught, blocked, or ignored. + * Other signals when not blocked and set to SIG_DFL behaves as follows. + * The job control signals also have other special effects. + * + * +--------------------+------------------+ + * | POSIX signal | default action | + * +--------------------+------------------+ + * | SIGHUP | terminate | + * | SIGINT | terminate | + * | SIGQUIT | coredump | + * | SIGILL | coredump | + * | SIGTRAP | coredump | + * | SIGABRT/SIGIOT | coredump | + * | SIGBUS | coredump | + * | SIGFPE | coredump | + * | SIGKILL | terminate(+) | + * | SIGUSR1 | terminate | + * | SIGSEGV | coredump | + * | SIGUSR2 | terminate | + * | SIGPIPE | terminate | + * | SIGALRM | terminate | + * | SIGTERM | terminate | + * | SIGCHLD | ignore | + * | SIGCONT | ignore(*) | + * | SIGSTOP | stop(*)(+) | + * | SIGTSTP | stop(*) | + * | SIGTTIN | stop(*) | + * | SIGTTOU | stop(*) | + * | SIGURG | ignore | + * | SIGXCPU | coredump | + * | SIGXFSZ | coredump | + * | SIGVTALRM | terminate | + * | SIGPROF | terminate | + * | SIGPOLL/SIGIO | terminate | + * | SIGSYS/SIGUNUSED | coredump | + * | SIGSTKFLT | terminate | + * | SIGWINCH | ignore | + * | SIGPWR | terminate | + * | SIGRTMIN-SIGRTMAX | terminate | + * +--------------------+------------------+ + * | non-POSIX signal | default action | + * +--------------------+------------------+ + * | SIGEMT | coredump | + * +--------------------+------------------+ + * + * (+) For SIGKILL and SIGSTOP the action is "always", not just "default". + * (*) Special job control effects: + * When SIGCONT is sent, it resumes the process (all threads in the group) + * from TASK_STOPPED state and also clears any pending/queued stop signals + * (any of those marked with "stop(*)"). This happens regardless of blocking, + * catching, or ignoring SIGCONT. When any stop signal is sent, it clears + * any pending/queued SIGCONT signals; this happens regardless of blocking, + * catching, or ignored the stop signal, though (except for SIGSTOP) the + * default action of stopping the process may happen later or never. */ -#ifdef SIGSTKFLT -#define M_SIGSTKFLT M(SIGSTKFLT) -#else -#define M_SIGSTKFLT 0 -#endif #ifdef SIGEMT #define M_SIGEMT M(SIGEMT) @@ -105,16 +120,6 @@ int max_queued_signals = 1024; #endif #define T(sig, mask) (M(sig) & (mask)) -#define SIG_KERNEL_BROADCAST_MASK (\ - M(SIGHUP) | M(SIGINT) | M(SIGQUIT) | M(SIGILL) | \ - M(SIGTRAP) | M(SIGABRT) | M(SIGBUS) | M(SIGFPE) | \ - M(SIGKILL) | M(SIGUSR1) | M(SIGSEGV) | M(SIGUSR2) | \ - M(SIGPIPE) | M(SIGALRM) | M(SIGTERM) | M(SIGXCPU) | \ - M(SIGXFSZ) | M(SIGVTALRM) | M(SIGPROF) | M(SIGPOLL) | \ - M(SIGSYS) | M_SIGSTKFLT | M(SIGPWR) | M(SIGCONT) | \ - M(SIGSTOP) | M(SIGTSTP) | M(SIGTTIN) | M(SIGTTOU) | \ - M_SIGEMT ) - #define SIG_KERNEL_ONLY_MASK (\ M(SIGKILL) | M(SIGSTOP) ) @@ -599,7 +604,7 @@ static void do_notify_parent_cldstop(struct task_struct *tsk, struct task_struct *parent); /* - * Handle magic process-wide effects of stop/continue signals, and SIGKILL. + * 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 @@ -1134,9 +1139,8 @@ static int kill_something_info(int sig, struct siginfo *info, int pid) */ /* - * XXX should probably nix these interfaces and update the kernel - * to specify explicitly whether the signal is a group signal or - * specific to a thread. + * These two are the most common entry points. They send a signal + * just to the specific thread. */ int send_sig_info(int sig, struct siginfo *info, struct task_struct *p) @@ -1150,13 +1154,9 @@ send_sig_info(int sig, struct siginfo *info, struct task_struct *p) * going away or changing from under us. */ read_lock(&tasklist_lock); - if (T(sig, SIG_KERNEL_BROADCAST_MASK)) { - ret = group_send_sig_info(sig, info, p); - } else { - spin_lock_irq(&p->sighand->siglock); - ret = specific_send_sig_info(sig, info, p); - spin_unlock_irq(&p->sighand->siglock); - } + spin_lock_irq(&p->sighand->siglock); + ret = specific_send_sig_info(sig, info, p); + spin_unlock_irq(&p->sighand->siglock); read_unlock(&tasklist_lock); return ret; } @@ -1167,6 +1167,20 @@ send_sig(int sig, struct task_struct *p, int priv) return send_sig_info(sig, (void*)(long)(priv != 0), p); } +/* + * This is the entry point for "process-wide" signals. + * They will go to an appropriate thread in the thread group. + */ +int +send_group_sig_info(int sig, struct siginfo *info, struct task_struct *p) +{ + int ret; + read_lock(&tasklist_lock); + ret = group_send_sig_info(sig, info, p); + read_unlock(&tasklist_lock); + return ret; +} + void force_sig(int sig, struct task_struct *p) { @@ -1642,6 +1656,7 @@ EXPORT_SYMBOL(kill_sl_info); EXPORT_SYMBOL(notify_parent); EXPORT_SYMBOL(send_sig); EXPORT_SYMBOL(send_sig_info); +EXPORT_SYMBOL(send_group_sig_info); EXPORT_SYMBOL(sigprocmask); EXPORT_SYMBOL(block_all_signals); EXPORT_SYMBOL(unblock_all_signals); -- cgit v1.2.3