分类: C/C++
2010-03-11 18:03:18
信号充当的是软件中断,向应用指示异步事件的发生;要处理信号,就必须把一个信号处理器与一个特定的信号类型关联在一起.一旦信号被引发,与之关联的信号处理器就会在信号上下文中被自动调用,这个信号上下文与被中断的主执行上下文是分开的.在信号处理器返回之后,程序会在主上下文中、从收到信号之前所在的地方继续执行,就好像中断从来都没有发生过一样;
ACE_Sig_Action类:
使用POSIX的sigaction()调用,程序员能够把一个动作(比如:某个回调函数)与某个特定信号的发生关联在一起;ACE提供了一个封装了sigaction()调用的包装;这个包装为信号登记提供了一个类型安全的接口;ACE还给它的一种参数类型sigset_t提供了一个类型安全的包装,这种类型表示的是一组信号;
在目前的信号模型下,任何一个特定的信号号码只能与一个信号处理器函数关联起来.这就意味着,你不能让多个信号处理器函数在某个信号引发时全都被调用.当然,你可以把同一个信号处理器函数与多个不同的信号号码关联起来;一般而言,信号处理器函数应该非常轻,并且能立即返回;否则就会阻塞你的程序的其余部分的执行;信号处理器函数通常用于向应用指示某个信号的发生;对信号进行实际处理的工作会在信号处理器返回之后进行;
ACE_Sig_Set类:
这个类是一个容器;它里面存放的是信号号码,它是一个信号集合,表示一组信号;如果要屏蔽一组信号,可以先把要被屏蔽的信号加入到这个容器中,然后再调用ACE_Sig_Action::mask()方法把这个信号容器加入到当前信号掩码中,而这个方法会把信号掩码传递给OS,OS会在信号处理器执行的过程中自动禁用这些信号;
你应该在登记信号处理器函数之前指定信号掩码,否则,OS不会注意到你指定的信号掩码;
注意:在针对特定信号登记完你想要的动作之后,你无需再保留ACE_Sig_Action类的对象;如果你想要改变之前设定的特定信号的处理方式(信号处理器函数),那么可以再另外创建一个ACE_Sig_Action类的对象来完成该工作;
大多数sigaction()的实现都允许你指定SA_RESTART标志,让被信号中断的调用在不被你知道的情况下自动重启;但是要注意,并不是所有被信号中断的调用都允许重启;一般而言,大多数SVR4 UNIX平台都提供了sigaction()实现和SA_RESTART标志;使用ACE的话,你可以调用ACE_Sig_Action::flags()方法来设置SA_RESTART标志,你也可以把这个标志作为参数传递给ACE_Sig_Action类的构造函数;
事件处理器:
在类型安全的sigaction()和sigset_t的封装基础之上,捆绑了一种面向对象的、基于事件处理器的信号登记与分派方案,你可以通过ACE_Sig_Handler类使用这一方案;
ACE_Sig_Handler类允许客户程序员登记基于ACE_Event_Handler类的对象,在有信号发生的时候被回调;ACE_Event_Handler类是ACE Reactor框架的中枢,但是在使用ACE_Sig_Handler时,并没有向反映器登记的步骤;
作为客户程序员,我们必须从ACE_Event_Handler类派生自己的子类,并实现虚方法handle_signal(),以响应信号处理;
siginfo_t介绍:
这个结构用于存放更多的关于信号的信息,它说明了正在递送的信号的起因及属性;取决于捕捉到的信号,siginfo_t结构里面存放的信息的含义会发生变化;基本上所有收到的信号都有si_signo、si_errno和si_code信息可用;
si_code成员告诉我们的是该信号的发送原因;有些值是真对特定信号的,而另外一些值则与所有信号都有关系;有时候我们必须对信号的值进行检查,以确定siginfo_t的哪些部分是适用的,比如:si_address属性只适用于信号SIGILL、SIGFPE、SIGSEGV和SIGBUS.siginfo_t的si_address属性描述的是引发信号的内存位置;
ucontext_t介绍:
这个结构中存放的是信号引发之前的一瞬间应用的执行上下文,比如:CPU状态和FPU(floating-pointer unit)寄存器,等等一些CPU现场信息;
堆放多个信号处理器:
POSIX信号模型只允许一个信号处理器在某个信号发生的时候被回调;但是有时候我们需要针对某个信号登记多个信号处理器,当该信号发生的时候,为该信号登记的所有信号处理器都要被回调;我们可以使用ACE提供的ACE_Sig_Handlers类来完成这个任务;这个类允许你针对某个特定的信号事件堆放(stacking)多个信号处理器对象;使用这个类的register_handler()方法来为某个特定的信号登记多个信号处理器对象;这个方法的参数为信号号码和ACE_Event_Handler类的对象;
ACE_Sig_Handlers类的对象使得我们能够针对同一个信号号码登记多个信号处理器对象,当信号发生的时候,所有的处理器对象都会被自动回调;
保护临界区:
当某个信号发生的时候,单线程应用中的控制线程(或多线程应用中的某个线程)会跳到信号处理器例程中执行,执行完信号处理例程之后,返回到常规处理;而在多线程应用中,对共享资源的保护,需要使用一些同步机制来实现;同样,可以把特定的小代码区当作临界区,你不想让任何信号中断临界区中代码的执行,也可以使用临界区同步机制来完成这项任务;
ACE提供了一个基于作用域的信号保护类ACE_Sig_Guard,它可以用来禁止或屏蔽临界区代码处理过程中的信号处理;但是还有一些信号是不能被屏蔽的,比如:SIGKILL和SIGSTOP;
ACE_Sig_Guard类的对象可以用来保护临界区代码的执行不会被信号中断,使ACE_Sig_Set ss中的所有信号都无法在这些代码的执行过程中发生;换句话说,ACE_Sig_Guard类的对象可以使得在ACE_Sig_Set ss对象中登记的所有信号都不能在临界区代码的执行过程中发生;在临界区代码的作用域范围内发生的所有信号都是"安全"的;即使是在程序离开临界区代码的作用域块之前我们向进程发送了一个信号,那么,该信号也是在我们的程序离开临界区代码作用域块之后才被接收和处理的;之所以会这样,那是因为我们在临界区代码中对这些信号做了保护,屏蔽掉了这些信号,因而这些信号就会处在等待处理的状态,而并不会递送给我们的进程.一旦我们的程序离开了临界区代码的作用域块,对这些信号的保护和屏蔽就会被自动解除,等待处理的信号就会被送给我们的进程;
通过反应器管理信号:
我们可以通过与ACE_Sig_Handler类似的方式把信号事件分派给事件处理器.实际上,在幕后,大多数反应器实现都使用了ACE_Sig_Handler来处理信号分派;重要的是要记住,与Reactor框架不同的是,信号处理器是在信号上下文中调用的.但是,你可以使用反应器通知,把控制转回用户上下文中指定的控制点;