Linux信号功能的实现

本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,
严禁用于任何商业用途。
msn: yfydz_no1@hotmail.com
来源:http://yfydz.cublog.cn

1. 前言

信号是类UNIX系统中一个重要的进程控制方法,向中断一样,可通过向进程发送不同的信号临时中断
程序的正常运行而进入信号处理程序或执行缺省的信号响应,如重新更新程序配置、终止进程等。

在用户空间中,信号处理接口是通过一系列系统调用来实现的,包括signal, sigaction,
sigsuspend, sigaltstack等,进程可通过以上这些接口定义进程的信号处理函数。如果要向其他进
程发信号,可通过kill系统调用实现。

在内核中对信号的处理程序主要定义在 kernel/signal.c, arch/***/kernel/signal.c等文件中,头
文件在 include/linux/signal.h, include/asm/signal.h等。

以下内核代码版本为2.6.19.2。

2. 数据结构

2.1 信号自身基本定义

用于定义信号处理

/* include/asm-generic/signal.h */
// 信号处理函数格式
typedef void __signalfn_t(int);
typedef __signalfn_t __user *__sighandler_t;
// 信号恢复函数
typedef void __restorefn_t(void);
typedef __restorefn_t __user *__sigrestore_t;
// 三个预定义的信号处理函数值
#define SIG_DFL ((__force __sighandler_t)0) /* default signal handling */
#define SIG_IGN ((__force __sighandler_t)1) /* ignore signal */
#define SIG_ERR ((__force __sighandler_t)-1) /* error return from signal */

/* include/asm-i386/signal.h */
#define _NSIG 64
#define _NSIG_BPW 32
#define _NSIG_WORDS (_NSIG / _NSIG_BPW)
typedef unsigned long old_sigset_t; /* at least 32 bits */
// 信号集, 最多64个信号
typedef struct {
unsigned long sig[_NSIG_WORDS];
} sigset_t;

// 老的信号处理结构
struct old_sigaction {
__sighandler_t sa_handler;
old_sigset_t sa_mask;
unsigned long sa_flags;
__sigrestore_t sa_restorer;
};
// 信号处理结构
struct sigaction {
// 信号处理函数
__sighandler_t sa_handler;
// 标志
unsigned long sa_flags;
// 信号恢复函数
__sigrestore_t sa_restorer;
// 信号集
sigset_t sa_mask; /* mask last for extensibility */
};
// 另一个定义
struct k_sigaction {
struct sigaction sa;
};

2.2 信号信息结构定义
用于发送信号

/* include/asm-generic/siginfo.h */
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 */
__ARCH_SI_UID_T _uid; /* sender's uid */
} _kill;
/* POSIX.1b timers */
struct {
timer_t _tid; /* timer id */
int _overrun; /* overrun count */
char _pad[sizeof( __ARCH_SI_UID_T) - sizeof(int)];
sigval_t _sigval; /* same as below */
int _sys_private; /* not to be passed to user */
} _timer;
/* POSIX.1b signals */
struct {
pid_t _pid; /* sender's pid */
__ARCH_SI_UID_T _uid; /* sender's uid */
sigval_t _sigval;
} _rt;
/* SIGCHLD */
struct {
pid_t _pid; /* which child */
__ARCH_SI_UID_T _uid; /* sender's uid */
int _status; /* exit code */
clock_t _utime;
clock_t _stime;
} _sigchld;
/* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
struct {
void __user *_addr; /* faulting insn/memory ref. */
#ifdef __ARCH_SI_TRAPNO
int _trapno; /* TRAP # which caused the signal */
#endif
} _sigfault;
/* SIGPOLL */
struct {
__ARCH_SI_BAND_T _band; /* POLL_IN, POLL_OUT, POLL_MSG */
int _fd;
} _sigpoll;
} _sifields;
} siginfo_t;

3. 定义信号处理函数

signal系统调用在内核中对应的是sys_signal,sigaction对应的是sys_sysaction:

/* kernel/signal.c */
/*
* For backwards compatibility. Functionality superseded by sigaction.
*/
// signal函数已经只是为了后向兼容了,以后尽量还是使用sigaction实现为好
asmlinkage unsigned long
sys_signal(int sig, __sighandler_t handler)
{
struct k_sigaction new_sa, old_sa;
int ret;
// 填写新的信号处理结构参数
// 最重要的是handler参数
new_sa.sa.sa_handler = handler;
new_sa.sa.sa_flags = SA_ONESHOT | SA_NOMASK;
sigemptyset(&new_sa.sa.sa_mask);
// 进入do_sigaction函数处理
ret = do_sigaction(sig, &new_sa, &old_sa);
// 如果返回值为0(成功), 返回原来的信号处理函数
return ret ? ret : (unsigned long)old_sa.sa.sa_handler;
}

/* arch/i386/kernel/signal.c */
// 注意输入的是老的信号处理结构
asmlinkage int
sys_sigaction(int sig, const struct old_sigaction __user *act,
struct old_sigaction __user *oact)
{
struct k_sigaction new_ka, old_ka;
int ret;
// act相当于新的信号处理结构定义
if (act) {
// 从用户层空间拷贝信号处理结构参数
old_sigset_t mask;
if (!access_ok(VERIFY_READ, act, sizeof(*act)) ||
__get_user(new_ka.sa.sa_handler, &act->sa_handler) ||
__get_user(new_ka.sa.sa_restorer, &act->sa_restorer))
return -EFAULT;
__get_user(new_ka.sa.sa_flags, &act->sa_flags);
__get_user(mask, &act->sa_mask);
siginitset(&new_ka.sa.sa_mask, mask);
}
// 进入do_sigaction函数处理
ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);

// 返回值为0(成功)时,返回老的信号处理结构信息
if (!ret && oact) {
if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) ||
__put_user(old_ka.sa.sa_handler, &oact->sa_handler) ||
__put_user(old_ka.sa.sa_restorer, &oact->sa_restorer))
return -EFAULT;
__put_user(old_ka.sa.sa_flags, &oact->sa_flags);
__put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask);
}
return ret;
}

这两个函数基本相同, 但一个是只处理一个信号处理函数,另一个则是处理一个信号处理结构, 参数
要复杂一些,但这两个函数的核心都是do_sysaction:

/* kernel/signal.c */
int do_sigaction(int sig, struct k_sigaction *act, struct k_sigaction *oact)
{
struct k_sigaction *k;
sigset_t mask;
// 检查一下参数是否合法
if (!valid_signal(sig) || sig < 1 || (act && sig_kernel_only(sig)))
return -EINVAL;
// k指向进程原来的信号处理结构
k = &current->sighand->action[sig-1];
spin_lock_irq(&current->sighand->siglock);
if (signal_pending(current)) {
// 目前进程在信号处理状态中
// 此时不能设置信号处理程序
/*
* If there might be a fatal signal pending on multiple
* threads, make sure we take it before changing the action.
*/
spin_unlock_irq(&current->sighand->siglock);
return -ERESTARTNOINTR;
}
// 将进程原来的信号处理结构备份到函数参数指定的原信号处理结构中
if (oact)
*oact = *k;
// 要更新信号处理
if (act) {
// SIGKILL和SIGSTOP这两个信号是不能定义新的处理函数的
// 相应标识始终被清除,因此即使设了也没用
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 (act->sa.sa_handler == SIG_IGN ||
(act->sa.sa_handler == SIG_DFL && sig_kernel_ignore(sig))) {
// 如果新的信号处理函数是忽略该信号
struct task_struct *t = current;
// 清空信号掩码
sigemptyset(&mask);
// 设置掩码中相应信号的位
sigaddset(&mask, sig);
// 从该进程的信号队列中清除该信号
rm_from_queue_full(&mask, &t->signal->shared_pending);
// 从该进程的所有子线程的信号队列中清除该信号
do {
rm_from_queue_full(&mask, &t->pending);
recalc_sigpending_tsk(t);
t = next_thread(t);
} while (t != current);
}
}
spin_unlock_irq(&current->sighand->siglock);
return 0;
}

4. 发送信号

发送信号是通过kill(2)系统调用实现的,内核对应的处理函数是sys_kill:
/* kernel/signal.c */
asmlinkage long
sys_kill(int pid, int sig)
{
struct siginfo info;
// 填写信号信息结构参数, 该结构使用前没清零,也没用填满结构中所有参数
// 信号值
info.si_signo = sig;
info.si_errno = 0;
info.si_code = SI_USER;
info.si_pid = current->tgid;
info.si_uid = current->uid;
// 向pid进程发送信号
return kill_something_info(sig, &info, pid);
}

/* kernel/signal.h */
/*
* kill_something_info() interprets pid in interesting ways just like kill(2).
*
* POSIX specifies that kill(-1,sig) is unspecified, but what we have
* is probably wrong. Should make it like BSD or SYSV.
*/
static int kill_something_info(int sig, struct siginfo *info, int pid)
{
if (!pid) {
// pid为0, 是向一个进程组发信号
return kill_pg_info(sig, info, process_group(current));
} else if (pid == -1) {
// pid为-1. 向所有其他组的进程发送信号
int retval = 0, count = 0;
struct task_struct * p;
read_lock(&tasklist_lock);
for_each_process(p) {
if (p->pid > 1 && p->tgid != current->tgid) {
int err = group_send_sig_info(sig, info, p);
++count;
if (err != -EPERM)
retval = err;
}
}
read_unlock(&tasklist_lock);
return count ? retval : -ESRCH;
} else if (pid < 0) {
// 其他的负pid值,将pid取反为正值后向进程组发送信号
return kill_pg_info(sig, info, -pid);
} else {
// 一般情况, 向指定pid的进程发送信号
return kill_proc_info(sig, info, pid);
}
}

函数调用流程图:
kill_proc_info kill_pg_info
| |
V V
kill_pid_info __kill_pg_info
| |
| V
| __kill_pgrp_info
| |
+----------------------+
|
V
group_send_sig_info
|
V
__group_send_sig_info
|
V
send_signal

/* kernel/signal.c */
static int send_signal(int sig, struct siginfo *info, struct task_struct *t,
struct sigpending *signals)
{
struct sigqueue * q = NULL;
int ret = 0;
/*
* fast-pathed signals for kernel-internal things like SIGSTOP
* or SIGKILL.
*/
// 强制发送信号(SIGKILL或SIGSTOP), 直接发送
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. */
// 分配信号队列结构, 从sigqueue_cachep中分配(这个一个kmem_cache)
q = __sigqueue_alloc(t, GFP_ATOMIC, (sig < SIGRTMIN &&
(is_si_special(info) ||
info->si_code >= 0)));
if (q) {
// 分配成功
// 将该信号队列挂接到待发送信号链表末尾, 注意add_list是添加到链表头的
list_add_tail(&q->list, &signals->list);
switch ((unsigned long) info) {
// 先看是否是几个特殊的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 = current->pid;
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:
// 设置信号集中相应的信号标志
sigaddset(&signals->signal, sig);
return ret;
}

/* include/asm-i386/signal.h */
#define sigaddset(set,sig) \
(__builtin_constant_p(sig) ? \
__const_sigaddset((set),(sig)) : \
__gen_sigaddset((set),(sig)))
static __inline__ void __gen_sigaddset(sigset_t *set, int _sig)
{
__asm__("btsl %1,%0" : "+m"(*set) : "Ir"(_sig - 1) : "cc");
}
static __inline__ void __const_sigaddset(sigset_t *set, int _sig)
{
unsigned long sig = _sig - 1;
set->sig[sig / _NSIG_BPW] |= 1 << (sig % _NSIG_BPW);
}

5. 处理信号

/* arch/i386/kernel/signal.c */
/*
* notification of userspace execution resumption
* - triggered by the TIF_WORK_MASK flags
*/
__attribute__((regparm(3)))
void do_notify_resume(struct pt_regs *regs, void *_unused,
__u32 thread_info_flags)
{
/* Pending single-step? */
if (thread_info_flags & _TIF_SINGLESTEP) {
regs->eflags |= TF_MASK;
clear_thread_flag(TIF_SINGLESTEP);
}
/* deal with pending signal delivery */
// 派发待发送的信号
if (thread_info_flags & (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK))
do_signal(regs);

clear_thread_flag(TIF_IRET);
}

/*
* Note that 'init' is a special process: it doesn't get signals it doesn't
* want to handle. Thus you cannot kill init even with a SIGKILL even by
* mistake.
*/
static void fastcall do_signal(struct pt_regs *regs)
{
siginfo_t info;
int signr;
struct k_sigaction ka;
sigset_t *oldset;
/*
* We want the common case to go fast, which
* is why we may in certain cases get here from
* kernel mode. Just return without doing anything
* if so. vm86 regs switched out by assembly code
* before reaching here, so testing against kernel
* CS suffices.
*/
// 信号处理函数是在用户空间
if (!user_mode(regs))
return;
// 原先的信息集指针
if (test_thread_flag(TIF_RESTORE_SIGMASK))
oldset = &current->saved_sigmask;
else
oldset = &current->blocked;
// 找要发送的信号
signr = get_signal_to_deliver(&info, &ka, regs, NULL);
if (signr > 0) {
// 找到一个信号
/* Reenable any watchpoints before delivering the
* signal to user space. The processor register will
* have been cleared if the watchpoint triggered
* inside the kernel.
*/
if (unlikely(current->thread.debugreg[7]))
set_debugreg(current->thread.debugreg[7], 7);
/* Whee! Actually deliver the signal. */
// 处理信号
if (handle_signal(signr, &info, &ka, oldset, regs) == 0) {
/* a signal was successfully delivered; the saved
* sigmask will have been stored in the signal frame,
* and will be restored by sigreturn, so we can simply
* clear the TIF_RESTORE_SIGMASK flag */
if (test_thread_flag(TIF_RESTORE_SIGMASK))
clear_thread_flag(TIF_RESTORE_SIGMASK);
}
return;
}
// 无信号的情况
/* Did we come from a system call? */
if (regs->orig_eax >= 0) {
/* Restart the system call - no handlers present */
switch (regs->eax) {
case -ERESTARTNOHAND:
case -ERESTARTSYS:
case -ERESTARTNOINTR:
regs->eax = regs->orig_eax;
regs->eip -= 2;
break;
case -ERESTART_RESTARTBLOCK:
regs->eax = __NR_restart_syscall;
regs->eip -= 2;
break;
}
}
/* if there's no signal to deliver, we just put the saved sigmask
* back */
if (test_thread_flag(TIF_RESTORE_SIGMASK)) {
clear_thread_flag(TIF_RESTORE_SIGMASK);
sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
}
}

/*
* OK, we're invoking a handler
*/
static int
handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
sigset_t *oldset, struct pt_regs * regs)
{
int ret;
/* Are we from a system call? */
if (regs->orig_eax >= 0) {
// 判断当前是否在系统调用中
/* If so, check system call restarting.. */
switch (regs->eax) {
case -ERESTART_RESTARTBLOCK:
case -ERESTARTNOHAND:
regs->eax = -EINTR;
break;
case -ERESTARTSYS:
if (!(ka->sa.sa_flags & SA_RESTART)) {
regs->eax = -EINTR;
break;
}
/* fallthrough */
case -ERESTARTNOINTR:
regs->eax = regs->orig_eax;
regs->eip -= 2;
}
}
/*
* If TF is set due to a debugger (PT_DTRACE), clear the TF flag so
* that register information in the sigcontext is correct.
*/
if (unlikely(regs->eflags & TF_MASK)
&& likely(current->ptrace & PT_DTRACE)) {
current->ptrace &= ~PT_DTRACE;
regs->eflags &= ~TF_MASK;
}
/* Set up the stack frame */
// 建立进入信号处理函数前堆栈帧
if (ka->sa.sa_flags & SA_SIGINFO)
ret = setup_rt_frame(sig, ka, info, oldset, regs);
else
ret = setup_frame(sig, ka, oldset, regs);
if (ret == 0) {
spin_lock_irq(&current->sighand->siglock);
sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
if (!(ka->sa.sa_flags & SA_NODEFER))
sigaddset(&current->blocked,sig);
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
}
return ret;
}

/* kernel/signal.c */
// 找要发送的信号
int get_signal_to_deliver(siginfo_t *info, struct k_sigaction *return_ka,
struct pt_regs *regs, void *cookie)
{
sigset_t *mask = &current->blocked;
int signr = 0;
try_to_freeze();
relock:
spin_lock_irq(&current->sighand->siglock);
for (;;) {
struct k_sigaction *ka;
// unlikely的处理就是大部分情况下可忽略
if (unlikely(current->signal->group_stop_count > 0) &&
handle_group_stop())
goto relock;
// 从当前进程的信号队列中取信号
signr = dequeue_signal(current, mask, info);
if (!signr)
// 无信号
break; /* will return 0 */
// 有信号
if ((current->ptrace & PT_PTRACED) && signr != SIGKILL) {
// 当前进程是被跟踪调试中
ptrace_signal_deliver(regs, cookie);
/* Let the debugger run. */
ptrace_stop(signr, signr, info);
/* We're back. Did the debugger cancel the sig? */
signr = current->exit_code;
if (signr == 0)
continue;
current->exit_code = 0;
/* Update the siginfo structure if the signal has
changed. If the debugger wanted something
specific in the siginfo structure then it should
have updated *info via PTRACE_SETSIGINFO. */
if (signr != info->si_signo) {
info->si_signo = signr;
info->si_errno = 0;
info->si_code = SI_USER;
info->si_pid = current->parent->pid;
info->si_uid = current->parent->uid;
}
/* If the (new) signal is now blocked, requeue it. */
if (sigismember(&current->blocked, signr)) {
specific_send_sig_info(signr, info, current);
continue;
}
}
// 获取当前进程的信号处理函数
ka = &current->sighand->action[signr-1];
if (ka->sa.sa_handler == SIG_IGN) /* Do nothing. */
// 该信号是要忽略的,重新找下一信号
continue;
if (ka->sa.sa_handler != SIG_DFL) {
// 定义了具体的函数处理函数, 复制要返回信号处理结构
/* Run the handler. */
*return_ka = *ka;
if (ka->sa.sa_flags & SA_ONESHOT)
ka->sa.sa_handler = SIG_DFL;
break; /* will return non-zero "signr" value */
}
/*
* Now we are doing the default action for this signal.
*/
// 否则执行系统缺省的信号处理
if (sig_kernel_ignore(signr)) /* Default is nothing. */
continue;
/* Init gets no signals it doesn't want. */
if (current == child_reaper)
continue;
if (sig_kernel_stop(signr)) {
/*
* The default action is to stop all threads in
* the thread group. The job control signals
* do nothing in an orphaned pgrp, but SIGSTOP
* always works. Note that siglock needs to be
* dropped during the call to is_orphaned_pgrp()
* because of lock ordering with tasklist_lock.
* This allows an intervening SIGCONT to be posted.
* We need to check for that and bail out if necessary.
*/
if (signr != SIGSTOP) {
spin_unlock_irq(&current->sighand->siglock);
/* signals can be posted during this window */
if (is_orphaned_pgrp(process_group(current)))
goto relock;
spin_lock_irq(&current->sighand->siglock);
}
if (likely(do_signal_stop(signr))) {
/* It released the siglock. */
goto relock;
}
/*
* We didn't actually stop, due to a race
* with SIGCONT or something like that.
*/
continue;
}
spin_unlock_irq(&current->sighand->siglock);
/*
* Anything else is fatal, maybe with a core dump.
*/
current->flags |= PF_SIGNALED;
if (sig_kernel_coredump(signr)) {
/*
* If it was able to dump core, this kills all
* other threads in the group and synchronizes with
* their demise. If we lost the race with another
* thread getting here, it set group_exit_code
* first and our do_group_exit call below will use
* that value and ignore the one we pass it.
*/
do_coredump((long)signr, signr, regs);
}
/*
* Death signals, no core dump.
*/
do_group_exit(signr);
/* NOTREACHED */
}
spin_unlock_irq(&current->sighand->siglock);
return signr;
}

6. 结论

Linux下的信号实现基本符合了APUE中对信号处理的描述, SIGKILL/SIGSTOP不可忽略, 发送给某进程的信号会排队, 信号可能会丢失等等.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值