Linux内核源代码情景分析-信号

本文详细分析了Linux内核中的信号机制,包括信号的数据结构、信号的发送与接收、处理过程,以及在系统调用、中断处理和异常处理中的应用。重点讨论了信号队列、信号遮蔽、信号处理函数的执行流程,以及从系统调用返回到用户空间时如何处理信号。
摘要由CSDN通过智能技术生成

    一、我们先来看下信号的所设计的数据结构:

struct task_struct {
	int sigpending;
	
	int exit_code, exit_signal;
		/* Protects signal and blocked */
	struct signal_struct *sig;

	sigset_t blocked;
	struct sigpending pending;

	
}
    sigpending,是个整数,用来表示这个进程是否有信号在等待处理。

    blocked表示信号遮蔽位,如果此位置1,说明不相应这个信号。

    pending结构如下:

struct sigpending {
	struct sigqueue *head, **tail;
	sigset_t signal;
};
    blocked和sigpending里面的signal都是64位的字段,sigset_t如下:

#define _NSIG		64
#define _NSIG_BPW	32
#define _NSIG_WORDS	(_NSIG / _NSIG_BPW)

typedef unsigned long old_sigset_t;		/* at least 32 bits */

typedef struct {
	unsigned long sig[_NSIG_WORDS];//两个32位就是64位
} sigset_t;
     sigpending里面的signal和blocked,分别用来模拟中断控制器硬件中的中断状态寄存器(有啥中断待处理)和中断屏蔽寄存器(哪些中断不能处理)。

    每当有一个中断请求到来时,中断状态寄存器中与之相应的某一位就被置为1,表示有相应的中断请求在等待处理,如果连续有两次中断请求到来,则有可能因为处理器来不及响应而被"合并"。

    可是信号机制中就不同了,信号机制为信号准备一个队列,每产生一个信号就把它挂入这个队列,这样就可以确保一个信号也不会丢失了。所以在sigpending结构里面会有sigqueue结构。

    sigpengding结构里面的sigqueue结构如下:

struct sigqueue {
	struct sigqueue *next;
	siginfo_t info;
};
    siginfo_t如下面所讲。

    发送信号,不但要将sigpengding结构里面的sig结构对应位置1,还要将分配一个sigqueue结构(里面siginfo_t结构si_signo代表哪个信号)链入到sigpengding结构里面的sigqueue。这样就不会丢失信号。只有sigpengding结构里面的sigqueue队列中对应信号所有的sigqueue结构全部移除后,sigpengding结构里面的sig结构对应位才置0。


    下面我们解释signal_struct数据结构:

struct signal_struct {
	atomic_t		count;
	struct k_sigaction	action[_NSIG];//64,信号处理函数一共64个,这就是为什么blocked和sigpending里面的signal是64位
	spinlock_t		siglock;
};
struct k_sigaction {
	struct sigaction sa;
};
struct sigaction {
	union {
	  __sighandler_t _sa_handler;
	  void (*_sa_sigaction)(int, struct siginfo *, void *);
	} _u;
	sigset_t sa_mask;
	unsigned long sa_flags;
	void (*sa_restorer)(void);	
};
    sigaction数据结构是信号处理函数。其中siginfo结构如下:

typedef struct siginfo {
	int si_signo;
	int si_errno;
	int si_code;

	union {
		int _pad[SI_PAD_SIZE];

		/* kill() */
		struct {
			pid_t _pid;		/* sender's pid */
			uid_t _uid;		/* sender's uid */
		} _kill;

		/* POSIX.1b timers */
		struct {
			unsigned int _timer1;
			unsigned int _timer2;
		} _timer;

		/* POSIX.1b signals */
		struct {
			pid_t _pid;		/* sender's pid */
			uid_t _uid;		/* sender's uid */
			sigval_t _sigval;
		} _rt;

		/* SIGCHLD */
		struct {
			pid_t _pid;		/* which child */
			uid_t _uid;		/* sender's uid */
			int _status;		/* exit code */
			clock_t _utime;
			clock_t _stime;
		} _sigchld;

		/* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
		struct {
			void *_addr; /* faulting insn/memory ref. */
		} _sigfault;

		/* SIGPOLL */
		struct {
			int _band;	/* POLL_IN, POLL_OUT, POLL_MSG */
			int _fd;
		} _sigpoll;
	} _sifields;
}siginfo_t


    二、先看"信号向量",也就是信号处理程序的"安装",其作用类似于中断向量的设置。Linux为此提供了,sigaction系统调用。

    int sigaction (int signum, const struct sigaction *newact, struct sigaction *oldact);

asmlinkage unsigned long
sys_signal(int sig, __sighandler_t handler)
{
	struct k_sigaction new_sa, old_sa;
	int ret;

	new_sa.sa.sa_handler = handler;
	new_sa.sa.sa_flags = SA_ONESHOT | SA_NOMASK;

	ret = do_sigaction(sig, &new_sa, &old_sa);

	return ret ? ret : (unsigned long)old_sa.sa.sa_handler;
}
int
do_sigaction(int sig, const struct k_sigaction *act, struct k_sigaction *oact)
{
	struct k_sigaction *k;

	if (sig < 1 || sig > _NSIG ||
	    (act && (sig == SIGKILL || sig == SIGSTOP)))//系统对信号SIGKILL和SIGSTOP的响应是不允许改变的
		return -EINVAL;

	k = &current->sig->action[sig-1];

	spin_lock(&current->sig->siglock);

	if (oact)
		*oact = *k;//返回原来的k_sigaction结构

	if (act) {
		*k = *act;//现在的k_sigaction结构赋值给current->sig->action[sig-1]
		sigdelsetmask(&k->sa.sa_mask, sigmask(SIGKILL) | sigmask(SIGSTOP));//SIGKILL和SIGSTOP相应屏蔽位也在每次设置"信号向量"时自动清0

		/*
		 * 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"
		 *
		 * Note the silly behaviour of SIGCHLD: SIG_IGN means that the
		 * signal isn't actually ignored, but does automatic child
		 * reaping, while SIG_DFL is explicitly said by POSIX to force
		 * the signal to be ignored.
		 */

		if (k->sa.sa_handler == SIG_IGN
		    || (k->sa.sa_handler == SIG_DFL
			&& (sig == SIGCONT ||
			    sig == SIGCHLD ||
			    sig == SIGWINCH))) {//新设置的向量为SIG_IGN时,或者为SIG_DFL而涉及的信号为SIGCONT、SIGCHLD和SIGWINCH之一时,如果已经有一个或几个这样的信号在等待处理,那么将这些已到达的信号丢弃
			spin_lock_irq(&current->sigmask_lock);
			if (rm_sig_from_queue(sig, current))//丢弃已经到达的信号
				recalc_sigpending(current);
			spin_unlock_irq(&current->sigmask_lock);
		}
	}

	spin_unlock(&current->sig->siglock);
	return 0;
}

    rm_from_queue丢弃已到达的信号:

static int rm_from_queue(int sig, struct sigpending *s)
{
	struct sigqueue *q, *
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值