微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > linux内核中的信号机制--信号发送

linux内核中的信号机制--信号发送

时间:11-22 来源:互联网 点击:
Kernel version:2.6.14

CPU architecture:ARM920T

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

应用程序发送信号时,主要通过kill进行。注意:不要被“kill”迷惑,它并不是发送SIGKILL信号专用函数。这个函数主要通过系统调用sys_kill()进入内核,它接收两个参数:

第一个参数为目标进程id,kill()可以向进程(或进程组),线程(轻权线程)发送信号,因此pid有以下几种情况:

  • pid>0:目标进程(可能是轻权进程)由pid指定。
  • pid=0:信号被发送到当前进程组中的每一个进程。
  • pid=-1:信号被发送到任何一个进程,init进程(PID=1)和以及当前进程无法发送信号的进程除外。
  • pid<-1:信号被发送到目标进程组,其id由参数中的pid的绝对值指定。
第二个参数为需要发送的信号。

由于sys_kill处理的情况比较多,分析起来比较复杂,我们从tkill()函数入手,这个函数把信号发送到由参数指定pid指定的线程(轻权进程)中。tkill的内核入口是sys_tkill(kernel/signal.c),其定义如下:

[plain]view plaincopyprint?

  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 plaincopyprint?

  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);//实际的发送工作
  23. if(!ret&&!sigismember(&t->blocked,sig))
  24. signal_wake_up(t,sig==SIGKILL);
  25. out:
  26. returnret;
  27. }

首先调用sig_ignored检查信号是否被忽略,然后检查发送的信号是不是普通信号,如果是普通信号,就需要根据信号位图来检查当前信号队列中是否已经存在该信号,如果已经存在,对于普通信号不需要做任何处理。然后调用send_signal来完成实际的发送工作,send_signal()是信号发送的重点,除sys_tkill之外的函数,最终都是通过send_signal()来完成信号的发送工作的。

这里注意到想send_signal()传递的参数时t->pending,也就是连接Private Signal Queue的那条链。最后,如果发送成功就调用signal_wake_up()来唤醒目标进程,这样可以保证该进程进入就绪状态,从而有机会被调度执行信号处理函数。

现在我们来看看send_signal()(kernel/signal.c)函数,这个函数的主要工作就是分配并初始化一个sigqueue结构,然后把它添加到信号队列中。

[plain]view plaincopyprint?

  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. allowedtofailwithEA

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top