数据结构
// include/linux/sched.h/line: 528
struct task_struct {
...
struct signal_struct *signal; // line: 632
struct sighand_struct *sighand;
sigset_t blocked, real_blocked;
struct sigpending pending;
unsigned long sas_ss_sp;
size_t sas_ss_size;
int (*notifier)(void *priv);
void *notifier_data;
sigset_t *notifier_mask;
...
}
// include/linux/sched.h/line: 274
struct signal_struct {
...
}
// include/linux/sched.h/line: 261
struct sighand_struct {
atomic_t count;
struct k_sigaction action[_NSIG]; // _NSIG,最大的信号种类数
spinlock_t siglock;
};
// include/linux/sched.h/line: 17
struct sigqueue {
struct list_head list;
spinlock_t *lock;
int flags;
siginfo_t info;
struct user_struct *user;
};
/* flags values. */
#define SIGQUEUE_PREALLOC 1
struct sigpending { // 在 task_struct 这里面被实例化,这个进程的私有信号队列链表头
struct list_head list; // 指向 struct sigqueue 中 list 的链表头
sigset_t signal;
};
// include/asm-generic/siginfo.h/line: 41
typedef struct siginfo {
int si_signo;
int si_errno;
int si_code;
union {
int _pad[SI_PAD_SIZE];
...
}
} __ARCH_SI_ATTRIBUTES siginfo_t;
// 一个及其复杂的数据结构
// 复杂性主要体现在这个共用体上了
// 这个共用体可以是多种信号的 info 的变体
函数
send_sig()
// kernel/signal.c/line: 1249
// 谁向谁发呢?就当是内核向进程发吧
int
send_sig(int sig, struct task_struct *p, int priv)
{
return send_sig_info(sig, (void*)(long)(priv != 0), p);
}
// == 和 != 是关系运算符,关系运算符的输出只能是0或者1
// 所以调用 send_sig 时,siginfo 的值只能是 0 或者 1
// 如果想充分利用 siginfo,就需要使用下面的函数
// kernel/signal.c/line: 1222
int
send_sig_info(int sig, struct siginfo *info, struct task_struct *p)
{
...
// 范围检查 _NSIG 是最大的信号加 1
if (sig < 0 || sig > _NSIG)
return -EINVAL;
...
read_lock(&tasklist_lock);
spin_lock_irqsave(&p->sighand->siglock, flags);
ret = specific_send_sig_info(sig, info, p);
spin_unlock_irqrestore(&p->sighand->siglock, flags);
read_unlock(&tasklist_lock);
return ret;
}
// kernel/fork.c/line: 61
__cacheline_aligned DEFINE_RWLOCK(tasklist_lock); /* outer */
EXPORT_SYMBOL(tasklist_lock);
// 这是一个读写锁
// 问题是,它保护的是谁呢?
// 貌似,改变进程关系,改变信号量等,都需要保护一下。
// kernel/signal.c/line: 843
static int
specific_send_sig_info(int sig, struct siginfo *info, struct task_struct *t)
{
...
if (((unsigned long)info > 2) && (info->si_code == SI_TIMER))
// Set up a return to indicate that we dropped the signal.
// 不清楚这个宏定义这个标志位最终的使用,所以代码中看不出意义
ret = info->si_sys_private;
/* Short-circuit ignored signals. */
// 如果被忽略了,就不发了;听起来就知道,但是这里说明了是如何实现的
if (sig_ignored(t, sig))
goto out;
/* Support queueing exactly one non-rt signal, so that we can get
more detailed information about the cause of the signal. */
// 首先信号需要是非实时的
// pending 是 task_t 里的一个实体,它里面有个元素是 sigset_t
// 其次如果信号已经被挂起了,那么依然不发送信号,直接跳出
// 哦,原来如此啊,秘密在这里!!!
// pending 用来管理挂起的信号,它是个链表加bitmap的结构
if (LEGACY_QUEUE(&t->pending, sig))
goto out;
// 这里有一个疑问,正常情况下,这个函数返回的都是 0
ret = send_signal(sig, info, t, &t->pending);
if (!ret && !sigismember(&t->blocked, sig))
signal_wake_up(t, sig == SIGKILL);
out:
return ret; // 这里说的成功产生信号会返回 1,科学吗?
}
// kernel/signal.c/line: 156
// 如果被忽略,则返回 1;如果不能被忽略,则返回 0
static int sig_ignored(struct task_struct *t, int sig)
{
// Tracers always want to know about signals.
// 如果被跟踪,那么就不能被忽略,就返回 1
if (t->ptrace & PT_PTRACED)
return 0;
// Blocked signals are never ignored, since the signal
// handler may change by the time it is unblocked.
// 这个函数的意义应该就是,看看 sig 有没有在 blocked 中,应该是按位与的操作
if (sigismember(&t->blocked, sig))
return 0;
/* Is it explicitly or implicitly ignored? */
// 判断语句,如果被忽略,== 返回 1
handler = t->sighand->action[sig-1].sa.sa_handler;
return handler == SIG_IGN ||
(handler == SIG_DFL && sig_kernel_ignore(sig));
}
// kernel/signal.c/line: 839
#define LEGACY_QUEUE(sigptr, sig) \
(((sig) < SIGRTMIN) && sigismember(&(sigptr)->signal, (sig)))
// kernel/signal.c/line: 583
void signal_wake_up(struct task_struct *t, int resume)
{
...
mask = TASK_INTERRUPTIBLE;
if (resume)
mask |= TASK_STOPPED | TASK_TRACED;
if (!wake_up_state(t, mask))
kick_process(t);
}
// kernel/sched.c/line: 1131
int fastcall wake_up_state(task_t *p, unsigned int state)
// kernel/sched.c/line: 985
// 这个函数涉及到了调度的核心
static int try_to_wake_up(task_t * p, unsigned int state, int sync)
// kernel/sched.c/line: 895
void kick_process(task_t *p)
{
int cpu;
preempt_disable();
cpu = task_cpu(p);
// 1. 不是相同的 cpu;2. 不是相同的进程
if ((cpu != smp_processor_id()) && task_curr(p))
smp_send_reschedule(cpu);
preempt_enable();
}
send_group_sig_info()
为什么会有共享信号?什么情况下,信号需要给整个组而不是单个线程
// kernel/signal.c/line: 1259
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;
}
// kernel/signal.c/line: 1114
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 && p->sighand) {
spin_lock_irqsave(&p->sighand->siglock, flags);
ret = __group_send_sig_info(sig, info, p);
spin_unlock_irqrestore(&p->sighand->siglock, flags);
}
return ret;
}
/*
* Bad permissions for sending the signal
*/
// kernel/signal.c/line: 630
static int check_kill_permission(int sig, struct siginfo *info,
struct task_struct *t)
{
...
// 全是与,也就是说,至少需要其中的一个条件成立,才往下走
if (
(!info ||
((unsigned long)info != 1 && (unsigned long)info != 2 && SI_FROMUSER(info))) &&
((sig != SIGCONT) ||
(current->signal->session != t->signal->session)) &&
(current->euid ^ t->suid) &&
(current->euid ^ t->uid) &&
(current->uid ^ t->suid) &&
(current->uid ^ t->uid) &&
!capable(CAP_KILL)
)
return error;
return security_task_kill(t, info, sig);
}
// kernel/signal.c/line: 1040
static int
__group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p)
{
...
handle_stop_signal(sig, p);
...
if (LEGACY_QUEUE(&p->signal->shared_pending, sig))
return ret;
// 看来这里的 share_pending 就是整个线程组共享
ret = send_signal(sig, info, p, &p->signal->shared_pending);
...
__group_complete_signal(sig, p);
return 0;
}
// 可知,continue 信号和 stop 信号是互斥的;是我就删你,是你就删我
// 可是,为什么发信号到单个进程的时候,不需要这样的操作呢
// kernel/signal.c/line: 660
static void handle_stop_signal(int sig, struct task_struct *p)
{
...
if (sig_kernel_stop(sig)) {
rm_from_queue(sigmask(SIGCONT), &p->signal->shared_pending);
t = p;
do {
rm_from_queue(sigmask(SIGCONT), &t->pending);
t = next_thread(t);
} while (t != p);
} else if (sig == SIGCONT) {
if (unlikely(p->signal->group_stop_count > 0)) {
...
}
rm_from_queue(SIG_KERNEL_STOP_MASK, &p->signal->shared_pending);
t = p;
do {
unsigned int state;
rm_from_queue(SIG_KERNEL_STOP_MASK, &t->pending);
... // TASK_STOPPED, TASK_INTERRUPTIBLE 相关的处理
t = next_thread(t);
} while (t != p);
if (p->signal->flags & SIGNAL_STOP_STOPPED) {
...
}
} else if (sig == SIGKILL) {
p->signal->flags = 0;
}
}
// kernel/exit.c/line: 859
// 返回线程组中的描述符
task_t fastcall *next_thread(const task_t *p)
{
return pid_task(p->pids[PIDTYPE_TGID].pid_list.next, PIDTYPE_TGID);
}
// include/linux/pid.h/line: 22
#define pid_task(elem, type) \
list_entry(elem, struct task_struct, pids[type].pid_list)
// kernel/signal.c/line: 927
static void
__group_complete_signal(int sig, struct task_struct *p)
公共函数 send_signal()
// kernel/signal.c/line: 775
static int send_signal(int sig, struct siginfo *info, struct task_struct *t,
struct sigpending *signals)
{
...
q = __sigqueue_alloc(t, GFP_ATOMIC); // 可用的数量是有限制的
if (q) {
list_add_tail(&q->list, &signals->list);
switch ((unsigned long) info) { // 地址或者特殊数字 0、1、2,其中 2 在上面判断过了
case 0:
...
case 1:
...
default:
// include/asm-generic/siginfo.h/line: 273
// 对未知的信号,会把整个结构体都 copy,对已知的信号只复制了确定的那部分
copy_siginfo(&q->info, info); // 对未知
break;
}
} else { // 有意思的一点是,即使没有空间来挂信号了,out_set 的语句也会被执行
... // 这样的设计,是 kill 总是成功的保证
}
out_set:
sigaddset(&signals->signal, sig);
return ret;
}