Sun Studio 12:Fortran 编程指南

6.3.3 异常处理程序和 ieee_handler()

关于 IEEE 异常,通常需要关注以下问题:

用户例程的异常捕获以系统产生浮点异常信号开始。signal: floating-point exception的 UNIX 标准名称是 SIGFPE。出现异常时,SPARC 平台上的缺省情况是产生 SIGFPE。要使系统产生 SIGFPE,必须先启用异常捕获,这通常通过对 ieee_handler() 的调用来完成。

6.3.3.1 建立异常处理程序函数

要将函数作为异常处理程序建立,请将函数名称与要监视的异常的名称和要采取的操作一起传递给 ieee_handler()。一旦建立了处理程序,无论何时出现特定的浮点异常和调用指定的函数,都会产生 SIGFPE 信号。

ieee_handler() 的调用形式如下表所示:

表 6–4 ieee_handler (action , exception , handler) 的参数

参数 

类型 

可能值 

action

character

getsetclear

exception

character

invaliddivisionoverflowunderflowinexact

handler

函数名 

用户处理函数的名称或 SIGFPE_DEFAULT、SIGFPE_IGNORESIGFPE_ABORT

返回值 

integer

0 =OK

f95 编译的、调用 ieee_handler() 的 Fortran 95 例程还应该声明:

#include ’floatingpoint.h’

特殊参数 SIGFPE_DEFAULTSIGFPE_IGNORESIGFPE_ABORT 定义在这些包含文件中,可用于更改与特定异常相应的程序行为:

SIGFPE_DEFAULT SIGFPE_IGNORE

出现指定异常时不采取任何操作。 

SIGFPE_ABORT

程序在异常时中止(可能会使用转储文件)。 

6.3.3.2 编写用户异常处理程序函数

异常处理程序采取的操作由您决定。但是,此例程必须是整型函数,且具有下面指定的三个参数:

handler_name( sig, sip, uap )

示例:异常处理程序函数:


      INTEGER FUNCTION hand( sig, sip, uap )
      INTEGER sig, location
      STRUCTURE /fault/
           INTEGER address
           INTEGER trapno
      END STRUCTURE
      STRUCTURE /siginfo/
           INTEGER si_signo
           INTEGER si_code
           INTEGER si_errno
           RECORD /fault/ fault
      END STRUCTURE
      RECORD /siginfo/ sip
      location = sip.fault.address
      ... actions you take ...
      END

此示例需要修改才能运行在 64 位 SPARC 体系结构上,方法是使用 INTEGER*8 替换每个 STRUCTURE 中的所有 INTEGER 声明。

如果由 ieee_handler() 启用的处理程序例程与此示例中一样,是用 Fortran 编写的,则此例程不能对其第一个参数 (sig) 进行任何引用。该第一个参数按值传递给此例程,并且只能作为 loc(sig) 进行引用。此值是信号编号。

通过处理程序检测异常

下列示例展示如何创建处理程序例程来检测浮点异常。

示例:检测异常并中止


demo% cat DetExcHan.f
      EXTERNAL myhandler
      REAL :: r = 14.2 , s = 0.0
      i = ieee_handler (’set’, ’division’, myhandler )
      t = r/s
      END

      INTEGER FUNCTION myhandler(sig,code,context)
      INTEGER sig, code, context(5)
      CALL abort()
      END
demo% f95 DetExcHan.f
demo% a.out
Abort
demo%

SIGFPE 可在浮点异常出现的任何时间产生。检测到 SIGFPE 时,控制将传递给 myhandler 函数,该函数会立即中止。用 -g 编译,并使用 dbx 查找异常位置。

通过处理程序定位异常

示例:定位异常(打印地址)并中止:


demo% cat LocExcHan.F
#include "floatingpoint.h"
      EXTERNAL Exhandler
      INTEGER Exhandler, i, ieee_handler
      REAL:: r = 14.2 ,  s = 0.0 , t
C  Detect division by zero
      i = ieee_handler( ’set’, ’division’, Exhandler )
      t = r/s
      END

      INTEGER FUNCTION Exhandler( sig, sip, uap)
      INTEGER sig
      STRUCTURE /fault/
                  INTEGER address
      END STRUCTURE
      STRUCTURE /siginfo/
                  INTEGER si_signo
                  INTEGER si_code
                  INTEGER si_errno
                  RECORD /fault/ fault
      END STRUCTURE
      RECORD /siginfo/ sip
      WRITE (*,10)  sip.si_signo, sip.si_code, sip.fault.address
10        FORMAT(’Signal ’,i4,’ code ’,i4,’ at hex address ’, Z8 )
      Exhandler=1
      CALL abort()
      END
demo%  f95 -g LocExcHan.F
demo%  a.out
Signal   8 code    3 at hex address    11230
Abort
demo%

在 64 位 SPARC 环境中,请用 INTEGER*8 替换每个 STRUCTURE 中的 INTEGER 声明,用 i8 替换 i4 格式。(注意,该例接受 VAX Fortran STRUCTURE 语句,依靠的是 f95 编译器的扩展。)

大多数情况下,知道异常的实际地址并无太大用处,但对于 dbx 除外:


demo% dbx a.out
(dbx) stopi at 0x11230     Set breakpoint at address
(2) stopi at &MAIN+0x68
(dbx) run               Run program
Running: a.out
(process id 18803)
stopped in MAIN at 0x11230
MAIN+0x68:      fdivs   %f3, %f2, %f2
(dbx) where           Shows the line number of the exception
=>[1] MAIN(), line 7 in "LocExcHan.F"
(dbx) list 7          Displays the source code line
    7         t = r/s
(dbx) cont            Continue after breakpoint, enter handler routine
Signal    8 code    3 at hex address    11230
abort: called
signal ABRT (Abort) in _kill at 0xef6e18a4
_kill+0x8:      bgeu    _kill+0x30
Current function is exhandler
   24         CALL abort()
(dbx) quit
demo%

当然,还有更容易的方法来确定引起错误的源码行。但是,本例确实足以展示异常处理的基本内容。