亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

  免費(fèi)注冊 查看新帖 |

Chinaunix

  平臺(tái) 論壇 博客 文庫
最近訪問板塊 發(fā)新帖
查看: 1242 | 回復(fù): 0
打印 上一主題 下一主題

linux進(jìn)程管理之信號(hào)處理 [復(fù)制鏈接]

論壇徽章:
0
跳轉(zhuǎn)到指定樓層
1 [收藏(0)] [報(bào)告]
發(fā)表于 2008-11-11 16:58 |只看該作者 |倒序?yàn)g覽

信號(hào)是操作系統(tǒng)中一種很重要的通信方式.近幾個(gè)版本中,信號(hào)處理這部份很少有大的變動(dòng).我們從用戶空間的信號(hào)應(yīng)用來分析Linux內(nèi)核的信號(hào)實(shí)現(xiàn)方式.
一:信號(hào)有關(guān)的數(shù)據(jù)結(jié)構(gòu)
在task_struct中有關(guān)的信號(hào)結(jié)構(gòu):
struct task_struct {
……
//指向進(jìn)程信號(hào)描述符
    struct signal_struct *signal;
     //指向信號(hào)的處理描述符
     struct sighand_struct *sighand;

     //阻塞信號(hào)的掩碼
     sigset_t blocked, real_blocked;
     //保存的信號(hào)掩碼.當(dāng)定義TIF_RESTORE_SIGMASK的時(shí)候,恢復(fù)信號(hào)掩碼
     sigset_t saved_sigmask;     /* To be restored with TIF_RESTORE_SIGMASK */
     //存放掛起的信號(hào)
     struct sigpending pending;
     
     //指定信號(hào)處理程序的棧地址
     unsigned long sas_ss_sp;
     //信號(hào)處理程序的棧大小
     size_t sas_ss_size;
     //反映向一個(gè)函數(shù)的指針,設(shè)備驅(qū)動(dòng)用此來阻塞進(jìn)程的某些信號(hào)
     int (*notifier)(void *priv);
     //notifier()的參數(shù)
     void *notifier_data;
     //驅(qū)動(dòng)程序通過notifier()所阻塞信號(hào)的位圖
     sigset_t *notifier_mask;
     ……
}
Sigset_t的數(shù)據(jù)結(jié)構(gòu)如下:
//信號(hào)位圖.
typedef struct {
         //在x86中需要64位掩碼,即2元素的32位數(shù)組
     unsigned long sig[_NSIG_WORDS];
} sigset_t;
#define _NSIG      64

#ifdef __i386__
# define _NSIG_BPW 32
#else
# define _NSIG_BPW 64
#endif

#define _NSIG_WORDS    (_NSIG / _NSIG_BPW)
在linux中共有64個(gè)信號(hào).前32個(gè)為常規(guī)信號(hào).后32個(gè)為實(shí)時(shí)信號(hào).實(shí)時(shí)信號(hào)與常規(guī)信號(hào)的唯一區(qū)別就是實(shí)時(shí)信號(hào)會(huì)排隊(duì)等候.
struct sigpending結(jié)構(gòu)如下:
//信號(hào)等待隊(duì)列
struct sigpending {
     struct list_head list;
     //如果某信號(hào)在等待,則該信號(hào)表示的位置1
     sigset_t signal;
};

Struct sighand_struct的結(jié)構(gòu)如下:
struct sighand_struct {
//引用計(jì)數(shù)
atomic_t      count;
//信號(hào)向量表
struct k_sigaction action[_NSIG];
spinlock_t         siglock;
wait_queue_head_t  signalfd_wqh;
}
同中斷處理一樣,每一個(gè)信號(hào)都對應(yīng)action中的一個(gè)處理函數(shù).
struct k_sigaction結(jié)構(gòu)如下示:
struct sigaction {
     //信號(hào)處理函數(shù)
__sighandler_t sa_handler;
     //指定的信號(hào)處理標(biāo)志
unsigned long sa_flags;
__sigrestore_t sa_restorer;
     //在運(yùn)行處理信號(hào)的時(shí)候要屏弊的信號(hào)
sigset_t sa_mask;      /* mask last for extensibility */
};

Struct signal_struct結(jié)構(gòu)如下:
struct signal_struct {

//共享計(jì)數(shù)
atomic_t      count;
//線程組內(nèi)存活的信號(hào)
atomic_t      live;

//wait_chldexit:子進(jìn)程的等待隊(duì)列
wait_queue_head_t  wait_chldexit;     /* for wait4() */

/* current thread group signal load-balancing target: */
//線程組內(nèi)最使收到信號(hào)的進(jìn)程
struct task_struct *curr_target;

/* shared signal handling: */
//共享信號(hào)的等待隊(duì)列
struct sigpending  shared_pending;

/* thread group exit support */
//線程組的終止碼
int           group_exit_code;
/* overloaded:
* - notify group_exit_task when ->count is equal to notify_count
* - everyone except group_exit_task is stopped during signal delivery
*   of fatal signals, group_exit_task processes the signal.
*/

//當(dāng)kill 掉整個(gè)線程組的時(shí)候使用
struct task_struct *group_exit_task;
//當(dāng)kill 掉整個(gè)線程組的時(shí)候使用
int           notify_count;

/* thread group stop support, overloads group_exit_code too */
//當(dāng)整個(gè)線程組停止的時(shí)候使用     
int           group_stop_count;
unsigned int       flags; /* see SIGNAL_* flags below */
……
}

上述所討論的數(shù)據(jù)結(jié)構(gòu)可以用下圖表示(摘自>):


二:更改信號(hào)的處理函數(shù)
在用戶空間編程的時(shí)候,我們常用的注冊信號(hào)處理函數(shù)的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);
兩者都可以更改信號(hào).sigaction是Unix后期才出現(xiàn)的接口.這個(gè)接口較signal()更為健壯也更為強(qiáng)大:
Signal()只能為指定的信號(hào)設(shè)置信號(hào)處理函數(shù).而sigaction()不僅可以設(shè)置信號(hào)處理函數(shù),還可以設(shè)置進(jìn)程的信號(hào)掩碼.返回設(shè)置之前的sigaction結(jié)構(gòu).sigaction結(jié)構(gòu)在上面已經(jīng)分析過了.
這兩個(gè)用戶空間的接口對應(yīng)的系統(tǒng)調(diào)用為別是:
sys_signal(int sig, __sighandler_t handler)
sys_sigaction(int sig, const struct old_sigaction __user *act, struct old_sigaction __user *oact)
我們來分析一下內(nèi)核是怎么樣處理的.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:使用了函數(shù)指針之后,將其處理函數(shù)設(shè)為SIG_DEF
     //SA_NOMASK:  在執(zhí)行信號(hào)處理的時(shí)候,不執(zhí)行任何信號(hào)屏弊
     
     new_sa.sa.sa_flags = SA_ONESHOT | SA_NOMASK;
     //清除信號(hào)掩碼.表示在處理該信號(hào)的時(shí)候不要屏弊任何信號(hào)
     sigemptyset(&new_sa.sa.sa_mask);

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

     //如果調(diào)用錯(cuò)誤,返回錯(cuò)誤碼.如果成功,返回之前的處理函數(shù)
     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 拷貝到內(nèi)核空間
     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);

     //出錯(cuò),返回錯(cuò)誤代碼.否則返回信號(hào)的sigaction結(jié)構(gòu)
     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;
}
由此可以看出,兩個(gè)函數(shù)最終都會(huì)調(diào)用do_sigaction()進(jìn)行處理.該函數(shù)代碼如下:
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信號(hào)重設(shè)處理函數(shù)
     if (!valid_signal(sig) || sig
         return -EINVAL;

     //取進(jìn)程的舊k_sigaction
     k = &current->sighand->action[sig-1];

     spin_lock_irq(&current->sighand->siglock);

     // 如果oact不為空,則將其賦給oact .oact參數(shù)返回舊的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標(biāo)準(zhǔn):
         //如果設(shè)置的處理為SIG_IGN 或者是SIG_DEL而且是對SIGCONT SIGCHILD SIGWINCH
         //進(jìn)行重設(shè)時(shí)
         //如果有一個(gè)或者幾個(gè)這樣的信號(hào)在等待,則刪除之
         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);

              //如果不是共享信號(hào),在線程中的線程等待隊(duì)列中將該信號(hào)
              //刪除
              do {
                   rm_from_queue_full(&mask, &t->pending);
                   t = next_thread(t);
              } while (t != current);
         }
     }

     spin_unlock_irq(&current->sighand->siglock);
     return 0;
}

Rm_from_queue_full()用來將等待隊(duì)列中的信號(hào)刪除.并清除等待隊(duì)列中的位圖.代碼如下:
static int rm_from_queue_full(sigset_t *mask, struct sigpending *s)
{
     struct sigqueue *q, *n;
     sigset_t m;


     //如果進(jìn)程接收到了一個(gè)信號(hào),但末處理,只是將sigpending->signal簡單置位

     //在等待隊(duì)列中無此信號(hào)
     sigandsets(&m, mask, &s->signal);
     if (sigisemptyset(&m))
         return 0;

     // 刪除等待的信號(hào)
     signandsets(&s->signal, &s->signal, mask);
     list_for_each_entry_safe(q, n, &s->list, list) {
         //如果該信號(hào)就是mask中設(shè)置的信號(hào)
         if (sigismember(mask, q->info.si_signo)) {
              //將其脫鏈并且初始化
              list_del_init(&q->list);
              //釋放對應(yīng)項(xiàng)
              __sigqueue_free(q);
         }
     }
     return 1;
}
上面有關(guān)POSIX標(biāo)準(zhǔn),請自行查閱相關(guān)資料.

三:發(fā)送信號(hào)
在用戶空間中,我們可以用kill()給指定進(jìn)程發(fā)送相應(yīng)信號(hào).它在用戶空間的定義如下所示:
int kill(pid_t pid, int signo)
pid的含義如下所示:
pid > 0 將信號(hào)發(fā)送給進(jìn)程ID為pid的進(jìn)程。
pid == 0 將信號(hào)發(fā)送給其進(jìn)程組ID等于發(fā)送進(jìn)程的進(jìn)程組ID,而且發(fā)送進(jìn)程有許可權(quán)向
其發(fā)送信號(hào)的所有進(jìn)程。
這里用的術(shù)語“所有進(jìn)程”不包括實(shí)現(xiàn)定義的系統(tǒng)進(jìn)程集。對于大多數(shù)U N I X系統(tǒng),系統(tǒng)
進(jìn)程集包括:交換進(jìn)程(pid 0),init (pid 1)以及頁精靈進(jìn)程(pid 2)。
Pid == -1 將信號(hào)發(fā)送給所有進(jìn)程.除了swapper(0),init(1)和當(dāng)前進(jìn)程
pid
的所有進(jìn)程。如上所述一樣,“所有進(jìn)程”并不包括系統(tǒng)進(jìn)程集中的進(jìn)程.
Kill()的系統(tǒng)調(diào)用接口為sys_kill():
asmlinkage long
sys_kill(int pid, int sig)
{
     struct siginfo info;

     //構(gòu)造一個(gè)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);
}
轉(zhuǎn)到kill_something_info():
static int kill_something_info(int sig, struct siginfo *info, int pid)
{
     int ret;
     rcu_read_lock();
     
     if (!pid) {

         //將信號(hào)發(fā)送到進(jìn)程組
         ret = kill_pgrp_info(sig, info, task_pgrp(current));
     } else if (pid == -1) {

         //將信號(hào)發(fā)送到所有大于1的進(jìn)程
         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
         //把信號(hào)發(fā)送到進(jìn)程組-pid的所有進(jìn)程
         ret = kill_pgrp_info(sig, info, find_vpid(-pid));
     } else {

         //將信號(hào)發(fā)送到pid的進(jìn)程
         ret = kill_pid_info(sig, info, find_vpid(pid));
     }
     rcu_read_unlock();
     return ret;
}
假設(shè)pid > 0.轉(zhuǎn)入kill_pid_info().即把信號(hào)發(fā)送到pid的進(jìn)程
int kill_pid_info(int sig, struct siginfo *info, struct pid *pid)
{
     int error;
     struct task_struct *p;

     rcu_read_lock();
     if (unlikely(sig_needs_tasklist(sig)))
         read_lock(&tasklist_lock);

     //找到進(jìn)程號(hào)為pid 的進(jìn)程
     p = pid_task(pid, PIDTYPE_PID);
     error = -ESRCH;
     if (p)
         error = group_send_sig_info(sig, info, p);

     if (unlikely(sig_needs_tasklist(sig)))
         read_unlock(&tasklist_lock);
     rcu_read_unlock();
     return error;
}
在這里將pid轉(zhuǎn)化為對應(yīng)的task_struct.然后調(diào)用group_send_sig_info().代碼如下:
int group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p)
{
     unsigned long flags;
     int ret;

     //檢查是否有權(quán)限發(fā)送信號(hào)
     ret = check_kill_permission(sig, info, p);

     if (!ret && sig) {
         ret = -ESRCH;

              //為了防止競爭.加鎖
         if (lock_task_sighand(p, &flags)) {
              //發(fā)送信號(hào)
              ret = __group_send_sig_info(sig, info, p);

              //解鎖
              unlock_task_sighand(p, &flags);
         }
     }

     return ret;
}
首先,要給進(jìn)程發(fā)送信號(hào),應(yīng)該先判斷它是否具有這樣的權(quán)限.判斷的依據(jù)為:
如果是用戶空間發(fā)送的信號(hào),檢查其是否有相應(yīng)的權(quán)限
必須要滿足以下幾個(gè)條件中的任一個(gè)才可以發(fā)送:
1:發(fā)送信號(hào)者必須擁有相關(guān)的權(quán)能
2: 如果是發(fā)送SIGCONT且發(fā)送進(jìn)程與種目標(biāo)進(jìn)程處于同一個(gè)注冊會(huì)話中
3:屬于同一個(gè)用戶的進(jìn)程
轉(zhuǎn)入__group_send_sig_info():
int
__group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p)
{
     int ret = 0;

     assert_spin_locked(&p->sighand->siglock);

     //對會(huì)引起進(jìn)程停止的進(jìn)程進(jìn)行一些特定的處理
     handle_stop_signal(sig, p);

     /* Short-circuit ignored signals.  */

     //判斷信號(hào)是不是被忽略
     if (sig_ignored(p, sig))
         return ret;

     //如果不是一個(gè)RT信號(hào),且等待隊(duì)列中已經(jīng)有這個(gè)信號(hào)了,返回即可
     //TODO: 常規(guī)信號(hào)是不會(huì)排隊(duì)的
     if (LEGACY_QUEUE(&p->signal->shared_pending, sig))
         /* This is a non-RT signal and we already have one queued.  */
         return ret;

     /*
      * Put this signal on the shared-pending queue, or fail with EAGAIN.
      * We always use the shared queue for process-wide signals,
      * to avoid several races.
      */
     ret = send_signal(sig, info, p, &p->signal->shared_pending);
     if (unlikely(ret))
         return ret;

     //喚醒該進(jìn)程對該信號(hào)進(jìn)行處理
     //如果該進(jìn)程對此信號(hào)進(jìn)行了屏弊,則選擇線程組中一個(gè)合適的進(jìn)程來喚醒
     __group_complete_signal(sig, p);
     return 0;
}

具體的進(jìn)程發(fā)送過程是在send_signal()完成的.它的代碼如下:

static int send_signal(int sig, struct siginfo *info, struct task_struct *t,
              struct sigpending *signals)
{
     struct sigqueue * q = NULL;
     int ret = 0;

     /*
      * Deliver the signal to listening signalfds. This must be called
      * with the sighand lock held.
      */

     //選擇編譯函數(shù)
     signalfd_notify(t, sig);

     /*
      * fast-pathed signals for kernel-internal things like SIGSTOP
      * or SIGKILL.
      */
     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.  */

     //分配一個(gè)sigqueue
     q = __sigqueue_alloc(t, GFP_ATOMIC, (sig
                            (is_si_special(info) ||
                             info->si_code >= 0)));
     if (q) {

         //將分配的sigqueue 加入等待隊(duì)列
         list_add_tail(&q->list, &signals->list);
         switch ((unsigned long) 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 = task_pid_vnr(current);
              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:
     //更新等待隊(duì)列的signal 位圖,表示收到了一個(gè)信號(hào),但沒有處理
     sigaddset(&signals->signal, sig);
     return ret;
}
經(jīng)過這個(gè)過程,我們看到了進(jìn)程怎么將信號(hào)發(fā)送到另外的進(jìn)程.特別要注意的是,目標(biāo)進(jìn)程接收到信號(hào)之后會(huì)將其喚醒.這時(shí)如果目標(biāo)進(jìn)程是系統(tǒng)調(diào)用阻塞狀態(tài)就會(huì)將它的系統(tǒng)調(diào)用中斷.
另外,內(nèi)核經(jīng)常使用force_sig_info()/force_sig()來給進(jìn)程發(fā)送信號(hào).這樣的信號(hào)經(jīng)常不可以忽略,不可以阻塞.我們來看一下它的處理.代碼如下:
int
force_sig_info(int sig, struct siginfo *info, struct task_struct *t)
{
     unsigned long int flags;
     int ret, blocked, ignored;
     struct k_sigaction *action;

     spin_lock_irqsave(&t->sighand->siglock, flags);

     //取進(jìn)程的信號(hào)的處理函數(shù)
     action = &t->sighand->action[sig-1];

     //如果該信號(hào)被忽略或者該信號(hào)被阻塞
     ignored = action->sa.sa_handler == SIG_IGN;
     blocked = sigismember(&t->blocked, sig);
     if (blocked || ignored) {
         //重信號(hào)處理函數(shù)為默認(rèn)的處理
         action->sa.sa_handler = SIG_DFL;

         //如果信號(hào)被屏弊
         if (blocked) {
              //清除信號(hào)屏弊位
              sigdelset(&t->blocked, sig);
              //重新計(jì)算進(jìn)程是否有末處理的信號(hào)
              recalc_sigpending_and_wake(t);
         }
     }

     //"特殊"的信號(hào)發(fā)送
     ret = specific_send_sig_info(sig, info, t);
     spin_unlock_irqrestore(&t->sighand->siglock, flags);

     return ret;
}
當(dāng)進(jìn)程的信號(hào)阻塞標(biāo)志被更改時(shí),就會(huì)引起TIF_SIGPENDING標(biāo)志的變化.對于TIF_SIGPENDING標(biāo)志的檢測是在recalc_sigpending_and_wake()調(diào)用recalc_sigpending_tsk()來完成的.它實(shí)際是判斷等待隊(duì)列中是否有沒有被阻塞的信號(hào).如果有,則設(shè)置TIF_SIGPENDING標(biāo)志.

specific_send_sig_info()內(nèi)核用于將信號(hào)發(fā)送到進(jìn)程.我們比較一下它跟用戶空間的發(fā)送有什么不同.它的代碼如下:
static int
specific_send_sig_info(int sig, struct siginfo *info, struct task_struct *t)
{
     int ret = 0;

     BUG_ON(!irqs_disabled());
     assert_spin_locked(&t->sighand->siglock);

     /* Short-circuit ignored signals.  */
     
     //信號(hào)被忽略,退出
     if (sig_ignored(t, sig))
         goto out;

     /* Support queueing exactly one non-rt signal, so that we
        can get more detailed information about the cause of
        the signal. */

     //如果不是實(shí)時(shí)信號(hào),且已經(jīng)有信號(hào)在等待隊(duì)列中了.直接等待(不排隊(duì))
     if (LEGACY_QUEUE(&t->pending, sig))
         goto out;
     //將信號(hào)發(fā)送到目標(biāo)進(jìn)程
     ret = send_signal(sig, info, t, &t->pending);
     // TODO: 這里調(diào)用signal_wake_up()直接喚醒進(jìn)程
     if (!ret && !sigismember(&t->blocked, sig))
         signal_wake_up(t, sig == SIGKILL);
out:
     return ret;
}
這樣,內(nèi)核就將信號(hào)傳送給目標(biāo)進(jìn)程.無論進(jìn)程用什么樣的方式,都不能阻止對此信號(hào)的處理.

四:信號(hào)的處理
信號(hào)處理的時(shí)機(jī):每次從內(nèi)核空間返回用戶空間時(shí),都會(huì)檢查當(dāng)前進(jìn)程是否有末處理的信號(hào).如果有,則對信號(hào)進(jìn)行處理
信號(hào)的處理函數(shù)如下:
static void fastcall do_signal(struct pt_regs *regs)
{
     siginfo_t info;
     int signr;
     struct k_sigaction ka;
     sigset_t *oldset;

     //判斷是否是處于返回到用戶空間的前夕.不需要處理
     if (!user_mode(regs))
         return;

     //要從task->saved_sigmask中恢復(fù)進(jìn)程信號(hào)掩碼
     if (test_thread_flag(TIF_RESTORE_SIGMASK))
         oldset = &current->saved_sigmask;
     else
         oldset = &current->blocked;

     //對等待信號(hào)的處理
     //只有遇到用戶重設(shè)信號(hào)處理函數(shù)的信號(hào)或者處理完等待信號(hào)才會(huì)返回
     signr = get_signal_to_deliver(&info, &ka, regs, NULL);
     if (signr > 0) {

         //對用戶設(shè)置了信號(hào)處理函數(shù)的信號(hào)處理
         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) {
              if (test_thread_flag(TIF_RESTORE_SIGMASK))
                   clear_thread_flag(TIF_RESTORE_SIGMASK);
         }

         return;
     }


     //沒有Catch信號(hào)的系統(tǒng)調(diào)用重啟
     
     /* 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;
         //如果是返回-ERESTART_RESTARTBLOCK ,返回用戶空間后重新發(fā)起
         //系統(tǒng)調(diào)用.系統(tǒng)調(diào)用號(hào)為__NR_restart_syscall

         //一般用在與timer有關(guān)的系統(tǒng)調(diào)用中
         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)) {
         //清除TIF_RESTORE_SIGMASK 并恢復(fù)信號(hào)掩碼
         clear_thread_flag(TIF_RESTORE_SIGMASK);
         sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
     }
}
正好我們在上節(jié)發(fā)送信號(hào)中所論述的一樣,信號(hào)可能會(huì)引起系統(tǒng)調(diào)用中斷.這里必須要采取必要的措施來使系統(tǒng)調(diào)用重啟.
關(guān)于返回值與重啟還是忽略如下表如示(摘自 understanding the linux kernel >>):
Signal
Action
EINTR
ERESTARTSYS
ERESTARTNOHAND
ERESTART_RESTARTBLOCK
a

ERESTARTNOINTR
Default
Terminate
Reexecute
Reexecute
Reexecute
Ignore
Terminate
Reexecute
Reexecute
Reexecute
Catch
Terminate
Depends
Terminate
Reexecute

有必要關(guān)注一下上面的系統(tǒng)調(diào)用重啟過程:
Regs參數(shù)表示用戶空的硬件環(huán)境.regs->eax是表示返回用戶空間后的eax寄存器的值.regs->eip是返回用戶空間后執(zhí)行的指針地址. regs->orig_eax是表示系統(tǒng)調(diào)用時(shí)eax的值,里面存放著系統(tǒng)調(diào)用號(hào).請參閱本站的有關(guān)中斷初始化的文檔.
Regs->eip -= 2 ,為什么eip要減2呢?因?yàn)榘l(fā)現(xiàn)系統(tǒng)調(diào)用是int 0x80 指令.中斷后,eip會(huì)指向int 80后面的一條指令.這樣,如果要重新執(zhí)新int 0x80.那就必須要把eip返回兩條指令.
轉(zhuǎn)入get_signal_to_deliver():
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;

     //選擇編譯函數(shù)
     try_to_freeze();

relock:
     spin_lock_irq(&current->sighand->siglock);
     for (;;) {
         struct k_sigaction *ka;

         if (unlikely(current->signal->group_stop_count > 0) &&
             handle_group_stop())
              goto relock;

         //從等待隊(duì)列中取信號(hào)
         signr = dequeue_signal(current, mask, info);


         //信號(hào)為空,退出
         if (!signr)
              break; /* will return 0 */

         //當(dāng)前進(jìn)程正在被跟蹤
         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 = task_pid_vnr(current->parent);
                   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];

         //信號(hào)被忽略,不做任何處理
         if (ka->sa.sa_handler == SIG_IGN) /* Do nothing.  */
              continue;

         //如果不為默認(rèn)操作.也就是說用戶已經(jīng)重置了該信號(hào)的處理
         //這樣情況下會(huì)調(diào)用break退出循環(huán)
         if (ka->sa.sa_handler != SIG_DFL) {
              /* Run the handler.  */
              *return_ka = *ka;

              //如果定義了SA_ONESHOT 標(biāo)志,指明信號(hào)處理完之后,恢復(fù)信號(hào)的默認(rèn)處理
              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.
          */

         //如果是內(nèi)核所忽略的信號(hào),不做任何處理

         //這里注意了.Child信號(hào)的默認(rèn)處理是忽略.這就是形成僵尸進(jìn)程
         //的主要原因
         if (sig_kernel_ignore(signr)) /* Default is nothing. */
              continue;

         /*
          * Global init gets no signals it doesn't want.
          */
          //判斷是否是INIT 進(jìn)程
         if (is_global_init(current))
              continue;


         //引起進(jìn)程掛起的信號(hào)   
         if (sig_kernel_stop(signr)) {
              
              //SIGSTOP的處理與其它會(huì)引起停止的信號(hào)有點(diǎn)不同
              //SIGSTOP總是停止進(jìn)程,而其它信號(hào)只會(huì)停止不在孤兒進(jìn)程組
              //中的進(jìn)程
              if (signr != SIGSTOP) {
                   spin_unlock_irq(&current->sighand->siglock);

                   /* signals can be posted during this window */

                   if (is_current_pgrp_orphaned())
                       goto relock;

                   spin_lock_irq(&current->sighand->siglock);
              }


              //停止進(jìn)程
              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);


         //除去內(nèi)核忽略和引起進(jìn)程停止的信號(hào)之處的所有信號(hào)都會(huì)讓過程
         //終止
         
         /*
          * Anything else is fatal, maybe with a core dump.
          */

         //置進(jìn)程標(biāo)志位PF_SIGNALED.表示該信號(hào)終止是由信號(hào)引起的
         current->flags |= PF_SIGNALED;
         if ((signr != SIGKILL) && print_fatal_signals)
              print_fatal_signal(regs, signr);

         //如果是一些會(huì)引起核心轉(zhuǎn)儲(chǔ)的信號(hào)
         //建立核心轉(zhuǎn)儲(chǔ)文件后退出
         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.
          */

         //進(jìn)程組退出
         do_group_exit(signr);
         /* NOTREACHED */
     }
     spin_unlock_irq(&current->sighand->siglock);
     return signr;
}
這個(gè)函數(shù)比較簡單,基本上就是遍歷信號(hào)等待隊(duì)列.然后處理信號(hào).一直遇到信號(hào)處理被重設(shè)或者沒有等待信號(hào)之后才會(huì)返回.
信號(hào)出列函數(shù)為dequeue_signal():
int dequeue_signal(struct task_struct *tsk, sigset_t *mask, siginfo_t *info)
{
     int signr = 0;

     /* We only dequeue private signals from ourselves, we don't let
      * signalfd steal them
      */

     //從pending 隊(duì)列中取出等待信號(hào)
     signr = __dequeue_signal(&tsk->pending, mask, info);

     //如果pending 隊(duì)列中沒有等待信號(hào),則從shared_pending中取
     if (!signr) {
         signr = __dequeue_signal(&tsk->signal->shared_pending,
                        mask, info);
         //如果是SIGALRM 信號(hào)
         //重啟計(jì)時(shí)器
         if (unlikely(signr == SIGALRM)) {
              struct hrtimer *tmr = &tsk->signal->real_timer;

              if (!hrtimer_is_queued(tmr) &&
                  tsk->signal->it_real_incr.tv64 != 0) {
                   hrtimer_forward(tmr, tmr->base->get_time(),
                            tsk->signal->it_real_incr);
                   hrtimer_restart(tmr);
              }
         }
     }

     //重新判斷是位還有末處理的信號(hào),更新TIF_SIGPENDING 標(biāo)志
     recalc_sigpending();

     //會(huì)引起進(jìn)程終止的信號(hào),置SIGNAL_STOP_DEQUEUED 標(biāo)志
     //禁止信號(hào)出列,即阻止后續(xù)的信號(hào)處理
     if (signr && unlikely(sig_kernel_stop(signr))) {
         if (!(tsk->signal->flags & SIGNAL_GROUP_EXIT))
              tsk->signal->flags |= SIGNAL_STOP_DEQUEUED;
     }

     //__SI_TIMER : 定時(shí)器到期
     if (signr &&
          ((info->si_code & __SI_MASK) == __SI_TIMER) &&
          info->si_sys_private){
         spin_unlock(&tsk->sighand->siglock);
         do_schedule_next_timer(info);
         spin_lock(&tsk->sighand->siglock);
     }
     return signr;
}
__dequeue_signal()用于從等待隊(duì)列中取出信號(hào).代碼如下:
static int __dequeue_signal(struct sigpending *pending, sigset_t *mask,
              siginfo_t *info)
{
     //取位圖中為第一個(gè)為1的標(biāo)志位
     int sig = next_signal(pending, mask);

     if (sig) {

         //如果定義了進(jìn)程通告?

         //task->notifier:指向一個(gè)函數(shù)指針. 設(shè)備驅(qū)動(dòng)程序用它來阻塞某些信號(hào)
         if (current->notifier) {
              if (sigismember(current->notifier_mask, sig)) {
                   if (!(current->notifier)(current->notifier_data)) {
                       clear_thread_flag(TIF_SIGPENDING);
                       return 0;
                   }
              }
         }

         //將信號(hào)從等待隊(duì)列中移除,更新等待信號(hào)標(biāo)志位
         if (!collect_signal(sig, pending, info))
              sig = 0;
     }

     return sig;
}

Cllect_signal()代碼如下:
static int collect_signal(int sig, struct sigpending *list, siginfo_t *info)
{
     struct sigqueue *q, *first = NULL;
     int still_pending = 0;


     //要處理的信號(hào)沒有包含在等待隊(duì)列中,退出
     if (unlikely(!sigismember(&list->signal, sig)))
         return 0;

     /*
      * Collect the siginfo appropriate to this signal.  Check if
      * there is another siginfo for the same signal.
     */

     //遍歷等待隊(duì)列.如果不止有一個(gè)sig 信號(hào)在等待.still_pending為1
list_for_each_entry(q, &list->list, list) {
         if (q->info.si_signo == sig) {
              if (first) {
                   still_pending = 1;
                   break;
              }
              first = q;
         }
     }
     if (first) {

         //如果等待隊(duì)列中有此信號(hào)

         //在等待隊(duì)列中將它刪除
         list_del_init(&first->list);
         //將信號(hào)信號(hào)copy 到info
         copy_siginfo(info, &first->info);

         //釋放信號(hào)
         __sigqueue_free(first);
         if (!still_pending)
              //如果只有一個(gè)信號(hào)在等待,也就是說該類的等待信號(hào)已經(jīng)處理完了
              //從等待位圖中刪除該位
              sigdelset(&list->signal, sig);
     } else {

         /* Ok, it wasn't in the queue.  This must be
            a fast-pathed signal or we must have been
            out of queue space.  So zero out the info.
          */
          //如果等待隊(duì)列中沒有此信號(hào),將對應(yīng)位圖置0.
          //info信號(hào)置空
         sigdelset(&list->signal, sig);
         info->si_signo = sig;
         info->si_errno = 0;
         info->si_code = 0;
         info->si_pid = 0;
         info->si_uid = 0;
     }
     return 1;
}
返回do_signal()中看看如果信號(hào)處理函數(shù)被重設(shè)會(huì)怎么樣處理.這也是信號(hào)處理中比較難理解的部份.轉(zhuǎn)入具體的處理代碼之前,先思考一下:
用戶空間的函數(shù)地址傳遞給內(nèi)核空間之后,可不可以在內(nèi)核直接運(yùn)行呢?(即設(shè)置好內(nèi)核堆,再把eip設(shè)為fuction address)?
是有可能運(yùn)行的.因?yàn)閮?nèi)核切占不會(huì)切換CR3.用戶進(jìn)程切換會(huì)切換CR3.因此可以保證進(jìn)程陷入內(nèi)核后可以正常的對用戶空間的地址進(jìn)行尋址.但是基于以下幾點(diǎn)原因.不建議直接在內(nèi)核空間運(yùn)行
1:安全因素.陷入內(nèi)核空間后,對內(nèi)核地址空間具有全部訪問權(quán)限,沒有內(nèi)存保護(hù)進(jìn)制
2:內(nèi)核堆棧過小,最大只有8KB.
3:用戶空間的函數(shù)在運(yùn)行的時(shí)候可能會(huì)發(fā)出系統(tǒng)調(diào)用.由于在最高特權(quán)級下,導(dǎo)致系統(tǒng)調(diào)用/異常處理失敗.

既然這樣,那怎么運(yùn)行信號(hào)處理函數(shù)呢?
我們只需要讓它在返回用戶空間后馬上運(yùn)行信號(hào)處理函數(shù),運(yùn)行信號(hào)處理函數(shù)再系統(tǒng)調(diào)用返回內(nèi)核就可以了.
先分析一下有關(guān)的數(shù)據(jù)結(jié)構(gòu):
struct sigframe
{
     //信號(hào)處理函數(shù)的返回地址,它指向同一個(gè)結(jié)構(gòu)中的retcode字段
     char __user *pretcode;
     //信號(hào)數(shù)值
     int sig;
     //保存當(dāng)前regs的一個(gè)結(jié)構(gòu)
     struct sigcontext sc;
     //保存FPU,MMX,XMM等相關(guān)信息
     struct _fpstate fpstate;
     //被阻塞的實(shí)時(shí)信號(hào)的位數(shù)組
     unsigned long extramask[_NSIG_WORDS-1];
     //信號(hào)處理程序運(yùn)行完后執(zhí)行的執(zhí)令
     char retcode[8];
}
現(xiàn)在我們轉(zhuǎn)入代碼看是如何處理的:
static int
handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
           sigset_t *oldset, struct pt_regs * regs)
{
     int ret;


     //在執(zhí)行系統(tǒng)調(diào)用的時(shí)候,可能被信號(hào)給中斷了.
     //要根據(jù)返回值判斷是否可以重啟系統(tǒng)調(diào)用
     
     /* 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.
      */

     //如果處于跟蹤狀態(tài)
     //就像進(jìn)行中斷處理程序,關(guān)閉中斷一樣
     if (unlikely(regs->eflags & TF_MASK)
         && likely(current->ptrace & PT_DTRACE)) {
         current->ptrace &= ~PT_DTRACE;
         regs->eflags &= ~TF_MASK;
     }

     /* Set up the stack frame */
     
     //SA_SIGINFO:為信號(hào)處理提供額外的信息

     //建立幀結(jié)構(gòu)
     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);

         //SA_NODEFER:在執(zhí)行信號(hào)處理函數(shù)的時(shí)候,不屏弊信號(hào)
         if (!(ka->sa.sa_flags & SA_NODEFER))
              //如果沒有定義SA_NODEFER.那屏弊掉當(dāng)前信號(hào)
              sigaddset(&current->blocked,sig);

         //更新TIF_SIGPENDING 標(biāo)志位
         recalc_sigpending();
         spin_unlock_irq(&current->sighand->siglock);
     }

     return ret;
}
首先,也要恢復(fù)被中斷的系統(tǒng)調(diào)用.然后,再調(diào)用setup_frame()或者是setup_rt_frame().setup_rt_frame()是有跟實(shí)時(shí)信號(hào)有關(guān)的.在這里以setup_frame()為例進(jìn)行分析.代碼如下:
static int setup_frame(int sig, struct k_sigaction *ka,
                sigset_t *set, struct pt_regs * regs)
{
     void __user *restorer;
     struct sigframe __user *frame;
     int err = 0;
     int usig;


     //取得在用戶空間棧中存放frame的位置
     frame = get_sigframe(ka, regs, sizeof(*frame));

     //檢查是否是可寫的
     if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
         goto give_sigsegv;

     //在有的執(zhí)行域里.信號(hào)的數(shù)值可能不一樣,因此,需要轉(zhuǎn)換一下
     //signal_invmap:信號(hào)轉(zhuǎn)換表
     usig = current_thread_info()->exec_domain
         && current_thread_info()->exec_domain->signal_invmap
         && sig
         ? current_thread_info()->exec_domain->signal_invmap[sig]
         : sig;

     err = __put_user(usig, &frame->sig);
     if (err)
         goto give_sigsegv;

     //保存當(dāng)前內(nèi)核棧里保存的用戶空間的硬件環(huán)境
     err = setup_sigcontext(&frame->sc, &frame->fpstate, regs, set->sig[0]);
     if (err)
         goto give_sigsegv;


     //set:這里是表示進(jìn)程以前的信號(hào)掩碼

     //在extramask里保存以前的信號(hào)掩碼
     if (_NSIG_WORDS > 1) {
         //將set->sig的高32位存于extramask中
         err = __copy_to_user(&frame->extramask, &set->sig[1],
                         sizeof(frame->extramask));
         if (err)
              goto give_sigsegv;
     }

     if (current->binfmt->hasvdso)
         restorer = (void *)VDSO_SYM(&__kernel_sigreturn);
     else
         restorer = (void *)&frame->retcode;
     if (ka->sa.sa_flags & SA_RESTORER)
         restorer = ka->sa.sa_restorer;

     /* Set up to return from userspace.  */

     //使frame->pretcode指向 frame->retcode
     err |= __put_user(restorer, &frame->pretcode);
      
     /*
      * This is popl %eax ; movl $,%eax ; int $0x80
      *
      * WE DO NOT USE IT ANY MORE! It's only left here for historical
      * reasons and because gdb uses it as a signature to notice
      * signal handler stack frames.
      */

     //frame->retcode:執(zhí)行完信號(hào)處理函數(shù)后的下一條指令

     //這里構(gòu)建了一次系統(tǒng)調(diào)用.調(diào)用號(hào)是__NR_sigreturn
     
     err |= __put_user(0xb858, (short __user *)(frame->retcode+0));
     err |= __put_user(__NR_sigreturn, (int __user *)(frame->retcode+2));
     err |= __put_user(0x80cd, (short __user *)(frame->retcode+6));

     if (err)
         goto give_sigsegv;

     /* Set up registers for signal handler */

     //因?yàn)閞egs結(jié)構(gòu)已經(jīng)保存在frame之中了.這里可以隨意的修改

     //修改用戶空間的棧指針位置,指向frame
     regs->esp = (unsigned long) frame;

     //返回到用戶空間的下一條指令
     //即返回到用戶空間后,執(zhí)行信號(hào)處理程序
     regs->eip = (unsigned long) ka->sa.sa_handler;
     regs->eax = (unsigned long) sig;
     regs->edx = (unsigned long) 0;
     regs->ecx = (unsigned long) 0;


     //用戶空間的段寄存器都是__USER_DS
     //這里是為了防止有意外的修改
     regs->xds = __USER_DS;
     regs->xes = __USER_DS;
     regs->xss = __USER_DS;
     regs->xcs = __USER_CS;

     /*
      * Clear TF when entering the signal handler, but
      * notify any tracer that was single-stepping it.
      * The tracer may want to single-step inside the
      * handler too.
      */

     //清除跟蹤標(biāo)志
     //就像是處理中斷處理程序,清除中斷標(biāo)志位一樣
     regs->eflags &= ~TF_MASK;

     if (test_thread_flag(TIF_SINGLESTEP))
         ptrace_notify(SIGTRAP);

#if DEBUG_SIG
     printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n",
         current->comm, current->pid, frame, regs->eip, frame->pretcode);
#endif

     return 0;

give_sigsegv:
     force_sigsegv(sig, current);
     return -EFAULT;
}
get_sigframe()代碼如下:
static inline void __user *
get_sigframe(struct k_sigaction *ka, struct pt_regs * regs, size_t frame_size)
{
     unsigned long esp;

     /* Default to using normal stack */
     esp = regs->esp;

     /* This is the X/Open sanctioned signal stack switching.  */
     //用戶指定了棧位置

     //sas__ss_flags:判斷指定的棧位置是否為于當(dāng)前棧的下部有效空間
     //進(jìn)程的地址空間中,棧空間占據(jù)著最上部
     if (ka->sa.sa_flags & SA_ONSTACK) {
         if (sas_ss_flags(esp) == 0)
              //獲得棧頂位置
              esp = current->sas_ss_sp + current->sas_ss_size;
     }

     /* This is the legacy signal stack switching. */
     //從Unix中遺留的調(diào)用.為了保持兼容性而設(shè)置
     //不提 倡使用
     else if ((regs->xss & 0xffff) != __USER_DS &&
          !(ka->sa.sa_flags & SA_RESTORER) &&
          ka->sa.sa_restorer) {
         esp = (unsigned long) ka->sa.sa_restorer;
     }

     //為frame結(jié)構(gòu)空出位置
     esp -= frame_size;
     /* Align the stack pointer according to the i386 ABI,
      * i.e. so that on function entry ((sp + 4) & 15) == 0. */

     //按照i386 ABI規(guī)范.對齊棧指針
     esp = ((esp + 4) & -16ul) - 4;
     return (void __user *) esp;
}

setup_sigcontext()代碼如下:
static int
setup_sigcontext(struct sigcontext __user *sc, struct _fpstate __user *fpstate,
          struct pt_regs *regs, unsigned long mask)
{
     int tmp, err = 0;


     //保存regs
     err |= __put_user(regs->xfs, (unsigned int __user *)&sc->fs);
     savesegment(gs, tmp);
     err |= __put_user(tmp, (unsigned int __user *)&sc->gs);

     err |= __put_user(regs->xes, (unsigned int __user *)&sc->es);
     err |= __put_user(regs->xds, (unsigned int __user *)&sc->ds);
     err |= __put_user(regs->edi, &sc->edi);
     err |= __put_user(regs->esi, &sc->esi);
     err |= __put_user(regs->ebp, &sc->ebp);
     err |= __put_user(regs->esp, &sc->esp);
     err |= __put_user(regs->ebx, &sc->ebx);
     err |= __put_user(regs->edx, &sc->edx);
     err |= __put_user(regs->ecx, &sc->ecx);
     err |= __put_user(regs->eax, &sc->eax);
     err |= __put_user(current->thread.trap_no, &sc->trapno);
     err |= __put_user(current->thread.error_code, &sc->err);
     err |= __put_user(regs->eip, &sc->eip);
     err |= __put_user(regs->xcs, (unsigned int __user *)&sc->cs);
     err |= __put_user(regs->eflags, &sc->eflags);
     err |= __put_user(regs->esp, &sc->esp_at_signal);
     err |= __put_user(regs->xss, (unsigned int __user *)&sc->ss);

     //保存FPU,XMM.MXX等信息
     tmp = save_i387(fpstate);
     if (tmp
       err = 1;
     else
       err |= __put_user(tmp ? fpstate : NULL, &sc->fpstate);

     /* non-iBCS2 extensions.. */

     //mask:即為set->sig 的低32位
     err |= __put_user(mask, &sc->oldmask);
     err |= __put_user(current->thread.cr2, &sc->cr2);

     return err;
}
用下圖表示上述的操作:



注意到代碼中有以下兩條指令:
regs->esp = (unsigned long) frame;
regs->eip = (unsigned long) ka->sa.sa_handler;
第一條把用戶的棧指令指向了frame
第二條把返回用戶空間的eip設(shè)為了信號(hào)的處理函數(shù).
這樣返回到用戶空間后就會(huì)執(zhí)行ka->sa.sa_handler這個(gè)函數(shù).注意到上面的堆棧結(jié)構(gòu),其實(shí)它模擬了一次函數(shù)調(diào)用.函數(shù)調(diào)用時(shí),先把參數(shù)壓棧,再把返回地址壓棧.在上面的棧中,函數(shù)的參數(shù)為sig.返回地址為pretcode.這樣,在信號(hào)處理函數(shù)返回之后.就會(huì)把pretcode裝入eip.而pretcode又是指向retcode.也就是說函數(shù)返回之后,會(huì)運(yùn)行retcode對應(yīng)的指令.
Retcode在上面的代碼中是這樣被設(shè)置的:
     err |= __put_user(0xb858, (short __user *)(frame->retcode+0));
     err |= __put_user(__NR_sigreturn, (int __user *)(frame->retcode+2));
     err |= __put_user(0x80cd, (short __user *)(frame->retcode+6));
代碼中的0xb858 0x80cd可能對應(yīng)的就是指令的機(jī)器碼.它相應(yīng)于如下指令:
popl %eax ;
movl $,%eax ;
int $0x80
即會(huì)產(chǎn)生一個(gè)系統(tǒng)調(diào)用號(hào)為__NR_sigreturn的系統(tǒng)調(diào)用.它對應(yīng)的入口是:
asmlinkage int sys_sigreturn(unsigned long __unused)
{
     //第一個(gè)參數(shù)地址就是棧指針位置
     struct pt_regs *regs = (struct pt_regs *) &__unused;
     //esp-8是因?yàn)樵谟脩艨臻g運(yùn)行的時(shí)候,棧出了兩個(gè)單元
     //即上圖中的pretcode出棧.sig出棧
     struct sigframe __user *frame = (struct sigframe __user *)(regs->esp - 8);
     sigset_t set;
     int eax;

     //檢查對應(yīng)區(qū)域是否可讀
     if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
         goto badframe;

     //從frame->sc.oldmask 恢復(fù)set.sig的低32 位
     if (__get_user(set.sig[0], &frame->sc.oldmask)
         || (_NSIG_WORDS > 1
         //從frame->extramask 中恢復(fù)set.sig的高32位
         && __copy_from_user(&set.sig[1], &frame->extramask,
                       sizeof(frame->extramask))))
         goto badframe;

     sigdelsetmask(&set, ~_BLOCKABLE);
     spin_lock_irq(&current->sighand->siglock);
     current->blocked = set;

     //重新判斷是否還有末處理的信號(hào)
     recalc_sigpending();
     spin_unlock_irq(&current->sighand->siglock);


     //從frame->sc中恢復(fù)系統(tǒng)調(diào)用前的硬件環(huán)境
     if (restore_sigcontext(regs, &frame->sc, &eax))
         goto badframe;
     return eax;

badframe:
     if (show_unhandled_signals && printk_ratelimit())
         printk("%s%s[%d] bad frame in sigreturn frame:%p eip:%lx"
                " esp:%lx oeax:%lx\n",
             task_pid_nr(current) > 1 ? KERN_INFO : KERN_EMERG,
             current->comm, task_pid_nr(current), frame, regs->eip,
             regs->esp, regs->orig_eax);

     force_sig(SIGSEGV, current);
     return 0;
}
至此,內(nèi)核棧又回復(fù)到以前的樣子了.
五:小結(jié)
本節(jié)中,在Linux內(nèi)核中跟蹤了信號(hào)處理函數(shù)的設(shè)置,信號(hào)的發(fā)送.信號(hào)的處理.涉及到的代碼都不是很難理解.在理解了用戶自定義的信號(hào)函數(shù)的運(yùn)行機(jī)制之后,我們也很容易調(diào)用用戶空間的一個(gè)特定操作.另外,雖然內(nèi)核涉及到的信號(hào)處理比較簡單,但要在用戶空間使用好信號(hào)就要看一個(gè)人的程序設(shè)計(jì)功底了.

本文來自ChinaUnix博客,如果查看原文請點(diǎn):http://blog.chinaunix.net/u/26185/showart_1405384.html
您需要登錄后才可以回帖 登錄 | 注冊

本版積分規(guī)則 發(fā)表回復(fù)

  

北京盛拓優(yōu)訊信息技術(shù)有限公司. 版權(quán)所有 京ICP備16024965號(hào)-6 北京市公安局海淀分局網(wǎng)監(jiān)中心備案編號(hào):11010802020122 niuxiaotong@pcpop.com 17352615567
未成年舉報(bào)專區(qū)
中國互聯(lián)網(wǎng)協(xié)會(huì)會(huì)員  聯(lián)系我們:huangweiwei@itpub.net
感謝所有關(guān)心和支持過ChinaUnix的朋友們 轉(zhuǎn)載本站內(nèi)容請注明原作者名及出處

清除 Cookies - ChinaUnix - Archiver - WAP - TOP