在Linux环境下使用gfortran编译器生成fortran语言的.so共享对象文件 并使用JNA调用 带参方法
Posted 新来的大狮
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在Linux环境下使用gfortran编译器生成fortran语言的.so共享对象文件 并使用JNA调用 带参方法相关的知识,希望对你有一定的参考价值。
在Linux环境下使用gfortran编译器生成fortran语言的.so共享对象文件 并使用JNA调用 带参方法
1 致谢和参考文章
1.1 致谢
1.2 参考文章
2 环境介绍
Java开发环境 IDEA-2018.3
Fortran开发环境 VS2019-community + XE2020
Linux环境 VMware 15 中的 centos 7
3 整体思路
① 准备好linux环境,并安装gfortran。
② 编写好 待测试/使用的fortran源文件。
③ 将fortran源文件 在linux系统中 使用gfortran 编译成.so共享库文件,并放置于某个绝对路径下。
④ 使用JNA编写java调用.so文件的测试程序。
4 具体步骤
4.1 环境准备
准备一台虚拟机,并安装Centos7操作系统。
gfortran安装 (centos环境,以root身份运行,终端输入命令无需sudo)
安装命令: yum install gcc-gfortran
测试是否安装成功:gfortran -v
4.2 编写测试的fotran源文件
测试源文件为 Source2.f90 (封装了一个简单的加法函数)
integer function add_so(first, second)
implicit none
integer first,second
add_so = first + second
return
end function
4.3 在linux环境中编译fortran源文件
将源文件Source2.f90编译成 add.so的共享对象文件
编译命令:gfortran -shared -fPIC -o add.so Source2.f90
编译成功后 输出的add.so文件
查看并验证add.so中暴露的端口是否有源文件的add_so方法。
注意,暴露的方法默认会给源文件中定义的方法增加一个下划线,即:方法名变成 add_so_
查看命令: nm -D add.so
4.4 将生成的.so放置于linux的某个绝对路径下,为JNA调用时的路径提供依据
测试存放路径为 /root/user/add.so
4.5 编写JNA调用.so测试程序 - 部分主要代码
使用的JNA的Maven仓库依赖
<!--JNA相关依赖-->
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>4.2.2</version>
</dependency>
为将调用的.so文件编写接口文件SoTest.java
注意必须使用抽象父类ByReference,使用实现子类会发生错误
注意.so文件暴露的接口方法后 默认要增加一个下划线,即:add_so_
import com.sun.jna.Library;
import com.sun.jna.ptr.ByReference;
public interface SoTest extends Library
// 注意必须使用抽象父类ByReference,默认情况下,fortran使用引用参数
// 使用IntByReference实现类等会发生错误
int add_so_(ByReference a, ByReference b);
// int add_so_(int a, int b);
测试.so文件的程序代码(注意匹配.so文件的路径)
测试时若页面返回 996 则表示调用成功
/**
* linux环境测试调用简单的.so文件
* @return
*/
@RequestMapping(value = "/soTest")
public String soTest()
// String libName = "mytest";
String libName = "/root/user/add.so";
SoTest soTest = (SoTest) Native.loadLibrary(libName, SoTest.class);
// 带参数的test_so
IntByReference a = new IntByReference(995);
IntByReference b = new IntByReference(1);
int result = soTest.add_so_(a, b);
System.out.println("操作完成");
return "result = " + result;
4.6 执行测试程序
测试程序本身处于一个Springboot的 web 项目中,结果将以页面形式返回。
返回结果
最终输出996, 使用 JNA 调用带参数的 fortran语言的.so文件 成功。
5 注意事项和坑
在为fortran调用的函数而编写的 java接口文件中,即SoTest.java中 【详见4.5】
5.1 传入参数未使用引用类型(报错)
针对带有参数的方法,必须使用引用类型。否则会发生致命错误 导致 Failed to write core dump
演示的错误代码 - 传入参数未使用引用类型
public interface SoTest extends Library
// 注意必须使用抽象父类ByReference,默认情况下,fortran使用引用参数
// 使用IntByReference实现类等会发生错误
// int add_so_(ByReference a, ByReference b);
int add_so_(int a, int b);
具体错误如下
#
# A fatal error has been detected by the Java Runtime Environment:
#
# SIGSEGV (0xb) at pc=0x00007f3943dfe6a5, pid=3947, tid=0x00007f3968fa9700
#
# JRE version: Java(TM) SE Runtime Environment (8.0_144-b01) (build 1.8.0_144-b01)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.144-b01 mixed mode linux-amd64 compressed oops)
# Problematic frame:
# C [add.so+0x6a5] add_so_+0x10
#
# Failed to write core dump. Core dumps have been disabled. To enable core dumping,
try "ulimit -c unlimited" before starting Java again
#
# An error report file with more information is saved as:
# /root/user/******/hs_err_pid3947.log
#
# If you would like to submit a bug report, please visit:
# http://bugreport.java.com/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
#
已放弃(吐核)
5.2 方法返回值使用引用类型, 并用实现类对其进行 向下强制转型(报错)
针对为fortran文件编写的java接口中,暴露的方法返回类型 需要使用 基本类型接收 (相对于引用类型而言) , 否则会发生抽象引用类型无法实例化,缺少一个无参构造函数的错误。
演示的错误代码 - 方法返回值使用引用类型,并用实现类对其进行 向下强制转型
接口定义 SoTest.java
public interface SoTest extends Library
// 注意必须使用抽象父类ByReference,默认情况下,fortran使用引用参数
// 使用IntByReference实现类等会发生错误
// int add_so_(ByReference a, ByReference b);
// int add_so_(int a, int b);
ByReference add_so_(ByReference a, ByReference b);
测试程序的接收, 用实现类对抽象引用类型强制向下转型发生错误
/**
* linux环境测试调用简单的.so文件
* @return
*/
@RequestMapping(value = "/soTest")
public String soTest()
// String libName = "mytest";
String libName = "/root/user/add.so";
SoTest soTest = (SoTest) Native.loadLibrary(libName, SoTest.class);
// 带参数的test_so
IntByReference a = new IntByReference(995);
IntByReference b = new IntByReference(1);
System.out.println("a = " + a.getValue());
System.out.println("b = " + b.getValue());
// int result = soTest.add_so_(a, b);
// int result = soTest.add_so_(995, 1);
int result = ((IntByReference)soTest.add_so_(a,b)).getValue();
System.out.println("操作完成");
return "result = " + result;
具体错误 - 部分
ERROR 4173 --- [nio-9010-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] :
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw
exception [Request processing failed; nested exception is
java.lang.IllegalArgumentException: Can't create an instance of class
com.sun.jna.ptr.ByReference, requires a no-arg constructor:
java.lang.InstantiationException: com.sun.jna.ptr.ByReference] with root cause
java.lang.IllegalArgumentException: Can't create an instance of class
com.sun.jna.ptr.ByReference, requires a no-arg constructor:
java.lang.InstantiationException: com.sun.jna.ptr.ByReference
at com.sun.jna.NativeMappedConverter.defaultValue(NativeMappedConverter.java:55) ~[jna-4.2.2.jar!/:4.2.2 (b0)]
at com.sun.jna.NativeMappedConverter.<init>(NativeMappedConverter.java:44) ~[jna-4.2.2.jar!/:4.2.2 (b0)]
at com.sun.jna.NativeMappedConverter.getInstance(NativeMappedConverter.java:32) ~[jna-4.2.2.jar!/:4.2.2 (b0)]
at com.sun.jna.Function.invoke(Function.java:312) ~[jna-4.2.2.jar!/:4.2.2 (b0)]
at com.sun.jna.Library$Handler.invoke(Library.java:236) ~[jna-4.2.2.jar!/:4.2.2 (b0)]
at com.sun.proxy.$Proxy56.add_so_(Unknown Source) ~[na:na]
....等等信息
以上是关于在Linux环境下使用gfortran编译器生成fortran语言的.so共享对象文件 并使用JNA调用 带参方法的主要内容,如果未能解决你的问题,请参考以下文章
在Linux环境下使用gfortran编译器生成fortran语言的.so共享对象文件 并使用JNA调用 带参方法
在Linux环境下,使用ifortran编译 带有结构的和.inc头文件 的fortran项目生成.so文件