联系我:新浪微博@阳光下的云朵2012或者zhangcaiyun_86#163.com(将#换成@)

读书笔记7----linux中signal的处理机制

上一篇 / 下一篇  2013-05-31 15:32:00 / 个人分类:linux

一、信号的基本概念
1.基本概念
    软中断信号(signal,又简称为信号)用来通知进程发生了异步事件。进程之间可以互相通过系统调用kill发送软中断信号。内核也可以因为内部事件而给进程发送信号,通知进程发生了某个事件。注意,信号只是用来通知某进程法身了什么事件,并不给该进程传递任何数据。
  收到信号的进程对各种信号有不同的处理方法。处理方法可以分为三类:第一种是类似中断的处理程序,对于需要处理的信号,进程可以指定处理函数,由该函数来处理。第二种方法是,是忽略某个信号,对该信号不做任何处理,就像未发生过一样。第三种方法是,对该信号的处理保留系统的默认值,这种缺省操作,对大部分信号的缺省操作是是使得进程终止。近侧很难过通过系统调用signal来指定进程对某个信号的处理行为。
  在进程表的表项中有一个软中断信号域,该域中每一位对应一个信号,当信号发送给进程时,对应位置位。由此可以看出,进程对不同的信号可以同时保留。但对于同一个信号,进程并不知道在处理之前来过多少。
2.信号的类型
发出信号的原因很多,这里按发出信号的原因简单分类,以了解各种信号:
(1)与进程终止相关的信号。当进程退出,或者子进程终止时,发出这类信号。
(2)与进程例外事件相关的信号。如进程越界,或企图写一个只读的内存区域(如程序正文区),或执行一个特权指令及其他各种硬件错误。
(3)在与系统调用期间遇到不可恢复条件相关的信号。如执行系统调用exec时,原有资源已经释放,而目前系统资源又已经耗尽。
(4)与执行系统调用时遇到非预测错误条件相关的信号。如执行一个并不存在的系统调用。
(5)在用户态下的进程发出的信号。如进程调用系统用kill向其他进程发送信号。
(6)与终端交互相关的信号。如用户关闭一个终端,或按下break键等情况。
(7)跟踪进程执行的信号。
二、有关信号的系统调用
   系统调用signal是进程用来设定某个信号的处理方法,系统调用kill是用来发送信号给执行进程的。这两个调用可以形成信号的基本操作。后两个调用pause和alarm是通过信号实现的进程暂停和定时器,调用alarma是通过信号通知进程定时器到时。所以在这里还要介绍这两个调用:
  1.signal系统调用
   系统调用signal用来设置信号的处理方法。该调用声明的格式如下:
void(*signal signum,void(*handler)(int)))(int);
   在使用该调用的进程中加入以下头文件:
#include<signal.h>
   上述声明格式比较复杂,如果不清楚如何使用,也可以通过下面这种类型定义的格式来使用(POSIX的定义):
   typedef void(*sighandler_t)(int);
   sighandler_t_signal(int signum,sighandler_t handler);
但这种格式在不同的系统中有不同的类型定义,所以要使用这种格式,最好还是参考一下联机手册。
   在调用中,参数signum指出要设置处理方法的信号。第二个参数handler是一个处理函数,或者是
   SIG_IGN:忽略参数signum所指的信号。
   SIG_DFL:回复参数signum所指信号的处理方法为默认值。
   传递给信号处理例程的整数参数是信号值,这样可以使得一个信号处理例程处理多个信号。系统调用signal返回值是指定信号signum前一次的处理历程或者错误代码SIG_ERR。
   2.kill系统调用
   系统调用ikll用来向进程发送一个信号。改调用声明的格式如下:
   int kill(pid_t pid,int sig);
   在使用该调用的进程中加入以下头文件:
   #include<sys/types.h>
   #include<signal.h>
   该系统调用可以用来向任何进程或进程组发送任何信号。
   如果参数pid是正数,那么该调用将信号sig发送到进程号为pid的进程。
   如果参数pid等于0,那么信号sig将发送给当前进程所属进程组里的所有进程。
   如果参数pid等于-1,信号sig将发送给除了进程1和自身以外的所有进程。
   如果参数pid小于-1,信号sig将发送给属于进程组-pid的所有进程。如果参数为0.将不发送信号。该调用执行成功时,返回值为0;错误时,返回-1,并设置相应的错误代码errno。下面是一些可能返回的错误代码:
   EINVAL:指定的信号sig无效。
   ESRCH:参数pid指定的进程或进程组不存在。注意,在进程表项中存在的进程,可能是一个还没有被wait收回,但已经终止执行的僵死进程。
   EPERM:进程没有权利将这个信号发送到执行接收信号的进程。因为,一个进程被允许将信号发送到进程pid时,必须拥有root权利,或者是发送出调用的进程UID或EUID与指定接收的进程的UID或保存用户ID(saveset-user-ID)相同。如果参数小于-1,即该信号发送给一个组,则该错误表示组中有成员进程不能接收该信号。
  3.pause系统调用
  系统调用pause的作用是等待一个信号。该调用的声明格式如下:
  int pause(void);
  在使用才调用的进程中加入以下头文件:
  #include<unistd.h>
  该调用使得发出调用的进程进入睡眠,知道接收到一个信号为止。该调用总是返回-1,并设置错误代码EINTR(接收到一个信号)。
4.alarm和setitimer系统调用
   系统调用alarm的功能是设置一个定时器,当定时器及时到达时,将发出一个信号给进程。该调用的声明格式如下:
   unsigned int alarm(unisigned int seconds);
   在使用该调用的进程中加入以下头文件:
   #include<unistd.h>
   系统调用alarm安排内核我调用进程在指定的seconds秒后发出一个SIGALRM的信号。如果执行的参数seconds为0,则不再发送SIGALRM信号。后一次设定将取消前一次的设定。该调用返回值为上次定时调用到发送之间剩余的时间,或者因为没有前一次定时调用而返回0。
    注意,在使用时,alarm只设定为发送一次信号,如果要发送多次,就需要多次使用alarm调用。
    对于alarm,这里不再举例。现在的系统中许多程序不再使用alarm调用,而是使用setitimer调用来设置定时器,用getitimer来得到定时器的状态,这两个调用的声明格式如下:
    int getitimer(int which,struct itimerval *value);
    int getitimer(int which,const struct itimerval *value,struct itimerval *ovalue);
    在使用这两个调用的进程中加入以下头文件:
    #include<sys/time.h>
    该系统调用给进程提供了三个定时器,他们各自有其独有的计时域,当其中任何一个到达,就发送一个相应的信号给进程,并使得计时器重新开始。三个计时器由参数which指定,如下所示:
   TIMER_REAL:按实际时间计时,计时到达将给进程发送SIGALRM信号。
   ITIMER_VIRTUAL:仅当进程执行时才进行计时。计时到达将发送SIGVTALRM信号给进程。
   ITIMER_PROF:当进程执行时和系统为该进程执行动作时都计时。与ITIMER_VIR-TUAL是一对,该定时器经常用来统计进程在用户态和内核态花费的时间。计时到达将发送SIGPROF信号给进程。
   定时器中的参数value用来指明定时器的时间,其结构如下:
   struct itimerval{
   struct itimerval it_interval;/*下一次的取值*/
   struct itimerval it_value;/*本次的设定值*/
   };
   该结构中timeval结构定义如下:
   struct timerval{
   long tv_sec;/*秒*/
   long tv_usec;/* 微妙,1秒=1000000微秒*/
   };
   在setitimer调用中,参数ovalue如果不为空,则其中保留的是上次调用设定的值。定时器将it_value递减到0时,产生一个信号,并将it_value的值设定为it_interval的值,然后重新开始计时,如此往复。当it_value设定为0时,计时器停止,或者当他计时到期,而it_interval为0时停止。调用成功时,返回0;错误时,返回-1,并设置相应的错误代码errno:
   EFAULT:参数 value或ovslue是无效的指针。
   EINVAL:参数which不是ITIMER_REAL、ITIMER_VIRT或ITIMER_PROF中的一个。
  
 作者: yangzhu1982
转自:http://blog.csdn.net/yangzhu1982/article/details/6256945,转载请注明出处。谢谢。

TAG:

 

评分:0

我来说两句

Open Toolbar