Java 串口通信: 32位迁移至64位解决方案
2025-01-13 16:23:58
Java 串口通信:从32位迁移到64位
问题背景
旧的 Java 应用程序依赖于名为 libLinuxSerialParallel.so
的动态链接库进行串口通信。该库为 32 位版本,导致必须在 Ubuntu 22 等 64 位系统上使用 32 位兼容层。将应用程序迁移至 64 位架构势在必行,目的是利用 64 位平台的性能优势并简化系统维护。迁移的关键在于寻找合适的替代库,并确保其能正常工作。在进行 64 位编译后,出现 UnsatisfiedLinkError
异常,提示“wrong ELF class: ELFCLASS32”,说明尝试加载32位版本的so库。此外还出现了栈保护(stack guard)警告,提示旧库可能存在安全隐患。
问题分析
直接原因在于原有的 libLinuxSerialParallel.so
为 32 位编译的动态链接库,而 64 位 Java 虚拟机(JVM)无法直接加载它。根本原因则在于 32 位和 64 位应用程序的内存模型差异以及编译时所用指令集的不同,这导致库文件在不同的平台之间不能直接互用。另外,由于加载的so库比较老旧,JVM出现栈保护警告,表示需要更新库,增加栈保护机制。
解决方案一:使用librxtx-java
替代
原理
librxtx-java
提供了一种在 Java 中进行串口通信的标准方式,其64位版本可直接与64位JVM配合使用。此库包含了原生代码和Java接口,使得开发者可以直接在Java应用程序中使用标准的串口API。这种替代方案避免了兼容性问题。
步骤
-
安装
librxtx-java
:在终端中使用 apt 安装
librxtx-java
:sudo apt-get install librxtx-java
该命令会自动下载并安装
librxtx-java_2.2.0+dfsg-2_amd64.deb
(或者最新的版本),其中包含必要的64位库文件。 -
修改 Java 代码 :
不再直接调用
libLinuxSerialParallel.so
。需要改写你的代码,使用javax.comm
或者其他的第三方库来进行串口的读写操作。
// javax.comm 的使用范例
import javax.comm.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
public class SerialPortExample {
public static void main(String[] args) {
// 列出可用的串口
Enumeration<?> portList = CommPortIdentifier.getPortIdentifiers();
while (portList.hasMoreElements()) {
CommPortIdentifier portId = (CommPortIdentifier) portList.nextElement();
if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) {
System.out.println("Found serial port: " + portId.getName());
try {
// 打开端口
SerialPort serialPort = (SerialPort) portId.open("SerialExample", 2000);
// 设置串口参数
serialPort.setSerialPortParams(9600,SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
// 读写操作
InputStream inputStream = serialPort.getInputStream();
OutputStream outputStream = serialPort.getOutputStream();
// 读取串口数据
byte[] buffer = new byte[1024];
int len = inputStream.read(buffer);
if(len > 0)
{
String receiveStr = new String(buffer, 0 , len);
System.out.println("Receive data :" + receiveStr );
}
//写入串口数据
String sendStr = "Hello SerialPort!";
outputStream.write(sendStr.getBytes());
//关闭串口
serialPort.close();
}
catch (PortInUseException e){
System.err.println("Port in use :" + e.getMessage());
}
catch(IOException ex)
{
System.err.println("IOException Error: " + ex.getMessage());
}
catch(NoSuchPortException e)
{
System.err.println("NoSuchPort Exception Error:" + e.getMessage());
}
catch(UnsupportedCommOperationException e)
{
System.err.println("Unsupported Comm Exception Error:" + e.getMessage());
}
}
}
}
}
注意: 要编译以上代码,你需要添加 javax.comm
的jar包作为依赖项,具体的jar包文件位置一般在安装librxtx-java的目录,根据实际安装路径决定。另外,也需要引入相关的java.comm 的原生库。
-
测试 :
编译并运行 Java 应用程序,确认其能正常进行串口通信。确保没有
UnsatisfiedLinkError
错误,栈保护警告也已消失。
额外建议
- 确认系统用户拥有访问串口的权限,通常是
dialout
组的成员。你可以使用以下命令将用户加入dialout
组:sudo usermod -a -G dialout $USER
,然后重新登录。 - 如果使用了第三方串口库(如 RXTX ),务必选择支持 64 位的版本。
解决方案二:编译和使用自己构建的 libLinuxSerialParallel.so
库
原理
若没有 librxtx-java
或其他合适替代方案,也可尝试自行编译 libLinuxSerialParallel.so
库,前提是拥有源码。 需要用64位编译器针对目标架构构建新的库,确保其能够与 64 位 JVM 兼容。
步骤
-
获取源码:
确保你有libLinuxSerialParallel.so
对应的源代码, 否则,请不要轻易尝试。 -
配置编译环境:
安装64位开发工具链,确保 GCC 等相关编译工具正常工作。
sudo apt-get update sudo apt-get install build-essential gcc g++
-
配置编译参数:
在Makefile
或者编译脚本中加入正确的编译参数,指定生成 64 位的共享库:CFLAGS += -m64 -fPIC
在configure文件中也要指定编译参数,确保编译生成的是64位库。
或者也可以直接在命令行进行编译,命令需要添加 -fPIC, -m64 选项
g++ -shared -fPIC -m64 your_source_files.c -o libLinuxSerialParallel.so
其中
your_source_files.c
是你原先的库源码文件。注意,可能需要添加一些其他的库作为依赖项,例如 -lstdc++. 根据实际情况来选择合适的库 -
替换旧库:
编译完成后,使用新生成的 64 位
libLinuxSerialParallel.so
替换原先的 32 位版本, 并放在 java 程序的library 加载路径中。 -
执行修复堆栈:
如果出现
execstack
的问题,可执行以下命令修复新生成的库:
sudo execstack -c <新库文件路径>
-
测试 :
运行应用程序,确认已成功加载64位的
libLinuxSerialParallel.so
库,同时程序可以正常运行,没有UnsatisfiedLinkError
,以及stack guard
的警告信息。
额外建议
- 从源代码编译库需要仔细的测试和调试,务必确保库的功能和兼容性没有问题,另外需要确认对应的license是否符合使用要求。
- 对于安全方面的顾虑,建议优先考虑使用成熟稳定的第三方库。
结论
将 Java 串口通信程序从 32 位迁移到 64 位架构是必要的一步,其可通过使用 64 位版本的串口通信库实现,或者通过自己编译构建动态链接库来实现。根据情况选用适当的方案即可有效解决该问题,提升系统运行效率和安全性。