函数 abort、exit、_exit、_Exit 区别
abort 函数
abort 函数的功能是使程序异常终止。
#include <stdlib.h>
void abort(void);
// 此函数不返回值。
abort 函数将发送 SIGABRT 信号给调用进程, 前提是进程没有忽略此信号,而且 POSIX.1说明 abort 并不在意进程对这个信号阻塞还是忽略。ISO C标准中是调用 raise(SIGABROT) 函数,向主机环境发送一个程序没有成功结束的通知。
ISO C
SIGABRT是中止一个程序,它可以被捕捉,但不能被阻塞。处理函数返回后,所有打开的文件描述符将会被关闭,流也会被flush。程序会结束,有可能的话还会core dump。 当程序调用abort(3)时,该进程会向自己发送SIGABRT信号。所以,SIGABRT一般用于信号中一些关键的处理,assert失败时也会使用它。你不应该去捕捉SIGSEGV和SIGABRT信号,如果收到这种信号,说明进程处于一个不确定的状态,很可能会直接挂起。
我们一起看一下 POSIX.1 标准中 abort 函数:
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void abort(void)
{
/*信号集用来描述信号的集合
linux所支持的所有信号可以全部或部分的出现在信号集中
主要与信号阻塞相关函数配合使用。*/
sigset_t mask;
struct sigaction action;
// sigaction() 根据参数 signum 指定的信号编号来设置该信号的处理函数。
sigaction(SIGABRT, NULL, &action);
/*
SIG_DFL,SIG_IGN 分别表示无返回值的函数指针,指针值分别是0和1,这两个指针值逻辑上讲是实际程序中不可能出现的函数地址值。
SIG_DFL:默认信号处理程序
SIG_IGN:忽略信号的处理程序
*/
if(action.sa_handler == SIG_IGN)
{
action.sa_handler = SIG_DFL;
sigaction(SIGABRT, &action, NULL);
}
// 如果是默认信号处理函数,则冲洗所有标准I/O流
if( action.sa_handler == SIG_DFL )
fflush(NULL);
// else 否则
sigfillset( &mask ); // 获取 linux 支持的62中信号
sigdelset(&mask, SIGABRT); // 在mask信号集中删除 SIGABRT
/* 一个进程的信号屏蔽字规定了当前阻塞而不能递送给该进程的信号集。
int sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oldset);
sigprocmask()可以用来检测或改变目前的信号屏蔽字,其操作依参数how来决定,如果参数oldset不是NULL指针,
那么目前的信号屏蔽字会由此指针返回。SIG_SETMASK:设置当前信号掩码为参数 set 所指向的信号集中所包含的信号。
每个进程都有一个用来描述哪些信号递送到进程时将被阻塞的信号集,该信号集中的所有信号在递送到进程后都将被阻塞。
*/
sigprocmask(SIG_SETMASK, &mask, NULL); // 设置 除 SIGABRT 信号外 其他信号都在发送给进程是阻塞
kill(getpid(), SIGABRT); // 发送 SIGABRT 信号
/* 如果函数走到这一步 ,说明程序已经捕获到了 SIGABRT 信号
所有程序可能会产生新的输出,这里再一次冲洗 I/O
*/
fflush(NULL);
action.sa_handler = SIG_DFL;
sigaction(SIGABRT, &action, NULL);
sigprocmask(SIG_SETMASK, &mask, NULL);
kill(getpid(), SIGABRT);
exit(1);
}
所以,要么程序在捕捉到 SIGABRT 信号的处理函数中 结束进程,要么由 abort 自己结束进程,但是通常我们会在自己的信号捕获函数中做这样的操作,不会让 abort 返回,主要是可以完成一些资源的回收和释放,当然 abort 最终也是这样做的,只是用户自己可以定制的处理。