首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

linux进程管理之信号处理(转)(2)

linux进程管理之信号处理(转)(2)

二:更改信号的处理函数
在用户空间编程的时候,我们常用的注册信号处理函数的API有:
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
两者都可以更改信号.sigaction是Unix后期才出现的接口.这个接口较signal()更为健壮也更为强大:
Signal()只能为指定的信号设置信号处理函数.而sigaction()不仅可以设置信号处理函数,还可以设置进程的信号掩码.返回设置之前的sigaction结构.sigaction结构在上面已经分析过了.
这两个用户空间的接口对应的系统调用为别是:
sys_signal(int sig, __sighandler_t handler)
sys_sigaction(int sig, const struct old_sigaction __user *act, struct old_sigaction __user *oact)
我们来分析一下内核是怎么样处理的.sys_signal()代码如下:
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;

     //SA_ONESHOT:使用了函数指针之后,将其处理函数设为SIG_DEF
     //SA_NOMASK:  在执行信号处理的时候,不执行任何信号屏弊

     new_sa.sa.sa_flags = SA_ONESHOT | SA_NOMASK;
     //清除信号掩码.表示在处理该信号的时候不要屏弊任何信号
     sigemptyset(&new_sa.sa.sa_mask);

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

     //如果调用错误,返回错误码.如果成功,返回之前的处理函数
     return ret ? ret : (unsigned long)old_sa.sa.sa_handler;
}

sys_sigaction()的代码如下:
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;

     //将用户空间的sigaction 拷贝到内核空间
     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);
     }

     ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);

     //出错,返回错误代码.否则返回信号的sigaction结构
     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_sigaction()进行处理.该函数代码如下:
int do_sigaction(int sig, struct k_sigaction *act, struct k_sigaction *oact)
{
     struct k_sigaction *k;
     sigset_t mask;

     //sig_kernel_only:判断sig是否为SIGKILL SIGSTOP

     //不能为KILL, STOP信号重设处理函数
     if (!valid_signal(sig) || sig < 1 || (act && sig_kernel_only(sig)))
         return -EINVAL;

     //取进程的旧k_sigaction
     k = ¤t->sighand->action[sig-1];

     spin_lock_irq(¤t->sighand->siglock);

     // 如果oact不为空,则将其赋给oact .oact参数返回旧的k_sigaction
     if (oact)
         *oact = *k;

     if (act) {

         //使SIGKILL SIGSTOP不可屏弊
         sigdelsetmask(&act->sa.sa_mask,
                    sigmask(SIGKILL) | sigmask(SIGSTOP));

         //将新的k_siaction赋值到k
         *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"
          */

         //POSIX标准:
         //如果设置的处理为SIG_IGN 或者是SIG_DEL而且是对SIGCONT SIGCHILD SIGWINCH
         //进行重设时
         //如果有一个或者几个这样的信号在等待,则删除之
         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);
                   t = next_thread(t);
              } while (t != current);
         }
     }

     spin_unlock_irq(¤t->sighand->siglock);
     return 0;
}

Rm_from_queue_full()用来将等待队列中的信号删除.并清除等待队列中的位图.代码如下:
static int rm_from_queue_full(sigset_t *mask, struct sigpending *s)
{
     struct sigqueue *q, *n;
     sigset_t m;


     //如果进程接收到了一个信号,但末处理,只是将sigpending->signal简单置位

     //在等待队列中无此信号
     sigandsets(&m, mask, &s->signal);
     if (sigisemptyset(&m))
         return 0;

     // 删除等待的信号
     signandsets(&s->signal, &s->signal, mask);
     list_for_each_entry_safe(q, n, &s->list, list) {
         //如果该信号就是mask中设置的信号
         if (sigismember(mask, q->info.si_signo)) {
              //将其脱链并且初始化
              list_del_init(&q->list);
              //释放对应项
              __sigqueue_free(q);
         }
     }
     return 1;
}
上面有关POSIX标准,请自行查阅相关资料.

三:发送信号
在用户空间中,我们可以用kill()给指定进程发送相应信号.它在用户空间的定义如下所示:
int kill(pid_t pid, int signo)
pid的含义如下所示:
pid > 0 将信号发送给进程ID为pid的进程。
pid == 0 将信号发送给其进程组ID等于发送进程的进程组ID,而且发送进程有许可权向
其发送信号的所有进程。
这里用的术语“所有进程”不包括实现定义的系统进程集。对于大多数U N I X系统,系统
进程集包括:交换进程(pid 0),init (pid 1)以及页精灵进程(pid 2)。
Pid == -1 将信号发送给所有进程.除了swapper(0),init(1)和当前进程
pid < 0 将信号发送给其进程组I D等于p i d绝对值,而且发送进程有许可权向其发送信号
的所有进程。如上所述一样,“所有进程”并不包括系统进程集中的进程.
Kill()的系统调用接口为sys_kill():
asmlinkage long
sys_kill(int pid, int sig)
{
     struct siginfo info;

     //构造一个siginfo
     info.si_signo = sig;
     info.si_errno = 0;
     info.si_code = SI_USER;
     info.si_pid = task_tgid_vnr(current);
     info.si_uid = current->uid;

     return kill_something_info(sig, &info, pid);
}
转到kill_something_info():
static int kill_something_info(int sig, struct siginfo *info, int pid)
{
     int ret;
     rcu_read_lock();

     if (!pid) {

         //将信号发送到进程组
         ret = kill_pgrp_info(sig, info, task_pgrp(current));
     } else if (pid == -1) {

         //将信号发送到所有大于1的进程
         int retval = 0, count = 0;
         struct task_struct * p;

         read_lock(&tasklist_lock);
         for_each_process(p) {
              if (p->pid > 1 && !same_thread_group(p, current)) {
                   int err = group_send_sig_info(sig, info, p);
                   ++count;
                   if (err != -EPERM)
                       retval = err;
              }
         }
         read_unlock(&tasklist_lock);
         ret = count ? retval : -ESRCH;
     } else if (pid < 0) {
         //把信号发送到进程组-pid的所有进程
         ret = kill_pgrp_info(sig, info, find_vpid(-pid));
     } else {

         //将信号发送到pid的进程
         ret = kill_pid_info(sig, info, find_vpid(pid));
     }
     rcu_read_unlock();
     return ret;
}
继承事业,薪火相传
返回列表