Java通过JNA调用so文件,实现对硬件的操作

5 篇文章 0 订阅
3 篇文章 0 订阅

由于工作需要,最近被安排对公司新入手的设备进行性能测试,硬件厂商了提供了测试的C语言代码、硬件驱动和调用驱动所用的so文件。由于大学期间学了java后,打算不再使用C,导致对C的知识全部送还给老师了。而且大学期间从未接触过Linux,导致对C在linux运行完全不通。

经百度发现java可以使用JNI或者JNA调用C代码实现对硬件的调用。刚开始研究JNI发现需要编译C语言封装一下原接口才可以使用。因为完全忘记,果断放弃。然后发现除了JNI还有个叫JNA的相对于Java程序员更加方便。开始着手研究使用JNA的jar包实现调用。

但是调用过程中我产生了一个疑问:硬件厂家提供的文档中的方法参数中包含指针,且方法运行后通过调用参数指针获取到对应的值,但是java并无指针概念,由于从未使用Jna,所以认为需要传入特定指针类型,百度了大量方法,结果发现调用JNA和正常数据类型,再调用发现数据居然变了~感觉有点怪怪的但也没深究。

然后找了同部门的C语言同事进行讨论,他打包票的说,不论传啥执行正确,方法执行完再获取会被out的参数,值一定会变的,将信将疑的试了下,居然真的返回了~

不扯闲话了,先说说如何调用方法:

用户给予的硬件接口列举俩个:

int open(void **dev, int *chan, int *virt) 
int send(void *dev, int chan, int virt, const void *buf, size_t count) 
int save(void *dev, int chan, int virt, void *buf, size_t *count)

通过Git上JNA的测试源码得知:

C数据类型java数据类型
void **PointerByReference
int *IntByReference
void *Pointer
intint

C代码内的const void*类型在C内意义为不可改变的一段空间的指针,但经过实践发现,传入byte[] 数组即可使用,你按照void*的对应类型填上Pointer后调用方法C语言反而无法识别。个人认为void *对应的Java类型因实际使用的so文件而变化,在乎所谓的网上的新手规范的对照表,完全就是进入死路。

size_t单独使用时可以当做int使用,但是size_t*却无法像int*那样使用IntByReference来定义。我所用硬件涉及通信,就因为size_t*使用IntByReference会导致硬件厂家处理数据时前四byte数据被抹掉为0,因此我们选择像JNI那样封装一下接口,将size_t*的赋值由C代码去完成赋值再去调用厂家的方法。

最终实际上JNA的Java代码所写接口如下:

int Open(PointerByReference dev,IntByReference chan,IntByReference virt);
int Send(Pointer dev,int chan,int virt,byte[] buf,int count);
int Save(Pointer dev,int chan,int virt,byte[] buf,IntByReference count);

在Save方法中传入的count,在运行代码中并无用到,count数据全由C语言写入,写该参数是为了获取返回的数据长度。

然后以open方法的实现举例:

//声明引用的so文件
INSTANTCE = (接口名) Native.loadLibrary("so文件名,不需要包含前半部分lib和后半部分.so", 接口名.class);
PointerByReference dev = new PointerByReference();
IntByReference virt = new IntByReference(0);
IntByReference chan = new IntByReference(0);
int opensuccess = INSTANTCE.Open(dev, chan, virt);
//此列举方法除了int返回值,dev、chan、virt都会out出来,执行结束后直接调用对应数据就会得到返回。
int channum = chan.getValue();
int virtnum = virt.getValue();

然后谈谈我不擅长的同事写的C语言封装,封装代码我就不展示了,也就是个左手接右手的活,只是在size_t那,并没有把“左手”数据完全送给“右手”,而是“左手”(Java)调用该方法时,未获取size_t*数据类型所接受的数据,而是自己赋值了默认值然后去调用硬件厂商的代码。

且该衔接代码在Linux系统完成后需要进行编译为so文件,且放入执行java时所能引入的位置,需要的指令:

gcc -shared -I ./ -fPIC c语言代码名称.c -l指令名称(编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.so来确定库的名称) -o 编译成的so文件名称(一般前面需要加lib).so

cp 生成so文件名.so /usr/lib

先编译C代码生成so文件,并将so文件复制到指定位置,为java引用。

系统默认引用位置也可能为“/usr/lib64/”。

注:生成的so文件必须是lib开头,除非使用绝对路径定义或使用java代码进行引用。

我将提供下2020-11-10时候Git开发人员提供的测试源码和JNA4.2.1、5.6.0的jar的下载(虽然4.2.1可能有点老导致size_t*失败的,但是时间紧急也就没空替换试试了,欢迎喜欢研究的人去试试)

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值