【问题标题】:Exception handling in C - catching native C crash from Java JNIC 中的异常处理 - 从 Java JNI 捕获本机 C 崩溃
【发布时间】:2013-12-03 03:36:38
【问题描述】:

我是一名 Java 程序员,正在编写一个调用本机 C 代码的 JNI 应用程序。我想要的是从调用本机代码的Java代码中检测本机程序崩溃。

我知道 C 中没有异常处理,但我只是好奇我是否可以在 Java 的 try-catch 块中“安全地”编写危险代码(例如,运行时可能包含错误指针或文件未找到情况的代码)。在这种情况下,代码会崩溃,但我正在寻找一种安全的方式来报告它。

【问题讨论】:

  • 你可以处理分段错误(SIGSEGV 信号),但是你的程序处于一个糟糕的位置,一旦你得到一个。
  • 谁说C可以处理异常?您只需要自己构建它,而不是依赖预定义的代码。
  • @JonahNelson 为什么必须编写自己的代码?你听说过图书馆吗?
  • @self。是的 程序可能在不好的地方,我只是想安全地报告它。

标签: c exception java-native-interface


【解决方案1】:

我相信你采取了错误的方法。作为一种语言,C 不是一个“请求宽恕比请求许可更好”的地方。必须在 C 中请求许可,尽管在发生灾难时,你当然可以做一些清理工作:

您应该查看 signal handling

当你做类似的事情时

int *a = NULL;
int b = *a;    //segfault

您的程序将收到 SIGSEGV,这将强制您的程序退出,除非您已安装信号处理程序。

大多数未处理的信号会导致程序终止,并且大多数可以被捕获(例如 SIGKILL 不能被捕获)。

这允许您进行一些清理工作。

#include <signal.h>
#include <stdio.h>

//typedef void (*sighandler_t)(int);

void myhandler(int signal){
    printf("oh noes...\n");
}
int main(void)
{
    signal(SIGSEGV, myhandler);
    int *a = NULL;
    int b = *a;    //segfault
    return 0;
}

但不建议这样做。您必须始终了解指针的内容。否则,巨人会夷平你的村庄。

编辑:建议将signal() 函数弃用。新代码应改用[sigaction()][2]。为了简单起见,此示例使用signal()

【讨论】:

  • 你能解释一下typedef void (*sighandler_t)(int);这行是做什么的吗?
  • 这是signal() 作为第二个参数的函数指针的类型定义。它根本不需要在那里。这只是一个提醒,以显示您的处理程序应该是什么样子。我来评论一下
  • 感谢@ldrumm。写“程序将被终止”是指整个程序,即调用本机代码的java程序,对吗?然后也没什么可做的了。但是如果我能从本地程序中捕获并返回,我的java程序将被保存,并且基本上我希望我的java程序在本地代码崩溃的情况下仍然运行。
  • @giga 我不太了解信号如何跨语言绑定工作,因此这很可能会导致一些令人讨厌的意外。我很好奇为什么这个 C 模块这么容易出现段错误?
  • @ldrumm 问题是,c 模块是由我组织的许多人在不同时间编写的,所以我正在努力保护我的 java 程序免受可能的崩溃。
【解决方案2】:

不,您无法合理地检测到来自 Java 的本机崩溃。如果发生这样的崩溃,您的程序很可能在您有机会捕获它之前就被杀死。

您可以做的是检查返回码。如果失败,任何写得体面的 C 库都会返回一个错误代码(注意这与崩溃不同)。如果您愿意,可以使用这些代码并将它们转换为 Java 异常。

对于崩溃,您可以使用“信号”API,尽管它并不像看起来那么简单:如果您真的遇到了崩溃,那么您就无能为力了,因为整个程序内存可能已损坏。如果您是初学者,我建议您不要这样做。

【讨论】:

    【解决方案3】:

    C 并没有真正的例外。它有两种作用相似但又大不相同的东西:信号和错误代码。

    让我们首先考虑错误代码。当 C 中的函数调用失败时,它通常会通过返回错误代码或通过返回占位符值(例如,0、-1 或 NULL)并在 errno 中设置错误来向调用者发出失败信号,或者将其存储在可以通过调用另一个方法来检索的位置。处理这些异常不需要特别的努力。只需检查函数的返回值,例如

    FILE *fh = fopen("example", "r");
    if (fh == NULL) {
        fprintf(stderr, "Couldn't open file: %s\n", strerror(errno));
        return -1;
    }
    // do things with fh...
    

    另一方面,信号在 C 中用于表示真正不寻常的情况,例如尝试执行无效代码或取消引用错误指针,或其他外部条件,例如用户按下 control-C 来终止程序。您可以尝试使用 signal() 函数处理一些此类信号,但您所描述的信号,如错误指针,通常表明进程以相当永久的方式搞砸了,因此允许它们终止进程是通常是正确的解决方案。

    如果您确实在运行容易触发分段错误的代码,明智的做法是在单独的进程中运行它,而不是让它潜在地破坏 Java 运行时的状态。

    【讨论】:

    • 感谢您提出运行单独进程的想法。我想我会用这个而不是 JNI 方法。
    猜你喜欢
    • 2012-08-10
    • 1970-01-01
    • 1970-01-01
    • 2010-12-29
    • 2018-03-19
    • 1970-01-01
    • 1970-01-01
    • 2011-01-04
    相关资源
    最近更新 更多