前面已提及abort函数的功能是使程序异常终止。

#include <stdlib.h>
    void abort(void);
//此函数不返回值

此函数将SIGABRT信号发送给调用进程(进程不应忽略此信号)。ISO C规定,调用abort将向主机环境递送一个未成功终止的通知,其方法是调用raise(SIGABRT)函数。

ISO C要求若捕捉到此信号而且相应信号处理程序返回,abort仍不会返回到其调用者。如果捕捉到此信号,则信号处理程序不能返回的唯一方法是它调用exit_exit_Exitlongjmpsiglongjmp(10.15节讨论了longjmpsiglongjmp之间的区别)。POSIX.1也说明abort并不理会进程对此信号的阻塞和忽略。

让进程捕捉 SIGABRT 的意图是:在进程终止之前由其执行所需的清理操作。如果进程并不在信号处理程序中终止自己,POSIX.1声明当信号处理程序返回时,abort终止该进程。

ISO C针对此函数的规范将下列问题留由实现决定:是否要冲洗输出流以及是否要删除临时文件(见5.13节)。 POSIX.1的要求则更进一步,它要求如果abort调用终止进程,则它对所有打开标准I/O流的效果应当与进程终止前对每个流调用fclose相同。

System V的早期版本中,abort函数产生SIGIOT信号。更进一步,进程忽略此信号或者捕捉它并从信号处理程序返回,这都是可能的,在返回情况下,abort返回到它的调用者。

4.3BSD产生SIGILL信号。在此之前,该函数解除对此信号的阻塞,将其配置恢复为SIG_DFL (终止并创建core文件)。这阻止一个进程忽略或捕捉此信号。

历史上,abort的各种实现在如何处理标准I/O流方面是并不相同的。对于保护性的程序设计以及为提高可移植性,如果希望冲洗标准 I/O 流,则在调用 abort 之前要执行这种操作。在err_dump函数中实现了这一点(见附录B)。

因为大多数UNIX系统tmpfile(临时文件)的实现在创建该文件之后立即调用unlink,所以ISO C关于临时文件的警告通常与我们无关。

实例

图10-25中的abort函数是按POSIX.1说明实现的。

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void
abort(void)            /* POSIX-style abort() function */
{
    sigset_t            mask;
    struct sigaction    action;

    /* Caller can't ignore SIGABRT, if so reset to default */
    sigaction(SIGABRT, NULL, &action);
    if (action.sa_handler == SIG_IGN) {
        action.sa_handler = SIG_DFL;
        sigaction(SIGABRT, &action, NULL);
    }
    if (action.sa_handler == SIG_DFL)
        fflush(NULL);            /* flush all open stdio streams */

    /* Caller can't block SIGABRT; make sure it's unblocked */
    sigfillset(&mask);
    sigdelset(&mask, SIGABRT);    /* mask has only SIGABRT turned off */
    sigprocmask(SIG_SETMASK, &mask, NULL);
    kill(getpid(), SIGABRT);    /* send the signal */

    /* If we're here, process caught SIGABRT and returned */
    fflush(NULL);                /* flush all open stdio streams */
    action.sa_handler = SIG_DFL;
    sigaction(SIGABRT, &action, NULL);    /* reset to default */
    sigprocmask(SIG_SETMASK, &mask, NULL);    /* just in case ... */
    kill(getpid(), SIGABRT);                /* and one more time */
    exit(1);    /* this should never be executed ... */
}

图10-25 abort的POSIX.1实现

首先查看是否将执行默认动作,若是则冲洗所有标准I/O流。这并不等价于对所有打开的流调用fclose(因为只冲洗,并不关闭它们),但是当进程终止时,系统会关闭所有打开的文件。

如果进程捕捉此信号并返回,那么因为进程可能产生了更多的输出,所以再一次冲洗所有的流。不进行冲洗处理的唯一条件是如果进程捕捉此信号,然后调用_exit_Exit。在这种情况下,任何未冲洗的内存中的标准I/O缓存都被丢弃。我们假定捕捉此信号,而且_exit_Exit的调用者并不想要冲洗缓冲区。

回忆10.9节,如果调用kill使其为调用者产生信号,并且如果该信号是不被阻塞的(图10-25中的程序保证做到这一点),则在kill返回前该信号(或某个未决、未阻塞的信号)就被传送给了该进程。我们阻塞除SIGABRT外的所有信号,这样就可知如果对kill的调用返回了,则该进程一定已捕捉到该信号,并且也从该信号处理程序返回。

results matching ""

    No results matching ""