一、glibc的实现
#ifdef NDEBUG
# define assert(expr) (void (0))
#else
# define assert(expr) \
((expr) \
? void (0) \
: __assert_fail (#expr, __FILE__, __LINE__, __ASSERT_FUNCTION))
#endif
/* This prints an "Assertion failed" message and aborts. */
/* 打印“断言失败”消息并中止 */
extern void __assert_fail (const char *__assertion, const char *__file,
unsigned int __line, const char *__function)
__THROW __attribute__ ((__noreturn__));
void
__assert_fail_base (const char *fmt, const char *assertion, const char *file,
unsigned int line, const char *function)
{
char *str;
#ifdef FATAL_PREPARE
FATAL_PREPARE;
#endif
int total;
if (__asprintf (&str, fmt,
__progname, __progname[0] ? ": " : "",
file, line,
function ? function : "", function ? ": " : "",
assertion, &total) >= 0)
{
/* Print the message. */
(void) __fxprintf (NULL, "%s", str);
(void) fflush (stderr);
total = (total + 1 + GLRO(dl_pagesize) - 1) & ~(GLRO(dl_pagesize) - 1);
struct abort_msg_s *buf = __mmap (NULL, total, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE, -1, 0);
if (__glibc_likely (buf != MAP_FAILED))
{
buf->size = total;
strcpy (buf->msg, str);
/* We have to free the old buffer since the application might
catch the SIGABRT signal. */
struct abort_msg_s *old = atomic_exchange_acq (&__abort_msg, buf);
if (old != NULL)
__munmap (old, old->size);
}
free (str);
}
else
{
/* At least print a minimal message. */
static const char errstr[] = "Unexpected error.\n";
__libc_write (STDERR_FILENO, errstr, sizeof (errstr) - 1);
}
abort ();
}
void
__assert_fail (const char *assertion, const char *file, unsigned int line,
const char *function)
{
__assert_fail_base (_("%s%s%s:%u: %s%sAssertion `%s' failed.\n%n"),
assertion, file, line, function);
}
/* Cause an abnormal program termination with core-dump. */
void
abort (void)
{
struct sigaction act;
sigset_t sigs;
/* First acquire the lock. */
__libc_lock_lock_recursive (lock);
/* Now it's for sure we are alone. But recursive calls are possible. */
/* Unblock SIGABRT. */
if (stage == 0)
{
++stage;
__sigemptyset (&sigs);
__sigaddset (&sigs, SIGABRT);
__sigprocmask (SIG_UNBLOCK, &sigs, 0);
}
/* Send signal which possibly calls a user handler. */
if (stage == 1)
{
/* This stage is special: we must allow repeated calls of
`abort' when a user defined handler for SIGABRT is installed.
This is risky since the `raise' implementation might also
fail but I don't see another possibility. */
int save_stage = stage;
stage = 0;
__libc_lock_unlock_recursive (lock);
raise (SIGABRT);
__libc_lock_lock_recursive (lock);
stage = save_stage + 1;
}
/* There was a handler installed. Now remove it. */
if (stage == 2)
{
++stage;
memset (&act, '\0', sizeof (struct sigaction));
act.sa_handler = SIG_DFL;
__sigfillset (&act.sa_mask);
act.sa_flags = 0;
__sigaction (SIGABRT, &act, NULL);
}
/* Try again. */
if (stage == 3)
{
++stage;
raise (SIGABRT);
}
/* Now try to abort using the system specific command. */
if (stage == 4)
{
++stage;
ABORT_INSTRUCTION;
}
/* If we can't signal ourselves and the abort instruction failed, exit. */
if (stage == 5)
{
++stage;
_exit (127);
}
/* If even this fails try to use the provided instruction to crash
or otherwise make sure we never return. */
while (1)
/* Try for ever and ever. */
ABORT_INSTRUCTION;
}
abort函数的功能是使异常程序终止。
#include <stdlib.h>
void abort(void);
此函数不返回
此函数将SIGABRT信号发送给调用进程(进程不应忽略此信号)。ISO C规定,调用abort将向主机环境递送一个未成功的终止通知,其方法是调用raise(SIGABRT)函数。
二、另一种实现
#ifdef NDEBUG
# define assert(expr) (void (0))
#else
# define assert(expr) \
if (!(expr)) \
{ \
pid_t pid = getpid(); \
pid_t tid = syscall(SYS_gettid); \
/* 打印 pid tid __FILE__, __LINE__, __ASSERT_FUNCTION */
(void)syscall(SYS_tgkill, pid, tid, SIGABRT); \
}
#endif
__attribute__ noreturn 表示没有返回值
This attribute tells the compiler that the function won't ever return, and this can be used to suppress errors about code paths not being reached. The C library functions abort() and exit() are both declared with this attribute:
这个属性告诉编译器函数不会返回,这可以用来抑制关于未达到代码路径的错误。 C库函数abort()和exit()都使用此属性声明:
extern void exit(int) __attribute__((noreturn));
extern void abort(void) __attribute__((noreturn));
$ cat test1.c
extern void exitnow();
int foo(int n)
{
if ( n > 0 )
{
exitnow();
/* control never reaches this point */
}
else
return 0;
}
$ cc -c -Wall test1.c
test1.c: In function `foo':
test1.c:9: warning: this function may return with or without a value
$ cat test2.c
extern void exitnow() __attribute__((noreturn));
int foo(int n)
{
if ( n > 0 )
exitnow();
else
return 0;
}
$ cc -c -Wall test2.c
no warnings!