我们早先提到过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对这个函数的规定让实现来决定输出流是否被冲洗以及临时文件是否被删除。POSIX.1要求更多,要求如果abort调用终止进程,那么在进程里的打开的标准I/O流上的效果将和进程在终止前为每个流调用fclose的效果一样。
系统V的早期版本从abort函数产生SIGIOT信号。此外,一个进程可能忽略这个信号或捕获它并从信号处理器返回,在这种情况下abort返回到它的调用者。
4.3BSD产生了SIGILL信号。在这样做之前,4.3BSD函数反阻塞这个信号并重置它的布署为SIG_DFL(带有核心文件的终止)。这避免了一个进程忽略或捕获这个信号。
历史上,abort的实现在它们处理标准I/O流的方式上有所区别。为了健壮的程序和更好的可移植性,如果我们想标准I/O流被冲洗,那么我们在调用abort前应该明确地做这件事。
因为多数UNIX系统的temfile的实现在创建这个文件后立即调用unlink,所以ISO C关于临时文件的警告通常不用我们担心。
下面的代码展示了POSIX.1规定的abort函数的一个实现:
- #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... */
- }
我们首先来看到默认动作是否发生,如果是的话,我们把所有标准I/O流冲洗。这并不等价于对所有打开的流的fclose(因为它只是冲洗它们
而不关闭它们),但是当进程终止时,系统关闭所有打开的文件。如果进程捕获了信号并返回,我们再次冲洗所有的流,因为进程可能已经产生更多的输出。我们唯
一没有处理的情况是如果进程捕获信号并调用_exit或_Exit。在这种情况下,在内存里任何未冲洗的标准I/O缓冲都被舍弃了。我们假定这样做的调用
者并不想这些缓冲被冲洗。
回想10.9节,如果调用kill导致信号为调用者而产生,且信号没有被阻塞(上面的代码里我们已经保证
了),那么信号(或一些待定,未阻塞的信号)在kill返回前被分发到进程。我们阻塞除了SIGABRT之外的所有信号,所以我们知道如果kill调用返
回,进程捕获了这个信号而且信号处理器返回了。
阅读(1846) | 评论(0) | 转发(0) |