《深入理解Linux内核(第三版)》笔记(十四),第十一章信号

数据结构

// 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值