Linux内核进程管理子系统有什么第二十一回 —— 进程主结构详解(17)

接前一篇文章:Linux内核进程管理子系统有什么第二十回 —— 进程主结构详解(16)

 

本文内容参考:

Linux内核进程管理专题报告_linux rseq-CSDN博客

《趣谈Linux操作系统 核心原理篇:第三部分 进程管理》—— 刘超

《图解Linux内核 基于6.x》 —— 姜亚华 机械工业出版社

特此致谢!

 

进程管理核心结构 —— task_struct

3. 信号处理相关成员

上一回继续讲解task_struct结构中信号处理相关的成员,包括:

	/* Signal handlers: */
	struct signal_struct		*signal;
	struct sighand_struct __rcu		*sighand;
	sigset_t			blocked;
	sigset_t			real_blocked;
	/* Restored if set_restore_sigmask() was used: */
	sigset_t			saved_sigmask;
	struct sigpending		pending;
	unsigned long			sas_ss_sp;
	size_t				sas_ss_size;
	unsigned int			sas_ss_flags;

上一回讲到signal、sigaction和rt_sigaction这3个系统调用底层都是调用do_sigaction函数,也贴出了do_sigaction函数的代码(Linux内核源码/kernel/signal.c中):

int do_sigaction(int sig, struct k_sigaction *act, struct k_sigaction *oact)
{
	struct task_struct *p = current, *t;
	struct k_sigaction *k;
	sigset_t mask;
 
	if (!valid_signal(sig) || sig < 1 || (act && sig_kernel_only(sig)))
		return -EINVAL;
 
	k = &p->sighand->action[sig-1];
 
	spin_lock_irq(&p->sighand->siglock);
	if (k->sa.sa_flags & SA_IMMUTABLE) {
		spin_unlock_irq(&p->sighand->siglock);
		return -EINVAL;
	}
	if (oact)
		*oact = *k;
 
	/*
	 * Make sure that we never accidentally claim to support SA_UNSUPPORTED,
	 * e.g. by having an architecture use the bit in their uapi.
	 */
	BUILD_BUG_ON(UAPI_SA_FLAGS & SA_UNSUPPORTED);
 
	/*
	 * Clear unknown flag bits in order to allow userspace to detect missing
	 * support for flag bits and to allow the kernel to use non-uapi bits
	 * internally.
	 */
	if (act)
		act->sa.sa_flags &= UAPI_SA_FLAGS;
	if (oact)
		oact->sa.sa_flags &= UAPI_SA_FLAGS;
 
	sigaction_compat_abi(act, oact);
 
	if (act) {
		sigdelsetmask(&act->sa.sa_mask,
			      sigmask(SIGKILL) | sigmask(SIGSTOP));
		*k = *act;
		/*
		 * POSIX 3.3.1.3:
		 *  "Setting a signal action to SIG_IGN for a signal that is
		 *   pending shall cause the pending signal to be discarded,
		 *   whether or not it is blocked."
		 *
		 *  "Setting a signal action to SIG_DFL for a signal that is
		 *   pending and whose default action is to ignore the signal
		 *   (for example, SIGCHLD), shall cause the pending signal to
		 *   be discarded, whether or not it is blocked"
		 */
		if (sig_handler_ignored(sig_handler(p, sig), sig)) {
			sigemptyset(&mask);
			sigaddset(&mask, sig);
			flush_sigqueue_mask(&mask, &p->signal->shared_pending);
			for_each_thread(p, t)
				flush_sigqueue_mask(&mask, &t->pending);
		}
	}
 
	spin_unlock_irq(&p->sighand->siglock);
	return 0;
}

 do_sigaction函数也比较好理解。分为以下步骤:

1)首先进行合法性检查。

代码片段如下:

    if (!valid_signal(sig) || sig < 1 || (act && sig_kernel_only(sig)))
		return -EINVAL;

确保用户传递的sig必须在[1, 64]范围内,而且不能更改sig_kernel_only的信号处理方式。sig_kernel_only()包含SIGKILL和SIGSTOP两种信号,用户不可以自定义它们的处理方式。

include/linux/signal.h中:

#define siginmask(sig, mask) \
	((sig) > 0 && (sig) < SIGRTMIN && (rt_sigmask(sig) & (mask)))

#define SIG_KERNEL_ONLY_MASK (\
	rt_sigmask(SIGKILL)   |  rt_sigmask(SIGSTOP))

……

#define sig_kernel_only(sig)		siginmask(sig, SIG_KERNEL_ONLY_MASK)

2)设置p->sighand->action[sig-1],更改进程对信号的处理方式。

这部分就对应do_sigaction函数的后半段代码:

	/*
	 * Clear unknown flag bits in order to allow userspace to detect missing
	 * support for flag bits and to allow the kernel to use non-uapi bits
	 * internally.
	 */
	if (act)
		act->sa.sa_flags &= UAPI_SA_FLAGS;
	if (oact)
		oact->sa.sa_flags &= UAPI_SA_FLAGS;

	sigaction_compat_abi(act, oact);

	if (act) {
		sigdelsetmask(&act->sa.sa_mask,
			      sigmask(SIGKILL) | sigmask(SIGSTOP));
		*k = *act;
		/*
		 * POSIX 3.3.1.3:
		 *  "Setting a signal action to SIG_IGN for a signal that is
		 *   pending shall cause the pending signal to be discarded,
		 *   whether or not it is blocked."
		 *
		 *  "Setting a signal action to SIG_DFL for a signal that is
		 *   pending and whose default action is to ignore the signal
		 *   (for example, SIGCHLD), shall cause the pending signal to
		 *   be discarded, whether or not it is blocked"
		 */
		if (sig_handler_ignored(sig_handler(p, sig), sig)) {
			sigemptyset(&mask);
			sigaddset(&mask, sig);
			flush_sigqueue_mask(&mask, &p->signal->shared_pending);
			for_each_thread(p, t)
				flush_sigqueue_mask(&mask, &t->pending);
		}
	}

3)在上边代码片段的最后,更改了信号的处理方式后,如果信号的处理方式是忽略,则将之前收到的该信号从线程组中删除。

整体来看,do_sigaction函数就是设置task_struct结构中的成员struct sighand_struct __rcu        *sighand中的信号处理函数。

struct sighand_struct的定义在include/linux/sched/signal.h中,如下:

/*
 * Types defining task->signal and task->sighand and APIs using them:
 */

struct sighand_struct {
	spinlock_t		siglock;
	refcount_t		count;
	wait_queue_head_t	signalfd_wqh;
	struct k_sigaction	action[_NSIG];
};

struct k_sigaction的定义在include/linux/signal_types.h中,如下:

struct k_sigaction {
	struct sigaction sa;
#ifdef __ARCH_HAS_KA_RESTORER
	__sigrestore_t ka_restorer;
#endif
};

struct sigaction的定义也在include/linux/signal_types.h中,如下:

struct sigaction {
#ifndef __ARCH_HAS_IRIX_SIGACTION
	__sighandler_t	sa_handler;
	unsigned long	sa_flags;
#else
	unsigned int	sa_flags;
	__sighandler_t	sa_handler;
#endif
#ifdef __ARCH_HAS_SA_RESTORER
	__sigrestore_t sa_restorer;
#endif
	sigset_t	sa_mask;	/* mask last for extensibility */
};

那么,do_sigaction函数的核心代码:

    k = &p->sighand->action[sig-1];
    ……
    *k = *act;

实际上就是设置了进程对于某个信号的处理函数为用户自定义的(动作)函数。在此借用刘超先生《趣谈Linux操作系统 核心原理篇:第七部分 进程间通信》一节中这一部分的图以方便和加深理解:

这样,信号处理一般流程的第1步 —— 注册信号处理函数与task_struct结构中信号处理相关成员的关系就讲清楚了。

下一回开始讲解信号处理一般流程的第2步 —— 发送信号与task_struct结构中信号处理相关成员的关系。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蓝天居士

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值