最近项目需要从linux系统获取硬盘序列号,特把成果分享一下。至于什么是jni这里就不介绍了需要的童鞋可以google百度一把。这里包括两部分代码,一部分是java的代码供上层调用,一部分是c代码编译成so动态链接库供java代码,下面从c代码开始。
一 代码
c代码
getLocalSysInfo.cpp
- #include "getLocalSysInfo.h"
- #include <stdio.h>
- #include <sys/ioctl.h>
- #include <linux/hdreg.h>
- #include <sys/fcntl.h>
- JNIEXPORT void JNICALL Java_getSysInfo_getLocalHdSn(JNIEnv * env, jobject arg)
- {
- struct hd_driveid id;
- /*打开设备文件*/
- int fd = open("/dev/sda", O_RDONLY);
- if (fd < 0)
- {
- printf("oh can not open sda/n");
- return ;
- }
- /*读取设备信息*/
- if(!ioctl(fd, HDIO_GET_IDENTITY, &id))
- {
- /*打印磁盘序列号信息*/
- printf("Serial Number=%s\n",id.serial_no);
- }
- return;
- }
头文件
getLocalSysInfo.h
这个头文件是在执行
javac getSysInfo.java
后生成getSysInfo.class文件后再执行
javah -classpath . -jni getSysInfo
后生成getSysInfo.h头文件,我手痒生成后把它改为getLocalSysInfo.h的(^0^)。
- /* DO NOT EDIT THIS FILE - it is machine generated */
- #include <jni.h>
- /* Header for class HelloWorld */
- #ifndef _Included_HelloWorld
- #define _Included_HelloWorld
- #ifdef __cplusplus
- extern "C" {
- #endif
- /*
- * Class: getSysInfo
- * Method: getLocalHdSn
- * Signature: ()V
- */
- JNIEXPORT void JNICALL Java_getSysInfo_getLocalHdSn
- (JNIEnv *, jobject);
- #ifdef __cplusplus
- }
- #endif
- #endif
在c代码中打开硬盘设备文件/dev/sda( 注意每个系统可能不一样请根据自己的系统修改路径),然后调用ioctrl读取设备信息获取硬盘序列号并打印出来。接下来就是编译了我写了个小小的makefile文件:
- CC=g++
- JDK_INCLUDE=/usr/lib/jvm/jdk1.7.0/include
- INCLUDE=-I $(JDK_INCLUDE) -I $(JDK_INCLUDE)/linux
- FLAG=-fPIC -shared
- OBJ=libgetLocalSysInfo.so
- all:getLocalSysInfo.cpp getLocalSysInfo.h
- $(CC) $(FLAG) -cpp getLocalSysInfo.cpp -o $(OBJ) $(INCLUDE)
- clean:
- rm -rf *.o *.so getLocalSysInfo
编译器我用的是g++,我jdk的路径是/usr/lib/jvm/jdk1.7.0/include,这个必须包含否则会提示找不到jni.h这个文件。编译标致选择-fPIC和-shared这两个的含义如下:
-fPIC
生成的目标文件可在不固定内存位置执行,就是可动态链接的意思了。
-shared
生成的目标文件可以被共享即可以多进程调用,如不加该选项会提示要你添加main函数(呵呵你懂的)。
java代码
getSysInfo.java
- public class getSysInfo {
- /*java本地方法申明*/
- public native void getLocalHdSn( );
- public getSysInfo() {
- }
- /*装入动态链接库getHdSn.so*/
- static {
- System.loadLibrary("getLocalSysInfo");
- }
- }
- System.loadLibrary("getLocalSysInfo");
加载动态链接库libgetLocalSysInfo.so。编译java命令:
getHdSn.java
- public class getHdSn{
- /**
- * @param args
- */
- public static void main(String[] args) {
- getSysInfo sysInfo = new getSysInfo();
- sysInfo.getLocalHdSn( );
- }
- }
调用或者硬盘序列号方法。
编译和运行
编译和运行命令如下:
方法二
想通过java写一个读取安装在linux系统上的硬盘的序列号的util类。我写的代码是:
Java代码
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
/**
*
* Description: 获取各个操作系统下硬盘序列号
*
*
* @version 1.0 Aug 25, 2008 12:12:36 PM
*
*/
public class HDUtil {
/**
* Return Opertaion System Name;
*
* @return os name.
*/
public static String getOsName() {
String os = "";
os = System.getProperty("os.name");
return os;
}
/**
* Returns the HD SerialNo. of the computer.
*
* @return the HD SerialNo.
*/
public static String getHDSerialNo() {
String sn = "";
String os = getOsName();
if (os.startsWith("Linux")) {
if (isSCSIorIDEHD() == "scsi") {
// 注意如果是ubuntu等系统用户,本身没有root权限,请先:chmod 777 /dev/sda
String command = "hdparm -i /dev/sda";
Process p;
try {
p = Runtime.getRuntime().exec(command);
BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
while ((line = br.readLine()) != null) {
if (line.contains("SerialNo")) {
int index = line.indexOf("SerialNo") + "SerialNo".length() + 1;
sn = line.substring(index);
break;
}
}
br.close();
} catch (IOException e) {
}
} else if (isSCSIorIDEHD() == "ide") {
// 注意如果是ubuntu等系统用户,本身没有root权限,请先:chmod 777 /dev/sda
String command = "hdparm -i /dev/hda";
Process p;
try {
p = Runtime.getRuntime().exec(command);
BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
while ((line = br.readLine()) != null) {
if (line.contains("SerialNo")) {
int index = line.indexOf("SerialNo") + "SerialNo".length() + 1;
sn = line.substring(index);
break;
}
}
br.close();
} catch (IOException e) {
}
} else {
sn = "unknown";
}
}
sn = sn.trim();
return sn;
}
public static String isSCSIorIDEHD() {
String os = getOsName();
if (os.startsWith("Linux")) {
// ubuntu系统下确定有root权限
String command = "fdisk -l";
Process p;
try {
p = Runtime.getRuntime().exec(command);
BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
while ((line = br.readLine()) != null) {
if (line.contains("sd")) {
return "scsi";
}
if (line.contains("hd")) {
return "ide";
}
}
br.close();
} catch (IOException e) {
}
}
return "unkonwn"; // 未知类型
}
/**
* Main Class.
*
* @param args
*/
public static void main(String[] args) {
System.out.println("Operation System=" + getOsName());
System.out.println("HD SerialNo=" + getHDSerialNo());
}
}
现在在本地机器ubuntu上可以运行,但是放到其他比如Centos或suse上获取不了。
根本原因是通过java调用的一个SHELL>"hdparm -i /dev/sda1" ,这个命令对于SCSI 的硬盘好像支持不了。
有谁做过的给个建议阿。好多人说用c写个底层的,然后通过jni调用。那样不是系统得有c的运行环境吗?
我觉得一个util类有这个必要吗?