JNA回调在对本机库的特定内部方法调用中间歇性地使应用程序崩溃

JNA Callback crashing the application intermittently on specific internal method call to native library(JNA回调在对本机库的特定内部方法调用中间歇性地使应用程序崩溃)

本文介绍了JNA回调在对本机库的特定内部方法调用中间歇性地使应用程序崩溃的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的应用程序中,我使用JNA来使用用C编写的本机代码,在回调中我从本机应用程序获得通知。

在回调中,我得到一个指针和一些其他要处理的数据。在JNA回调代码中,我必须再次使用该指针来调用其他本机库代码,并且必须传递该指针。之后,我必须从该回调返回。

如果我不从回调调用中间本机库方法(传递指针),它工作得很好,但如果我添加这个调用,我的应用程序会间歇性地崩溃(主要是在处理了数百个回调请求之后,有时它也成功地运行了数千个回调)。 在本机代码中为其设置挂钩的NotificationHook类对象是一个静态变量,因为应用程序将只有一个挂钩。和本地库逐一调用。

public interface INotificationHook extends Callback {

  public int NotificationHook(TRANX htrans, NOTIFICATION.ByReference notification);

}


public class NotificationHook implements INotificationHook {

    @Override
    public int NotificationHook(final TRANX tranx, final NOTIFICATION.ByReference notification) {
       System.out.println("Enter Java Callback");
       // notification contains actual data to process
       library.SendSrvcResponse(tranx); // if I put this method call, application crashes intermittently

       System.out.println("Exit Java Callback");
       return 0;
    }

}

Tranx本地结构:

typedef struct tagTRANX { int unused; } *TRANX;

TRANX.java

import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.Structure.FieldOrder;

@FieldOrder({"unused"})
public class TRANX extends Structure {

  public static class ByReference extends TRANX implements Structure.ByReference {
  }

  public static class ByValue extends TRANX implements Structure.ByValue {
  }

  public int unused = 0;

  public TRANX () {
    super();
  }

  public TRANX(final int unused) {
    super();
    this.unused = unused;
  }

  public TRANX(final Pointer peer) {
    super(peer);
  }
}

JNA中的库定义(Java):

int SendSrvcResponse(TRANX tranx);

该指针实际上是一个结构指针,我尝试创建一个结构并将该指针替换为该指针,即使在这种情况下应用程序也会崩溃。

当我添加很少的进出日志时,观察结果如下:

printf("Enter to JNA from c library for callback");
hs = (*piHook->hookProc) (
tranx, notification); 
printf("Callback completed from JNA and returned to c library");

中间调用c库:

SendSrvcResponse(TRANX tranx) {
    printf("Enter to send response in c library");
    // do some operation
    printf("Enter to send response in c library");
}

每次成功执行时,这是我从控制台(集合JNA和C库)的打印日志中获得的信息:

  1. 从c库进入JNA//C库调用JNA回调
  2. 输入Java回调//Java回调调用Enter发送
  3. c库中的响应//调用本机库中的方法发送
  4. 在c库中回车以发送响应//本机库中的方法在发送响应后退出
  5. 退出Java回调//Java 回调完成其操作
  6. JNA回调完成后返回到c库//,将流程返回到c库 代码
在应用程序崩溃的情况下,打印日志中缺少最后一条语句。只有JNA将方法流返回给C,但C不会接收它。

其中通知是指针。

崩溃日志:

#
# A fatal error has been detected by the Java Runtime Environment:
#
#  SIGSEGV (0xb) at pc=0x0000ffff6c78f610, pid=6396, tid=0x0000ffff6d9f0ac8
#
# JRE version: OpenJDK Runtime Environment (8.0_272-b10) (build 1.8.0_272-b10)
# Java VM: OpenJDK 64-Bit Server VM (25.272-b10 mixed mode linux-aarch64 compressed oops)
# Derivative: IcedTea 3.17.0
# Distribution: Custom build (Fri Dec 11 01:04:16 UTC 2020)
# Problematic frame:
# C  [jna791751318727750086.tmp+0xa610]  Java_com_sun_jna_Native_setByte+0x50
#
# Core dump written. Default location: **************
#
# If you would like to submit a bug report, please include
# instructions on how to reproduce the bug and visit:
#   https://icedtea.classpath.org/bugzilla
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
#

---------------  T H R E A D  ---------------

Current thread (0x0000ffff6c7b7000):  JavaThread "Thread-5356" [_thread_in_native, id=6642, stack(0x0000ffff6d9d0000,0x0000ffff6d9f0ac8)]

siginfo: si_signo: 11 (SIGSEGV), si_code: 1 (SEGV_MAPERR), si_addr: 0x0000ffff6c2ec018

Registers:
R0=0x0000000000000000
R1=0x0000000000000000
R2=0x0000ffff6c2ec018
R3=0x0000000000000000
R4=0x0000000000000000
R5=0x0000000000000000
R6=0x0000ffff66b49458
R7=0x0000ffff66b49458
R8=0x0000ffff6c78f5c0
R9=0x0000ffff6c7b72c8
R10=0x0000000020002230
R11=0x00000000f212dde8
R12=0x0000ffff691637f0
R13=0x0000000020002230
R14=0x00000000f212e4e0
R15=0x00000000f213acd0
R16=0x00000001006c7770
R17=0x0000000000000000
R18=0x0000000000000001
R19=0x0000ffff6c7aa250
R20=0x0000000000000000
R21=0x00000000f212e2b0
R22=0x0000000100781ed8
R23=0x00000000f212fd60
R24=0x00000000f213a868
R25=0x00000001000016d0
R26=0x00000000f213a880
R27=0x0000000000000000
R28=0x0000ffff6c7b7000
R29=0x0000ffff6d9efcd0
R30=0x0000ffff78fea394

Top of Stack: (sp=0x0000ffff6d9efcd0)
0x0000ffff6d9efcd0:   0000ffff6d9efd60 0000ffff78fea394
0x0000ffff6d9efce0:   00000000200f03db 0000000000000000
0x0000ffff6d9efcf0:   0000ffff6c2ec018 0000000000000000
0x0000ffff6d9efd00:   0000ffff6c7b7250 00aebfc8f212dfd8
0x0000ffff6d9efd10:   00000000f212dde8 00000000f21343b0
0x0000ffff6d9efd20:   00000000f21342f0 00000000f2134350
0x0000ffff6d9efd30:   00000000f2134368 00000000f2134380
0x0000ffff6d9efd40:   00000000f2134320 00000000f2134398
0x0000ffff6d9efd50:   00000000fca7ebc8 0000ffff77adb000
0x0000ffff6d9efd60:   0000ffff6d9f0220 0000ffff7988acb4
0x0000ffff6d9efd70:   00000000f21343c8 00000000f213a978
0x0000ffff6d9efd80:   0000ffff6d9f0220 0000ffff79952fbc
0x0000ffff6d9efd90:   00000000f213aa28 00000000f213aa68
0x0000ffff6d9efda0:   0000000000000000 00000000f213aae8
0x0000ffff6d9efdb0:   0000ffff6d9efdd0 0000ffff80d78678
0x0000ffff6d9efdc0:   0000000000000000 00000001000115f0
0x0000ffff6d9efdd0:   00000000fc391290 00000000f212e4e0
0x0000ffff6d9efde0:   00000000f212dfd8 00000000fc3a3960
0x0000ffff6d9efdf0:   00000000200eada6 0000000000000000
0x0000ffff6d9efe00:   00000000f21342e0 0000ffff6c7b7000
0x0000ffff6d9efe10:   00000000fc43c670 00000000d024ce28
0x0000ffff6d9efe20:   0000ffff6d9f0220 0000ffff799275fc
0x0000ffff6d9efe30:   00000000f212e4e0 00000000f212fb00
0x0000ffff6d9efe40:   00000000d0d14760 00000000d0d14760
0x0000ffff6d9efe50:   00000000f2131bc0 f212f75000000000
0x0000ffff6d9efe60:   0000ffff6d9f0220 0000ffff78eccdd8
0x0000ffff6d9efe70:   00000000f2131c20 00000000f212e398
0x0000ffff6d9efe80:   0000ffff6d9f0220 0000ffff79463d2c
0x0000ffff6d9efe90:   200f03db00000000 0000000100781ed8
0x0000ffff6d9efea0:   00000000f213acb0 00000000f21342e0
0x0000ffff6d9efeb0:   00000000f212e398 0000000000000000
0x0000ffff6d9efec0:   00000000f212e4e0 0000000000000000 

Instructions: (pc=0x0000ffff6c78f610)
0x0000ffff6c78f5f0:   e1 ff 00 91 1f 00 01 eb a3 02 00 54 e2 0f 42 a9
0x0000ffff6c78f600:   c0 00 00 f0 00 40 09 91 e1 ff 40 39 00 20 42 b9
0x0000ffff6c78f610:   41 68 23 38 e0 00 00 34 e0 1b 40 f9 22 00 00 b0
0x0000ffff6c78f620:   21 00 00 b0 42 80 30 91 21 e0 30 91 45 e5 ff 97 

Register to memory mapping:

R0=0x0000000000000000
R1=0x0000000000000000
R2=0x0000ffff6c2ec018
R3=0x0000000000000000
R4=0x0000000000000000
R5=0x0000000000000000
R6=0x0000ffff66b49458
R7=0x0000ffff66b49458
R8=0x0000ffff6c78f5c0
R9=0x0000ffff6c7b72c8
R10=0x0000000020002230
R11=0x00000000f212dde8
R12=0x0000ffff691637f0
R13=0x0000000020002230
R14=0x00000000f212e4e0
R15=0x00000000f213acd0
R16=0x00000001006c7770
R17=0x0000000000000000
R18=0x0000000000000001
R19=0x0000ffff6c7aa250
R20=0x0000000000000000
R21=0x00000000f212e2b0
R22=0x0000000100781ed8
R23=0x00000000f212fd60
R24=0x00000000f213a868
R25=0x00000001000016d0
R26=0x00000000f213a880
R27=0x0000000000000000
R28=0x0000ffff6c7b7000
R29=0x0000ffff6d9efcd0
R30=0x0000ffff78fea394


Stack: [0x0000ffff6d9d0000,0x0000ffff6d9f0ac8],  sp=0x0000ffff6d9efcd0,  free space=127k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
C  [jna791751318727750086.tmp+0xa610]  Java_com_sun_jna_Native_setByte+0x50
J 5174  com.sun.jna.Native.setByte(Lcom/sun/jna/Pointer;JJB)V (0 bytes) @ 0x0000ffff78fea394 [0x0000ffff78fea300+0x94]
V  [libjvm.so+0x40f678]
C  0x00000000f212e4e0

Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
J 5174  com.sun.jna.Native.setByte(Lcom/sun/jna/Pointer;JJB)V (0 bytes) @ 0x0000ffff78fea398 [0x0000ffff78fea300+0x98]
J 9695 C2 com.sun.jna.Pointer.setByte(JB)V (11 bytes) @ 0x0000ffff7988acb4 [0x0000ffff7988ac80+0x34]
J 9266 C2 com.sun.jna.Pointer.setValue(JLjava/lang/Object;Ljava/lang/Class;)V (607 bytes) @ 0x0000ffff79952fbc [0x0000ffff79951ec0+0x10fc]
J 9230 C2 com.sun.jna.Structure.writeField(Lcom/sun/jna/Structure$StructField;)V (427 bytes) @ 0x0000ffff799275fc [0x0000ffff79927500+0xfc]
J 9423 C2 com.sun.jna.Structure.write()V (126 bytes) @ 0x0000ffff79463d2c [0x0000ffff79463840+0x4ec]
J 9434 C2 com.sun.jna.Structure.autoWrite()V (45 bytes) @ 0x0000ffff7904e274 [0x0000ffff7904e240+0x34]
J 9905 C1 com.sun.jna.CallbackReference$DefaultCallbackProxy.invokeCallback([Ljava/lang/Object;)Ljava/lang/Object; (238 bytes) @ 0x0000ffff78ee17c4 [0x0000ffff78ede580+0x3244]
J 9904 C1 com.sun.jna.CallbackReference$DefaultCallbackProxy.callback([Ljava/lang/Object;)Ljava/lang/Object; (22 bytes) @ 0x0000ffff78fa18b0 [0x0000ffff78fa1800+0xb0]
v  ~StubRoutines::call_stub

我尝试过的内容:

  1. 在JNA回调输入中用指针替换Tranx。
  2. 在Java回调方法的最后一行对结构调用clear()
  3. 在JNA中注释完整的处理回调代码,只保留对SendSrvcResponse方法的调用并返回回调。

所有这些都会导致崩溃,没有一个是有帮助的。

一个奇怪的观察是,当在本机C代码中实现此回调时,应用程序不会中断。只有在与JNA集成时才会中断。

有人能帮我了解一下这一事件的可能原因吗?或者我怎样才能进一步调查它?

推荐答案

根据您提供的代码,问题与这里的其他回调相关问题相同:由于JAVA的垃圾回收,您正在丢失TRANX的本机分配。

JNAStructure由两部分组成:(指向数据的)指针和数据本身。您尚未为TRANX提供本机typlef来确认您的JNA映射,但实例化的对象将有一个内部指针引用,指向4字节的内存分配(int unused)。

您只显示回调代码,其中TRANX已经是一个参数,这意味着您已经将其实例化以传递给回调。

如果您使用new TRANX()new TRANX(int unused)自己分配它,则JNA已

  • 分配了4字节的本机内存
  • 在内部存储指向它的指针
在JNA中,附加到Structure的本机内存作为垃圾收集过程的一部分被自动释放。这是回调的常见问题,因为您通常不控制回调返回的时间,因此会出现以下顺序:

  • 在Java中创建对象(分配TRANX结构在内部跟踪指针所指向的本机4字节)
  • TRANX对象传递给回调
  • 传递对象后,Java立即不再需要自己的对象;它是不可访问的,因此有资格进行垃圾回收
  • 发生GC时,本机4字节作为进程的一部分被释放
  • 回调中的TRANX对象在内部仍有指针,但它现在指向不再分配的内存,从而导致SIGSEGV(或无效内存访问错误,或由另一个线程分配内存时出现奇怪症状,或其他未定义的行为)。

该问题的解决方案是跟踪与TRANX关联的内存。

  • 如果您自己分配它,请保留对TRANX对象的引用,以防止它无法到达。
    • 这通常需要在您确定回调将被处理之后访问TRANX结构
    • 在JDK9+中,ReachabilityFence可用于此。
    • 在JDK8中,您应该以某种方式操作类(例如,从类中读取值,或对其调用toString等)。
  • 如果您使用本机分配并使用从本机API返回的peer值创建指针,则读取API以确定何时释放该内存。

这篇关于JNA回调在对本机库的特定内部方法调用中间歇性地使应用程序崩溃的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本文标题为:JNA回调在对本机库的特定内部方法调用中间歇性地使应用程序崩溃