日期:2014-05-16  浏览次数:20651 次

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