新聞中心

EEPW首頁 > 嵌入式系統 > 設計應用 > linux內核中的信號機制--信號發(fā)送

linux內核中的信號機制--信號發(fā)送

作者: 時間:2016-11-22 來源:網絡 收藏
Kernel version:2.6.14

CPU architecture:ARM920T

本文引用地址:http://2s4d.com/article/201611/320005.htm

Author:ce123(http://blog.csdn.net/ce123)

應用程序發(fā)送信號時,主要通過kill進行。注意:不要被“kill”迷惑,它并不是發(fā)送SIGKILL信號專用函數。這個函數主要通過系統調用sys_kill()進入內核,它接收兩個參數:

第一個參數為目標進程id,kill()可以向進程(或進程組),線程(輕權線程)發(fā)送信號,因此pid有以下幾種情況:

  • pid>0:目標進程(可能是輕權進程)由pid指定。
  • pid=0:信號被發(fā)送到當前進程組中的每一個進程。
  • pid=-1:信號被發(fā)送到任何一個進程,init進程(PID=1)和以及當前進程無法發(fā)送信號的進程除外。
  • pid<-1:信號被發(fā)送到目標進程組,其id由參數中的pid的絕對值指定。
第二個參數為需要發(fā)送的信號。

由于sys_kill處理的情況比較多,分析起來比較復雜,我們從tkill()函數入手,這個函數把信號發(fā)送到由參數指定pid指定的線程(輕權進程)中。tkill的內核入口是sys_tkill(kernel/signal.c),其定義如下:

[plain]view plaincopy
print?
  1. /*
  2. *Sendasignaltoonlyonetask,evenifitsaCLONE_THREADtask.
  3. */
  4. asmlinkagelong
  5. sys_tkill(intpid,intsig)
  6. {
  7. structsiginfoinfo;
  8. interror;
  9. structtask_struct*p;
  10. /*Thisisonlyvalidforsingletasks*/
  11. if(pid<=0)//對參數pid進行檢查
  12. return-EINVAL;
  13. info.si_signo=sig;//根據參數初始化一個siginfo結構
  14. info.si_errno=0;
  15. info.si_code=SI_TKILL;
  16. info.si_pid=current->tgid;
  17. info.si_uid=current->uid;
  18. read_lock(&tasklist_lock);
  19. p=find_task_by_pid(pid);//獲取由pid指定的線程的task_struct結構
  20. error=-ESRCH;
  21. if(p){
  22. error=check_kill_permission(sig,&info,p);//權限檢查
  23. /*
  24. *Thenullsignalisapermissionsandprocessexistence
  25. *probe.Nosignalisactuallydelivered.
  26. */
  27. if(!error&&sig&&p->sighand){
  28. spin_lock_irq(&p->sighand->siglock);
  29. handle_stop_signal(sig,p);
  30. //對某些特殊信號進程處理,例如當收到SIGSTOP時,需要把信號隊列中的SIGCONT全部刪除
  31. error=specific_send_sig_info(sig,&info,p);//把信號加入到信號隊列
  32. spin_unlock_irq(&p->sighand->siglock);
  33. }
  34. }
  35. read_unlock(&tasklist_lock);
  36. returnerror;
  37. }

sys_tkill函數主要是通過pecific_send_sig_info()函數實現的,下面我們看一下pecific_send_sig_info()(kernel/signal.c)的定義:

[plain]view plaincopy
print?
  1. staticint
  2. specific_send_sig_info(intsig,structsiginfo*info,structtask_struct*t)
  3. {
  4. intret=0;
  5. if(!irqs_disabled())
  6. BUG();
  7. assert_spin_locked(&t->sighand->siglock);
  8. if(((unsignedlong)info>2)&&(info->si_code==SI_TIMER))
  9. /*
  10. *Setupareturntoindicatethatwedroppedthesignal.
  11. */
  12. ret=info->si_sys_private;
  13. /*信號被忽略*/
  14. /*Short-circuitignoredsignals.*/
  15. if(sig_ignored(t,sig))
  16. gotoout;
  17. /*Supportqueueingexactlyonenon-rtsignal,sothatwe
  18. cangetmoredetailedinformationaboutthecauseof
  19. thesignal.*/
  20. if(LEGACY_QUEUE(&t->pending,sig))
  21. gotoout;
  22. ret=send_signal(sig,info,t,&t->pending);//實際的發(fā)送工作
  23. if(!ret&&!sigismember(&t->blocked,sig))
  24. signal_wake_up(t,sig==SIGKILL);
  25. out:
  26. returnret;
  27. }
首先調用sig_ignored檢查信號是否被忽略,然后檢查發(fā)送的信號是不是普通信號,如果是普通信號,就需要根據信號位圖來檢查當前信號隊列中是否已經存在該信號,如果已經存在,對于普通信號不需要做任何處理。然后調用send_signal來完成實際的發(fā)送工作,send_signal()是信號發(fā)送的重點,除sys_tkill之外的函數,最終都是通過send_signal()來完成信號的發(fā)送工作的。

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

現在我們來看看send_signal()(kernel/signal.c)函數,這個函數的主要工作就是分配并初始化一個sigqueue結構,然后把它添加到信號隊列中。

[plain]view plaincopy
print?
  1. staticintsend_signal(intsig,structsiginfo*info,structtask_struct*t,
  2. structsigpending*signals)
  3. {
  4. structsigqueue*q=NULL;
  5. intret=0;
  6. /*
  7. *fast-pathedsignalsforkernel-internalthingslikeSIGSTOP
  8. *orSIGKILL.
  9. */
  10. if((unsignedlong)info==2)
  11. gotoout_set;
  12. /*Real-timesignalsmustbequeuedifsentbysigqueue,or
  13. someotherreal-timemechanism.Itisimplementation
  14. definedwhetherkill()doesso.Weattempttodoso,on
  15. theprincipleofleastsurprise,butsincekillisnot
  16. allowedtofailwithEAGAINwhenlowonmemorywejust
  17. makesureatleastonesignalgetsdeliveredanddont
  18. passontheinfostruct.*/
  19. q=__sigqueue_alloc(t,GFP_ATOMIC,(sig
  20. ((unsignedlong)info<2||
  21. info->si_code>=0)));//分配sigqueue結構
  22. if(q){//如果成功分配到sigqueue結構,就把它添加到隊列中,并對其初始化
  23. list_add_tail(&q->list,&signals->list);
  24. switch((unsignedlong)info){
  25. case0:
  26. q->info.si_signo=sig;
  27. q->info.si_errno=0;
  28. q->info.si_code=SI_USER;
  29. q->info.si_pid=current->pid;
  30. q->info.si_uid=current->uid;
  31. break;
  32. case1:
  33. q->info.si_signo=sig;
  34. q->info.si_errno=0;
  35. q->info.si_code=SI_KERNEL;
  36. q->info.si_pid=0;
  37. q->info.si_uid=0;
  38. break;
  39. default:
  40. copy_siginfo(&q->info,info);//拷貝sigqueue結構
  41. break;
  42. }
  43. }else{
  44. if(sig>=SIGRTMIN&&info&&(unsignedlong)info!=1
  45. &&info->si_code!=SI_USER)
  46. /*
  47. *Queueoverflow,abort.Wemayabortifthesignalwasrt
  48. *andsentbyuserusingsomethingotherthankill().
  49. */
  50. return-EAGAIN;
  51. if(((unsignedlong)info>1)&&(info->si_code==SI_TIMER))
  52. /*
  53. *Setupareturntoindicatethatwedropped
  54. *thesignal.
  55. */
  56. ret=info->si_sys_private;
  57. }
  58. out_set:
  59. sigaddset(&signals->signal,sig);//設置信號位圖
  60. returnret;
  61. }
從上面的分析可以看出,我們看到信號被添加到信號隊列之后,會調用signal_wake_up()喚醒這個進程,signal_wake_up()(kernel/signal.c)的定義如下:

[plain]view plaincopy
print?
  1. /*
  2. *Tellaprocessthatithasanewactivesignal..
  3. *
  4. *NOTE!werelyonthepreviousspin_lockto
  5. *lockinterruptsforus!Wecanonlybecalledwith
  6. *"siglock"held,andthelocalinterruptmust
  7. *havebeendisabledwhenthatgotacquired!
  8. *
  9. *Noneedtosetneed_reschedsincesignaleventpassing
  10. *goesthrough->blocked
  11. */
  12. voidsignal_wake_up(structtask_struct*t,intresume)
  13. {
  14. unsignedintmask;
  15. set_tsk_thread_flag(t,TIF_SIGPENDING);//為進程設置TIF_SIGPENDING標志
  16. /*
  17. *ForSIGKILL,wewanttowakeitupinthestopped/tracedcase.
  18. *Wedontcheckt->stateherebecausethereisaracewithit
  19. *executinganotherprocessorandjustnowenteringstoppedstate.
  20. *Byusingwake_up_state,weensuretheprocesswillwakeupand
  21. *handleitsdeathsignal.
  22. */
  23. mask=TASK_INTERRUPTIBLE;
  24. if(resume)
  25. mask|=TASK_STOPPED|TASK_TRACED;
  26. if(!wake_up_state(t,mask))
  27. kick_process(t);
  28. }
signal_wake_up()首先為進程設置TIF_SIGPENDING標志,說明該進程有延遲的信號要等待處理。然后再調用wake_up_state()喚醒目標進程,如果目標進程在其他的CPU上運行,wake_up_state()將返回0,此時調用kick_process()向該CPU發(fā)送一個處理器間中斷。當中斷返回前戲,會為當前進程處理延遲的信號。

此后當該進程被調度時,在進程返回用戶空間前,會調用do_notify_resume()處理該進程的信號。



評論


技術專區(qū)

關閉