c++上下文使用信号小结
说明:本文的上下文环境是 unix/linux操作系统,g++编译环境, gdb调试环境。
signal是 阻塞模式的良好搭挡。一些需要低速调用的程序,例如 socket、oracle连接,若工作在非阻塞模式下,轮询会有大量的CPU开销,而阻塞模式则使得程序不能及时处理那些需要实时响应的操作。signal可以将程序从阻塞状态唤醒,以处理紧急事件,然后再做别的事情。
unix_program_advance 一书中第十章,详细地介绍了 unix 系统提供的信号捕获函数。
在开发中应注意:
1、确定要使用阻塞还是非阻塞。oci和 socket中,默认都是阻塞的。
在socket 中,可以使用 fcntl()来设置O_NONBLOCK, O_NDELAY.
{
}
在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 那样传递消息结构,而只能传递一个简单的信号。
信号会破坏程序的流程。因为我们不知道信号究竟什么时候会发生。
一个比较合理的处理办法是:
当进程收到关注的信号时,执抛出一个异常。这样,程序可以根据自己的意愿想停就停,想开就开。
示例如下:
范晨鹏
------------------
软件是一种态度
成功是一种习惯