我从事在 Wind River Linux 上运行的嵌入式系统的工作。
它是 Java 和 C++ 的混合体,带有一些用于技术之间通信的 JNI。
我们构建了自定义错误处理,以便在出现任何意外错误时生成回溯和其他信息来帮助我们确定问题。
此错误处理始终由 C++ 组件完成,所有其他组件都必须向该组件注册(以便可以安装适当的信号处理程序)。
因此,对于 Java 组件,我们使用 JNI 与 C++ 错误处理程序进行通信。
我们的测试程序使用 35 种不同的场景来测试所有各种类型的错误(内存不足、未处理的异常、堆栈溢出访问冲突等)。这是针对单个主线程情况和后台线程完成的。
除了 JNI 主线程和后台线程中导致的 Stackoverflow 之外,所有测试均正常工作。
在 Linux 上,Stackoverflow 应生成 SIGSEGV 并且应调用已安装的 sigaction。但我们只是简单地终止,即处理程序不会被调用。
如果我们不生成 stackoverflow,而是直接引发 SIGSEGV(信号 11),我们的信号处理程序就会正确调用。
请注意,我们还在 Oracle (Java) 提供的 libjsig.so 上执行了 LD_PRELOAD,这可能是使用 JNI 时正确安装自定义信号处理程序所必需的(如果不这样做,其他测试用例将失败)。
奇怪的是,如果我在没有 LD_PRELOAD 的情况下运行测试,在这种情况下确实会调用信号处理程序。
寻找有关如何调试或解决此问题的想法
最佳答案
当我必须编写 JNI 代码来处理 SIGSEGV
等时 - 我的代码必须在异常终止时清理某些文件状态 - 我发现更容易只需手动链接 SIGABRT
处理程序,根本不使用 libjsig.so
。 JVM 似乎总是因 SIGABRT
异常终止 - 我会导致致命的 SIGSEGV
,JVM 会处理该错误并将其转换为 SIGABRT
。我做了什么似乎并不重要。
我现在在Oracle文档中找不到它,但是IBM documents JVM signal handling thus :
Errors
The JVM raises a
SIGABRT
if it detects a condition from which it cannot recover.
我的代码的一个版本(为了消除滚动条而缩写):
typedef void ( *sigaction_handler_t )( int, siginfo_t *, void * );
static sigaction_handler_t original_sigabort_handler = NULL;
static void handler( int sig, siginfo_t *info, void *arg )
{
switch ( sig )
{
case SIGABRT:
//do stuff - stack trace, setrlimit() to generate core file, etc.
if ( NULL != original_sigabort_handler )
{
original_sigabort_handler( sig, info, arg );
}
break;
default:
break;
}
}
__attribute(( constructor )) void library_init_code( void )
{
struct sigaction new_act, old_act;
memset( &new_act, 0, sizeof( new_act );
memset( &old_act, 0, sizeof( old_act );
sigemptyset( &( new_act.sa_mask ) );
new_act.sa_sigaction = handler;
new_act.sa_flags = SA_SIGINFO;
sigaction( SIGABRT, &new_act, &old_act );
if ( ( old_act.sa_sigaction != ( sigaction_handler_t ) SIG_IGN ) &&
( old_act.sa_sigaction != ( sigaction_handler_t ) SIG_DFL ) )
{
original_sigabort_handler = old_act.sa_sigaction;
}
}
关于java - 使用 Java/JNI 时 Linux 上的信号处理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46647659/