当前位置: 首页 >> 程序设计 >> Linux信号功能的实现
 

Linux信号功能的实现

作者:      来源:zz     发表时间:2007-05-02     浏览次数:      字号:    

本文档的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);
 }
}
 

[1] [2]

责任编辑 webmaster

 
 
 
 
 
评论更多>>
 
 
 
发表
 
姓名: QQ:
性别: MSN:
E-mail: 主页:
评分: 1 2 3 4 5
评论内容:
验证码:
  
  • 请遵守《互联网电子公告服务管理规定》及中华人民共和国其他各项有关法律法规。
  • 严禁发表危害国家安全、损害国家利益、破坏民族团结、破坏国家宗教政策、破坏社会稳定、侮辱、诽谤、教唆、淫秽等内容的评论 。
  • 用户需对自己在使用本站服务过程中的行为承担法律责任(直接或间接导致的)。
  • 本站管理员有权保留或删除评论内容。
  • 评论内容只代表网友个人观点,与本网站立场无关。
  •