文章目录
在Java层,我们可以使用try/catch语句来捕获和处理异常。然而,在Android的Native层(C/C++代码),我们并没有内置的异常处理机制。这篇文章将介绍如何在Android Native层实现类似于try/catch的异常处理机制。
一、技术原理
在Native层实现异常处理的关键在于信号处理(Signal Handling)和非局部跳转(Non-Local Jumps)。当程序发生错误(如访问非法内存、除以零等)时,操作系统会向进程发送一个信号。我们可以设置一个信号处理函数(Signal Handler),在收到信号时执行特定的代码。
非局部跳转提供了一种在程序中跳转到另一个位置的方法,而不是按照正常的控制流程执行。在C语言中,我们可以使用setjmp
和longjmp
函数来实现非局部跳转。setjmp
函数保存当前的执行上下文(包括堆栈和寄存器状态等),并返回0。longjmp
函数恢复由setjmp
保存的上下文,并使setjmp
返回一个非零值。我们可以利用这个特性,在信号处理函数中调用longjmp
,跳转到setjmp
所在的位置,实现异常的捕获和处理。
二、代码实现
2.1 定义结构体保存线程的异常处理信息
首先,我们定义一个结构体native_code_handler_struct
,用于保存线程的异常处理信息。这个结构体包括一个sigjmp_buf
类型的变量ctx
,用于保存setjmp
的上下文;一个标志位ctx_is_set
,表示上下文是否已经被设置;以及其他与异常处理相关的信息。
/* Thread-specific crash handler structure. */
typedef struct native_code_handler_struct {
/* Restore point context. */
sigjmp_buf ctx;
int ctx_is_set;
int reenter;
/* Alternate stack. */
char *stack_buffer;
size_t stack_buffer_size;
stack_t stack_old;
/* Signal code and info. */
int code;
siginfo_t si;
ucontext_t uc;
/* Custom assertion failures. */
const char *expression;
const char *file;
int line;
/* Alarm was fired. */
int alarm;
} native_code_handler_struct;
native_code_handler_struct* native_code_handler_g;
static native_code_handler_struct* getCrashHandler() {
return native_code_handler_g;
}
2.2 实现try/catch语义
然后,我们定义了一系列的函数和宏,用于实现try/catch语义。COFFEE_TRY
宏检查当前是否已经在一个try块中(通过inside
函数),如果不在,则设置信号处理函数(通过setupSignalHandler
函数)并保存执行上下文(通过sigsetjmp
函数)。COFFEE_CATCH
宏和COFFEE_END
宏则用于标识catch块和try/catch块的结束。
/** Internal functions & definitions, not to be used directly. **/
#include <setjmp.h>
extern int inside(void);
extern int setupSignalHandler(int);
extern sigjmp_buf* get_ctx(void);
extern void cleanup(void);
#define COFFEE_TRY() \
if (inside() || \
(setupSignalHandler() == 0 \
&& sigsetjmp(*get_ctx(), 1) == 0))
#define COFFEE_CATCH() else
#define COFFEE_END() cleanup()
/** End of internal functions & definitions. **/
2.3 检查当前线程的异常处理信息
inside
函数检查当前线程的异常处理信息,如果已经在一个try块中,则增加reenter
计数并返回1;否则返回0。
* Returns 1 if we are already inside a coffeecatch block, 0 otherwise.
*/
int inside() {
native_code_handler_struct *const t = getCrashHandler();
if (t != NULL && t->reenter > 0) {
t->reenter++;
return 1;
}
return 0;
}
2.4 设置信号处理函数
setupSignalHandler
函数设置信号处理函数,并将reenter
计数加1,表示进入了一个新的try块。
/**
* Calls handler_setup(1) to setup a crash handler, mark the
* context as valid, and return 0 upon success.
*/
int setupSignalHandler(int id) {
if (handler_setup(1, id) == 0) {
native_code_handler_struct *const t = getCrashHandler();
assert(t != NULL);
t->reenter++;
t->ctx_is_set = 1;
LOGD("setup reenter:%d", t->reenter);
return 0;
} else {
return -1;
}
}
handler_setup
设置崩溃处理器,包括全局和线程相关的资源。首先调用handler_setup_global(id)
初始化全局资源,然后为当前线程初始化本地资源。
/**
* Acquire the crash handler for the current thread.
* The handler_cleanup() must be called to release allocated
* resources.
**/
static int handler_setup(int setup_thread, int id) {
int code;
DEBUG(print("setup for a new handler\n"));
/* Initialize globals. */
if (pthread_mutex_lock(&native_code_g.mutex) != 0) {
return -1;
}
code = handler_setup_global(id);
if (pthread_mutex_unlock(&native_code_g.mutex) != 0) {
return -1;
}
/* Global initialization failed. */
if (code != 0) {
return