技術(shù)員聯(lián)盟提供win764位系統(tǒng)下載,win10,win7,xp,裝機(jī)純凈版,64位旗艦版,綠色軟件,免費(fèi)軟件下載基地!

當(dāng)前位置:主頁(yè) > 教程 > 服務(wù)器類 >

Linux內(nèi)核中的信號(hào)機(jī)制的介紹

來(lái)源:技術(shù)員聯(lián)盟┆發(fā)布時(shí)間:2019-02-16 18:21┆點(diǎn)擊:

  應(yīng)用程序發(fā)送信號(hào)時(shí),主要通過(guò)kill進(jìn)行。注意:不要被“kill”迷惑,它并不是發(fā)送SIGKILL信號(hào)專用函數(shù)。這個(gè)函數(shù)主要通過(guò)系統(tǒng)調(diào)用sys_kill()進(jìn)入內(nèi)核,它接收兩個(gè)參數(shù):

  第一個(gè)參數(shù)為目標(biāo)進(jìn)程id,kill()可以向進(jìn)程(或進(jìn)程組),線程(輕權(quán)線程)發(fā)送信號(hào),因此pid有以下幾種情況:

  ● pid>0:目標(biāo)進(jìn)程(可能是輕權(quán)進(jìn)程)由pid指定。

  ● pid=0:信號(hào)被發(fā)送到當(dāng)前進(jìn)程組中的每一個(gè)進(jìn)程。

  ● pid=-1:信號(hào)被發(fā)送到任何一個(gè)進(jìn)程,init進(jìn)程(PID=1)和以及當(dāng)前進(jìn)程無(wú)法發(fā)送信號(hào)的進(jìn)程除外。

  ● pid<-1:信號(hào)被發(fā)送到目標(biāo)進(jìn)程組,其id由參數(shù)中的pid的絕對(duì)值指定。

  第二個(gè)參數(shù)為需要發(fā)送的信號(hào)。

  由于sys_kill處理的情況比較多,分析起來(lái)比較復(fù)雜,我們從太累了入手,這個(gè)函數(shù)把信號(hào)發(fā)送到由參數(shù)指定pid指定的線程(輕權(quán)進(jìn)程)中。tkill的內(nèi)核入口是sys_tkill(kernel/signal.c),其定義如下:

  /*

  * Send a signal to only one task, even if it's a CLONE_THREAD task.

  */

  asmlinkage long

  sys_tkill(int pid, int sig)

  {

  struct siginfo info;

  int error;

  struct task_struct *p;

  /* This is only valid for single tasks */

  if (pid <= 0)//對(duì)參數(shù)pid進(jìn)行檢查

  return -EINVAL;

  info.si_signo = sig; //根據(jù)參數(shù)初始化一個(gè)siginfo結(jié)構(gòu)

  info.si_errno = 0;

  info.si_code = SI_TKILL;

  info.si_pid = current->tgid;

  info.si_uid = current->uid;

  read_lock(&tasklist_lock);

  p = find_task_by_pid(pid);//獲取由pid指定的線程的task_struct結(jié)構(gòu)

  error = -ESRCH;

  if (p) {

  error = check_kill_permission(sig, &info, p);//權(quán)限檢查

  /*

  * The null signal is a permissions and process existence

  * probe. No signal is actually delivered.

  */

  if (!error && sig && p->sighand) {

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

  handle_stop_signal(sig, p);

  //對(duì)某些特殊信號(hào)進(jìn)程處理,例如當(dāng)收到SIGSTOP時(shí),需要把信號(hào)隊(duì)列中的SIGCONT全部刪除

  error = specific_send_sig_info(sig, &info, p);//把信號(hào)加入到信號(hào)隊(duì)列

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

  }

  }

  read_unlock(&tasklist_lock);

  return error;

  }

  sys_tkill函數(shù)主要是通過(guò)pecific_send_sig_info()函數(shù)實(shí)現(xiàn)的,下面我們看一下pecific_send_sig_info()(kernel/signal.c)的定義:

  static int

  specific_send_sig_info(int sig, struct siginfo *info, struct task_struct *t)

  {

  int ret = 0;

  if (!irqs_disabled())

  BUG();

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

  if (((unsigned long)info > 2) && (info->si_code == SI_TIMER))

  /*

  * Set up a return to indicate that we dropped the signal.

  */

  ret = info->si_sys_private;

  /*信號(hào)被忽略*/

  /* Short-circuit ignored signals. */

  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. */

  if (LEGACY_QUEUE(&t->pending, sig))

  goto out;

  ret = send_signal(sig, info, t, &t->pending);//實(shí)際的發(fā)送工作

  if (!ret && !sigismember(&t->blocked, sig))

  signal_wake_up(t, sig == SIGKILL);

  out:

  return ret;

  }

  首先調(diào)用sig_ignored檢查信號(hào)是否被忽略,然后檢查發(fā)送的信號(hào)是不是普通信號(hào),如果是普通信號(hào),就需要根據(jù)信號(hào)位圖來(lái)檢查當(dāng)前信號(hào)隊(duì)列中是否已經(jīng)存在該信號(hào),如果已經(jīng)存在,對(duì)于普通信號(hào)不需要做任何處理。然后調(diào)用send_signal來(lái)完成實(shí)際的發(fā)送工作,send_signal()是信號(hào)發(fā)送的重點(diǎn),除sys_tkill之外的函數(shù),最終都是通過(guò)send_signal()來(lái)完成信號(hào)的發(fā)送工作的。

  這里注意到想send_signal()傳遞的參數(shù)時(shí)t->pending,也就是連接Private Signal Queue的那條鏈。最后,如果發(fā)送成功就調(diào)用signal_wake_up()來(lái)喚醒目標(biāo)進(jìn)程,這樣可以保證該進(jìn)程進(jìn)入就緒狀態(tài),從而有機(jī)會(huì)被調(diào)度執(zhí)行信號(hào)處理函數(shù)。

  現(xiàn)在我們來(lái)看看send_signal()(kernel/signal.c)函數(shù),這個(gè)函數(shù)的主要工作就是分配并初始化一個(gè)sigqueue結(jié)構(gòu),然后把它添加到信號(hào)隊(duì)列中。

  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.

  */

  if ((unsigned long)info == 2)

  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. */

  q = __sigqueue_alloc(t, GFP_ATOMIC, (sig < SIGRTMIN &&

  ((unsigned long) info < 2 ||

  info->si_code >= 0)));//分配sigqueue結(jié)構(gòu)

  if (q) {//如果成功分配到sigqueue結(jié)構(gòu),就把它添加到隊(duì)列中,并對(duì)其初始化

  list_add_tail(&q->list, &signals->list);

  switch ((unsigned long) info) {

  case 0:

  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 1:

  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);//拷貝sigqueue結(jié)構(gòu)

  break;

  }

  } else {

  if (sig >= SIGRTMIN && info && (unsigned long)info != 1

  && 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;

  if (((unsigned long)info > 1) && (info->si_code == SI_TIMER))

  /*

  * Set up a return to indicate that we dropped

  * the signal.

  */

  ret = info->si_sys_private;

  }

  out_set:

  sigaddset(&signals->signal, sig);//設(shè)置信號(hào)位圖

  return ret;

  }

  從上面的分析可以看出,我們看到信號(hào)被添加到信號(hào)隊(duì)列之后,會(huì)調(diào)用signal_wake_up()喚醒這個(gè)進(jìn)程,signal_wake_up()(kernel/signal.c)的定義如下:

  /*

  * Tell a process that it has a new active signal..

  *

  * NOTE! we rely on the previous spin_lock to

  * lock interrupts for us! We can only be called with

  * "siglock" held, and the local interrupt must

  * have been disabled when that got acquired!

  *

  * No need to set need_resched since signal event passing

  * goes through ->blocked

  */

  void signal_wake_up(struct task_struct *t, int resume)

  {

  unsigned int mask;

  set_tsk_thread_flag(t, TIF_SIGPENDING);//為進(jìn)程設(shè)置TIF_SIGPENDING標(biāo)志

  /*

  * For SIGKILL, we want to wake it up in the stopped/traced case.

  * We don't check t->state here because there is a race with it

  * executing another processor and just now entering stopped state.

  * By using wake_up_state, we ensure the process will wake up and

  * handle its death signal.

  */

  mask = TASK_INTERRUPTIBLE;

  if (resume)

  mask |= TASK_STOPPED | TASK_TRACED;

  if (!wake_up_state(t, mask))

  kick_process(t);

  }