最近一直在研究Runtime.exec的使用,通过这个来运行脚本命令。
我想知道通过这个方法来调用的脚本命令运行之后的pid是多少,网上找了一遍,发现有个比较靠谱的方法,在这里记录一下。
通过jna调用动态链接库的方式来获取pid,需要在代码里面导入一个jna-4.1.0.jar,对应的maven如下:
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>4.1.0</version>
</dependency>
然后新建一个接口Kernel32,代码如下:
import com.sun.jna.Library;
import com.sun.jna.Native;
public interface Kernel32 extends Library{
public static Kernel32 INSTANCE = (Kernel32) Native.loadLibrary("kernel32", Kernel32.class);
public long GetProcessId(Long hProcess);
}
调用的时候使用,获取的 pid 就是我们想要的pid :
long pid = -1;
Field field = null;
try {
field = process.getClass().getDeclaredField("handle");
field.setAccessible(true);
pid = Kernel32.INSTANCE.GetProcessId((Long) field.get(process));
} catch (Exception ex) {
ex.printStackTrace();
}
上面的程序只适用于 windows 系统,下面我们讲程序优化一下,来适应更多的系统(linux、aix):
long pid = -1;
Field field = null;
if (Platform.isWindows()) {
try {
field = process.getClass().getDeclaredField("handle");
field.setAccessible(true);
pid = Kernel32.INSTANCE.GetProcessId((Long) field.get(process));
} catch (Exception ex) {
ex.printStackTrace();
}
} else if (Platform.isLinux() || Platform.isAIX()) {
try {
Class<?> clazz = Class.forName("java.lang.UNIXProcess");
field = clazz.getDeclaredField("pid");
field.setAccessible(true);
pid = (Integer) field.get(process);
} catch (Throwable e) {
e.printStackTrace();
}
} else {
}
这样的话,可以获取linux程序的pid。
但是这里有个问题,就是如果通过脚本文件运行,即脚本文件写脚本启动命令,我们获取的pid是运行脚本窗口的命令行的pid,而非程序的真实pid,这点需要注意。在这里提供linux系统的另外解决办法。
就是通过优化启动脚本,我仿照了zookeper的启动脚本写了一个:
#!/bin/sh
JAVA_HOME=/home/jdk1.7
JAVA_OPTS=-Xmx512m
BASE_HOME=..
CONFIG_FILE=$BASE_HOME/conf/config.properties
PID_FILE=agent.pid
echo -n "Starting agent ... "
if [ -f "$PID_FILE" ]; then
if kill -0 `cat "$PID_FILE"` > /dev/null 2>&1; then
echo {
mathJaxContainer[0]}PID_FILE"`.
exit 0
fi
fi
nohup $JAVA_HOME/bin/java $JAVA_OPTS -Dlog4j.configuration=file:$BASE_HOME/conf/log4j.properties -cp "$BASE_HOME/conf:$BASE_HOME/lib/*" com.mysite.main.Application -f $CONFIG_FILE > /dev/null 2>&1 &
if [ $? -eq 0 ]
then
if /bin/echo -n $! > "$PID_FILE"
then
sleep 1
pid={
mathJaxContainer[5]}{
PID_FILE}")
if ps -p "${pid}" > /dev/null 2>&1; then
echo STARTED
echo 'PID:'${pid}
else
echo FAILED TO START
exit 1
fi
else
echo FAILED TO WRITE PID
exit 1
fi
else
echo SERVER DID NOT START
exit 1
fi
通过阅读脚本大家可以看出,是在启动程序之后把pid记录到一个文件里面,通过对这个文件解析来获取真实pid,这个要求我们启动命令要后台执行(比如nohup),才能成功获取。这样写脚本还有效避免了重复启动程序的问题。
测试类
import com.sun.jna.Library;
import com.sun.jna.Native;
public interface Kernel32 extends Library {
public static Kernel32 INSTANCE = (Kernel32) Native.loadLibrary("kernel32", Kernel32.class);
public long GetProcessId(Long hProcess);
}
import java.lang.reflect.Field;
public class Test1 {
public static void main(String[] args) throws Exception {
Runtime r = Runtime.getRuntime();
long pid = -1;
try {
Process process = r.exec("D:\\Program Files\\Tencent\\QQ\\QQ.exe");
Field field = process.getClass().getDeclaredField("handle");
field.setAccessible(true);
pid = Kernel32.INSTANCE.GetProcessId((Long) field.get(process));
System.out.println(pid);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}