前面已提及abort函数的功能是使程序异常终止。
#include <stdlib.h>
void abort(void);
//此函数不返回值
此函数将SIGABRT
信号发送给调用进程(进程不应忽略此信号)。ISO C规定,调用abort将向主机环境递送一个未成功终止的通知,其方法是调用raise(SIGABRT)
函数。
ISO C要求若捕捉到此信号而且相应信号处理程序返回,abort仍不会返回到其调用者。如果捕捉到此信号,则信号处理程序不能返回的唯一方法是它调用exit
、_exit
、_Exit
、longjmp
或siglongjmp
(10.15节讨论了longjmp
和siglongjmp
之间的区别)。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
的调用返回了,则该进程一定已捕捉到该信号,并且也从该信号处理程序返回。