📄 signal.c
字号:
* This version takes a sigset mask and looks at all signals, * not just those in the first mask word. */static int rm_from_queue_full(sigset_t *mask, struct sigpending *s){ struct sigqueue *q, *n; sigset_t m; sigandsets(&m, mask, &s->signal); if (sigisemptyset(&m)) return 0; signandsets(&s->signal, &s->signal, mask); list_for_each_entry_safe(q, n, &s->list, list) { if (sigismember(mask, q->info.si_signo)) { list_del_init(&q->list); __sigqueue_free(q); } } return 1;}/* * Remove signals in mask from the pending set and queue. * Returns 1 if any signals were found. * * All callers must be holding the siglock. */static int rm_from_queue(unsigned long mask, struct sigpending *s){ struct sigqueue *q, *n; if (!sigtestsetmask(&s->signal, mask)) return 0; sigdelsetmask(&s->signal, mask); list_for_each_entry_safe(q, n, &s->list, list) { if (q->info.si_signo < SIGRTMIN && (mask & sigmask(q->info.si_signo))) { list_del_init(&q->list); __sigqueue_free(q); } } return 1;}/* * Bad permissions for sending the signal */static int check_kill_permission(int sig, struct siginfo *info, struct task_struct *t){ struct pid *sid; int error; if (!valid_signal(sig)) return -EINVAL; if (info != SEND_SIG_NOINFO && (is_si_special(info) || SI_FROMKERNEL(info))) return 0; error = audit_signal_info(sig, t); /* Let audit system see the signal */ if (error) return error; if ((current->euid ^ t->suid) && (current->euid ^ t->uid) && (current->uid ^ t->suid) && (current->uid ^ t->uid) && !capable(CAP_KILL)) { switch (sig) { case SIGCONT: sid = task_session(t); /* * We don't return the error if sid == NULL. The * task was unhashed, the caller must notice this. */ if (!sid || sid == task_session(current)) break; default: return -EPERM; } } return security_task_kill(t, info, sig, 0);}/* * 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 int prepare_signal(int sig, struct task_struct *p){ struct signal_struct *signal = p->signal; struct task_struct *t; if (unlikely(signal->flags & SIGNAL_GROUP_EXIT)) { /* * The process is in the middle of dying, nothing to do. */ } else if (sig_kernel_stop(sig)) { /* * This is a stop signal. Remove SIGCONT from all queues. */ rm_from_queue(sigmask(SIGCONT), &signal->shared_pending); t = p; do { rm_from_queue(sigmask(SIGCONT), &t->pending); } while_each_thread(p, t); } else if (sig == SIGCONT) { unsigned int why; /* * Remove all stop signals from all queues, * and wake all threads. */ rm_from_queue(SIG_KERNEL_STOP_MASK, &signal->shared_pending); t = p; do { unsigned int state; rm_from_queue(SIG_KERNEL_STOP_MASK, &t->pending); /* * If there is a handler for SIGCONT, we must make * sure that no thread returns to user mode before * we post the signal, in case it was the only * thread eligible to run the signal handler--then * it must not do anything between resuming and * running the handler. With the TIF_SIGPENDING * flag set, the thread will pause and acquire the * siglock that we hold now and until we've queued * the pending signal. * * Wake up the stopped thread _after_ setting * TIF_SIGPENDING */ state = __TASK_STOPPED; if (sig_user_defined(t, SIGCONT) && !sigismember(&t->blocked, SIGCONT)) { set_tsk_thread_flag(t, TIF_SIGPENDING); state |= TASK_INTERRUPTIBLE; } wake_up_state(t, state); } while_each_thread(p, t); /* * Notify the parent with CLD_CONTINUED if we were stopped. * * If we were in the middle of a group stop, we pretend it * was already finished, and then continued. Since SIGCHLD * doesn't queue we report only CLD_STOPPED, as if the next * CLD_CONTINUED was dropped. */ why = 0; if (signal->flags & SIGNAL_STOP_STOPPED) why |= SIGNAL_CLD_CONTINUED; else if (signal->group_stop_count) why |= SIGNAL_CLD_STOPPED; if (why) { /* * The first thread which returns from finish_stop() * will take ->siglock, notice SIGNAL_CLD_MASK, and * notify its parent. See get_signal_to_deliver(). */ signal->flags = why | SIGNAL_STOP_CONTINUED; signal->group_stop_count = 0; signal->group_exit_code = 0; } else { /* * We are not stopped, but there could be a stop * signal in the middle of being processed after * being removed from the queue. Clear that too. */ signal->flags &= ~SIGNAL_STOP_DEQUEUED; } } return !sig_ignored(p, sig);}/* * Test if P wants to take SIG. After we've checked all threads with this, * it's equivalent to finding no threads not blocking SIG. Any threads not * blocking SIG were ruled out because they are not running and already * have pending signals. Such threads will dequeue from the shared queue * as soon as they're available, so putting the signal on the shared queue * will be equivalent to sending it to one such thread. */static inline int wants_signal(int sig, struct task_struct *p){ if (sigismember(&p->blocked, sig)) return 0; if (p->flags & PF_EXITING) return 0; if (sig == SIGKILL) return 1; if (task_is_stopped_or_traced(p)) return 0; return task_curr(p) || !signal_pending(p);}static void complete_signal(int sig, struct task_struct *p, int group){ struct signal_struct *signal = p->signal; struct task_struct *t; /* * Now find a thread we can wake up to take the signal off the queue. * * If the main thread wants the signal, it gets first crack. * Probably the least surprising to the average bear. */ if (wants_signal(sig, p)) t = p; else if (!group || thread_group_empty(p)) /* * There is just one thread and it does not need to be woken. * It will dequeue unblocked signals before it runs again. */ return; else { /* * Otherwise try to find a suitable thread. */ t = signal->curr_target; while (!wants_signal(sig, t)) { t = next_thread(t); if (t == signal->curr_target) /* * No thread needs to be woken. * Any eligible threads will see * the signal in the queue soon. */ return; } signal->curr_target = t; } /* * Found a killable thread. If the signal will be fatal, * then start taking the whole group down immediately. */ if (sig_fatal(p, sig) && !(signal->flags & (SIGNAL_UNKILLABLE | SIGNAL_GROUP_EXIT)) && !sigismember(&t->real_blocked, sig) && (sig == SIGKILL || !tracehook_consider_fatal_signal(t, sig, SIG_DFL))) { /* * This signal will be fatal to the whole group. */ if (!sig_kernel_coredump(sig)) { /* * Start a group exit and wake everybody up. * This way we don't have other threads * running and doing things after a slower * thread has the fatal signal pending. */ signal->flags = SIGNAL_GROUP_EXIT; signal->group_exit_code = sig; signal->group_stop_count = 0; t = p; do { sigaddset(&t->pending.signal, SIGKILL); signal_wake_up(t, 1); } while_each_thread(p, t); return; } } /* * The signal is already in the shared-pending queue. * Tell the chosen thread to wake up and dequeue it. */ signal_wake_up(t, sig == SIGKILL); return;}static inline int legacy_queue(struct sigpending *signals, int sig){ return (sig < SIGRTMIN) && sigismember(&signals->signal, sig);}static int send_signal(int sig, struct siginfo *info, struct task_struct *t, int group){ struct sigpending *pending; struct sigqueue *q; assert_spin_locked(&t->sighand->siglock); if (!prepare_signal(sig, t)) return 0; pending = group ? &t->signal->shared_pending : &t->pending; /* * Short-circuit ignored signals and support queuing * exactly one non-rt signal, so that we can get more * detailed information about the cause of the signal. */ if (legacy_queue(pending, sig)) return 0; /* * fast-pathed signals for kernel-internal things like SIGSTOP * or SIGKILL. */ if (info == SEND_SIG_FORCED) goto out_set; /* Real-time signals must be queued if sent by sigqueue, or some other real-time mechanism. It is implementation defined whether kill() does so. We attempt to do so, on the principle of least surprise, but since kill is not allowed to fail with EAGAIN when low on memory we just make sure at least one signal gets delivered and don't pass on the info struct. */ q = __sigqueue_alloc(t, GFP_ATOMIC, (sig < SIGRTMIN && (is_si_special(info) || info->si_code >= 0))); if (q) { list_add_tail(&q->list, &pending->list); switch ((unsigned long) info) { case (unsigned long) SEND_SIG_NOINFO: q->info.si_signo = sig; q->info.si_errno = 0; q->info.si_code = SI_USER; q->info.si_pid = task_pid_vnr(current); q->info.si_uid = current->uid; break; case (unsigned long) SEND_SIG_PRIV: q->info.si_signo = sig; q->info.si_errno = 0; q->info.si_code = SI_KERNEL; q->info.si_pid = 0; q->info.si_uid = 0; break; default: copy_siginfo(&q->info, info); break; } } else if (!is_si_special(info)) { if (sig >= SIGRTMIN && info->si_code != SI_USER) /* * Queue overflow, abort. We may abort if the signal was rt * and sent by user using something other than kill(). */ return -EAGAIN; }out_set: signalfd_notify(t, sig); sigaddset(&pending->signal, sig); complete_signal(sig, t, group); return 0;}int print_fatal_signals;static void print_fatal_signal(struct pt_regs *regs, int signr){ printk("%s/%d: potentially unexpected fatal signal %d.\n", current->comm, task_pid_nr(current), signr);#if defined(__i386__) && !defined(__arch_um__) printk("code at %08lx: ", regs->ip); { int i; for (i = 0; i < 16; i++) { unsigned char insn; __get_user(insn, (unsigned char *)(regs->ip + i)); printk("%02x ", insn); } }#endif printk("\n"); show_regs(regs);}static int __init setup_print_fatal_signals(char *str){ get_option (&str, &print_fatal_signals); return 1;}__setup("print-fatal-signals=", setup_print_fatal_signals);int__group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p){ return send_signal(sig, info, p, 1);}static intspecific_send_sig_info(int sig, struct siginfo *info, struct task_struct *t){ return send_signal(sig, info, t, 0);}/* * Force a signal that the process can't ignore: if necessary * we unblock the signal and change any SIG_IGN to SIG_DFL. * * Note: If we unblock the signal, we always reset it to SIG_DFL, * since we do not want to have a signal handler that was blocked * be invoked when user space had explicitly blocked it. * * We don't want to have recursive SIGSEGV's etc, for example, * that is why we also clear SIGNAL_UNKILLABLE. */intforce_sig_info(int sig, struct siginfo *info, struct task_struct *t){ unsigned long int flags; int ret, blocked, ignored; struct k_sigaction *action; spin_lock_irqsave(&t->sighand->siglock, flags); action = &t->sighand->action[sig-1]; ignored = action->sa.sa_handler == SIG_IGN; blocked = sigismember(&t->blocked, sig); if (blocked || ignored) { action->sa.sa_handler = SIG_DFL; if (blocked) { sigdelset(&t->blocked, sig); recalc_sigpending_and_wake(t); } } if (action->sa.sa_handler == SIG_DFL) t->signal->flags &= ~SIGNAL_UNKILLABLE; ret = specific_send_sig_info(sig, info, t); spin_unlock_irqrestore(&t->sighand->siglock, flags); return ret;}voidforce_sig_specific(int sig, struct task_struct *t){ force_sig_info(sig, SEND_SIG_FORCED, t);}/* * Nuke all other threads in the group. */void zap_other_threads(struct task_struct *p){ struct task_struct *t; p->signal->group_stop_count = 0; for (t = next_thread(p); t != p; t = next_thread(t)) { /* * Don't bother with already dead threads */ if (t->exit_state) continue; /* SIGKILL will be handled before any pending SIGSTOP */ sigaddset(&t->pending.signal, SIGKILL); signal_wake_up(t, 1); }}int __fatal_signal_pending(struct task_struct *tsk){ return sigismember(&tsk->pending.signal, SIGKILL);}EXPORT_SYMBOL(__fatal_signal_pending);struct sighand_struct *lock_task_sighand(struct task_struct *tsk, unsigned long *flags){ struct sighand_struct *sighand; rcu_read_lock(); for (;;) { sighand = rcu_dereference(tsk->sighand); if (unlikely(sighand == NULL)) break; spin_lock_irqsave(&sighand->siglock, *flags); if (likely(sighand == tsk->sighand)) break; spin_unlock_irqrestore(&sighand->siglock, *flags); } rcu_read_unlock(); return sighand;}int group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p){ unsigned long flags; int ret; ret = check_kill_permission(sig, info, p); if (!ret && sig) { ret = -ESRCH; if (lock_task_sighand(p, &flags)) { ret = __group_send_sig_info(sig, info, p); unlock_task_sighand(p, &flags); } } return ret;}/* * __kill_pgrp_info() sends a signal to a process group: this is what the tty * control characters do (^C, ^Z etc) */int __kill_pgrp_info(int sig, struct siginfo *info, struct pid *pgrp){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -