这是我的页面头部

c++上下文使用信号小结

说明:本文的上下文环境是 unix/linux操作系统,g++编译环境, gdb调试环境。


signal是 阻塞模式的良好搭挡。一些需要低速调用的程序,例如 socket、oracle连接,若工作在非阻塞模式下,轮询会有大量的CPU开销,而阻塞模式则使得程序不能及时处理那些需要实时响应的操作。signal可以将程序从阻塞状态唤醒,以处理紧急事件,然后再做别的事情。

unix_program_advance 一书中第十章,详细地介绍了 unix 系统提供的信号捕获函数。

在开发中应注意:

1、确定要使用阻塞还是非阻塞。oci和 socket中,默认都是阻塞的。

在socket 中,可以使用  fcntl()来设置O_NONBLOCK, O_NDELAY.

 

if (fcntl(fd, F_SETFL, orig_flags | O_NONBLOCK) < 0
{
    
}

在oci中,可以使用 OCIAttrSet()函数的 OCI_ATTR_NONBLOCKING_MODE 属性值来设置oci的操作为非阻塞模式。详细的用法见 Oracle Call Interface Programmer's Guide  B10779-01. chapter 2 中 Nonblocking Mode in OCI 一节。

当socket为阻塞模式时,可以使用 setsockopt来设置 recv的超时时间。

1、任何一个可能无限阻塞的调用,调用之间加一个alarm()

在oracle中,如果在呼叫 oracle时网络掉线。则函数会永远阻塞。所以,有必要在可能阻塞的函数前,加一个alarm()以在超时后将其唤醒。

下面示例了信号的使用。当函数调用超过3秒后,跳出函数并重新连接数据库以执行此操作。直到操作三次仍不能成功,则放弃操作。

 

do
{
    alarm(
3);
    
if(  table.query() == false && errno == EINTR ) 
    {
        conndb();
    }
    
else
    {
     
break;
    }
}
while (retryTimes < 3)

alarm(0);

 

2、用 sigaction() 而不是 signal() 来设置信号钩子。signal()跨平台性能不好。在 linux下跑得很健壮的程序,到了 hp-unix或 ibm-aix上表现得非常糟糕,而 solaris上 signal 又有不同的表现。较于 signal, sigaction 定义了被中断的低速调用是否重新唤起 ( 默认不唤起);sigactionl() 还有屏蔽功能,这使得信号处理函数可以专注地处理当前的信号。

 

3、当前信号处理函数结束前要唤起先前定义的信号处理函数。

sigaction( signo, NULL, oldHandle );

4、可以设置捕获所有的信号,以避免程序收到信号时意外退出。信号编号下界限为1,最高界限是SIGMAX,可以使用for()循环设置钩子。

5、子进程会继承父进程的信号钩子,但不能继承父进程中已经设置的信号(例如,闹钟信号)。

6、如果想在收到信号后跳出当前的上下文环境,用  throw()。

 

相关资料 :

1、 <the design of the unix operationg system>第 7.2 信号  从操作系统实现的角度,详细地介绍了信号的分类、信号如何被唤醒、信号在什么情况下会丢失或引发意外。(注 : 书中讨论的内容是 原始版本的 signal(),实际上, sigaction 已经解决了信号丢失、信号异常的问题。)

2、<Advanced Programming in the UNIX Environment>  第 10章详细介绍了不可靠信号signal、可靠信号sigaction在 posix.1、SVR4、4.3+BSD中,信号的实现。并给出了对信号进行处理的相关源码。

 

 

使用信号的完整示例:

 

信号一个极有用的使用是:将进程从阻塞状态唤醒。
信号有一定的局限性。信号不能像 message 那样传递消息结构,而只能传递一个简单的信号。
信号会破坏程序的流程。因为我们不知道信号究竟什么时候会发生。
一个比较合理的处理办法是:
当进程收到关注的信号时,执抛出一个异常。这样,程序可以根据自己的意愿想停就停,想开就开。
示例如下:

posted @ 2008-09-24 00:03  范晨鹏  阅读(1718)  评论(0编辑  收藏  举报