深度探秘assert---(已放弃,核心已转储)
前言
前边一直再看内核的slab缓存,真是累如狗,但是它的体系是在是太复杂了,看来需要进一步总结,在这个过程中经常看到assert 断言这个东西,所以今天轻松一下,看看这个的底层实现。
__start
我使用的是glibc-2.18 还是挺新的,首先看一个例子,如果之前接触过C 的同学应该知道assert 名叫“断言”就是判断一个表达式的真伪。若果是错的就报出错误信息,中断程序。如果是真的就继续执行。
[c]
#include<stdio.h>
#include<assert.h>
int main(){
int a = 0;
assert(a != 0);
printf("hello world\n");
}
[/c]
程序很简单,看结果。
果然,“已放弃,(核心已转储)”。
所以今天就来看看assert 如何实现 “已放弃(核心已转储)” 。
[c]
#ifdef NDEBUG /*一个控制BUG 开关的宏*/
#define assert(expr) (__ASSERT_VOID_CAST (0)) /*如果关闭了强制转换成 “0”,这个宏的使用就是一个强转为0*/
[/c]
[c]
# define assert(expr) \ /*判断表达式的真假*/
((expr) \
? __ASSERT_VOID_CAST (0) \
: __assert_fail (__STRING(expr), __FILE__, __LINE__, __ASSERT_FUNCTION))
/*重点在这里如果在这里判断条件为假,就失败然后调转__assert_fail 函数,这里是一个函数实现*/
[/c]
__assert_fail 函数
[c]
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);
/*将assertion 修改成格式化的状态*/
}
hidden_def(__assert_fail)
[/c]
__assert_fail_base( ) 函数
[c]
参数说明 @fmt :格式化
@assertion :错误信息
@file : 文件名
@line : 错误行数
@function :错误函数
void
__assert_fail_base (const char *fmt, const char *assertion, const char *file,unsigned int line, const char *function)
{
char *str;
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 (__builtin_expect (buf != MAP_FAILED, 1)) /*空间申请成功*/
{
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);
/*应用程序有可能没有收到SIGABRT 信号,所以我们以必须识放空间。
SIGABRT是中止一个程序,它可以被捕捉,但不能被阻塞。处理函数返回后,所有打开的文件描述符将会被关闭,流也会被flush。程序会结束,有可能的话还会core dump。 当程序调用abort(3)时,该进程会向自己发送SIGABRT信号。所以,SIGABRT一般用于信号中一些关键的处理,assert失败时也会使用它。你不应该去捕捉SIGSEGV和SIGABRT信号,如果收到这种信号,说明进程处于一个不确定的状态,很可能会直接挂起。 */
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 (); /*thread结束*/
}
[/c]