Sun的JDK里获取当前进程ID的方法(hack)

Java标准库里常见的公有API确实是没有获取当前进程的ID的方法,有时候挺郁闷的,就是需要自己的PID。 
于是有各种workaround,其中有很靠谱的通过JNI调用外部的C/C++扩展,然后调用操作系统提供的相应API去获取PID;也有些不怎么靠谱的hack。这里要介绍的就是后者之一,只在Sun JDK或兼容的JDK上有效的方法。 

代码例子如下: 
Java代码   收藏代码
  1. import java.lang.management.ManagementFactory;  
  2. import java.lang.management.RuntimeMXBean;  
  3.   
  4. public class ShowOwnPID {  
  5.     public static void main(String[] args) throws Exception {  
  6.         int pid = getPid();  
  7.         System.out.println("pid: " + pid);  
  8.         System.in.read(); // block the program so that we can do some probing on it  
  9.     }  
  10.       
  11.     private static int getPid() {  
  12.         RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();  
  13.         String name = runtime.getName(); // format: "pid@hostname"  
  14.         try {  
  15.             return Integer.parseInt(name.substring(0, name.indexOf('@')));  
  16.         } catch (Exception e) {  
  17.             return -1;  
  18.         }  
  19.     }  
  20. }  

使用Sun JDK里RuntimeMXBean.getName()方法的实现,可以很轻松的拿到自身的PID。 
运行这个程序可以看到类似以下的输出: 
Command prompt代码   收藏代码
  1. D:\experiment>java ShowOwnPID  
  2. pid: 11704  

这个时候再跑一个jps来看看当前都有哪些Java进程的话,可以看到: 
Command prompt代码   收藏代码
  1. D:\experiment>jps  
  2. 7888 Jps  
  3. 11704 ShowOwnPID  

嗯……这PID没错。 

这PID是哪儿来的呢?先看RuntimeMXBean实例的来源,java.lang.management.ManagementFactory: 
Java代码   收藏代码
  1. package java.lang.management;  
  2.   
  3. // ...  
  4.   
  5. public class ManagementFactory {  
  6.     // ...  
  7.   
  8.     /** 
  9.      * Returns the managed bean for the runtime system of  
  10.      * the Java virtual machine. 
  11.      * 
  12.      * @return a {@link RuntimeMXBean} object for the Java virtual machine. 
  13.  
  14.      */  
  15.     public static RuntimeMXBean getRuntimeMXBean() {  
  16.         return sun.management.ManagementFactory.getRuntimeMXBean();  
  17.     }  
  18.       
  19.     // ...  
  20. }  

可以看到它依赖了sun.management.ManagementFactory,于是看看对应的实现: 
Java代码   收藏代码
  1. package sun.management;  
  2.   
  3. import java.lang.management.*;  
  4. // ...  
  5. import static java.lang.management.ManagementFactory.*;  
  6.   
  7. public class ManagementFactory {  
  8.     private ManagementFactory() {};  
  9.   
  10.     private static VMManagement jvm;  
  11.       
  12.     private static RuntimeImpl runtimeMBean = null;  
  13.   
  14.     public static synchronized RuntimeMXBean getRuntimeMXBean() {  
  15.         if (runtimeMBean == null) {  
  16.             runtimeMBean = new RuntimeImpl(jvm);  
  17.         }  
  18.         return runtimeMBean;  
  19.     }  
  20.       
  21.     static {  
  22.         AccessController.doPrivileged(new LoadLibraryAction("management"));  
  23.         jvm = new VMManagementImpl();  
  24.     }  
  25.       
  26.     // ...  
  27. }  

这里可以发现实现RuntimeMXBean接口的是sun.management.RuntimeImpl。它的实现是: 
Java代码   收藏代码
  1. package sun.management;  
  2.   
  3. import java.lang.management.RuntimeMXBean;  
  4. // ...  
  5.   
  6. /** 
  7.  * Implementation class for the runtime subsystem. 
  8.  * Standard and committed hotspot-specific metrics if any. 
  9.  * 
  10.  * ManagementFactory.getRuntimeMXBean() returns an instance 
  11.  * of this class. 
  12.  */  
  13. class RuntimeImpl implements RuntimeMXBean {  
  14.     private final VMManagement jvm;  
  15.     private final long vmStartupTime;  
  16.   
  17.     /** 
  18.      * Constructor of RuntimeImpl class. 
  19.      */  
  20.     RuntimeImpl(VMManagement vm) {  
  21.         this.jvm = vm;  
  22.         this.vmStartupTime = jvm.getStartupTime();  
  23.     }  
  24.   
  25.     public String getName() {  
  26.         return jvm.getVmId();  
  27.     }  
  28.       
  29.     // ...  
  30. }  

OK,看到getName()返回的是VMManagement.getVmId()的返回值,再跟过去看: 
Java代码   收藏代码
  1. package sun.management;  
  2.   
  3. import java.net.InetAddress;  
  4. import java.net.UnknownHostException;  
  5. // ...  
  6.   
  7. class VMManagementImpl implements VMManagement {  
  8.     // ...  
  9.       
  10.     public String getVmId() {  
  11.         int pid = getProcessId();  
  12.         String hostname = "localhost";  
  13.         try {  
  14.             hostname = InetAddress.getLocalHost().getHostName();  
  15.         } catch (UnknownHostException e) {  
  16.             // ignore  
  17.         }  
  18.    
  19.         return pid + "@" + hostname;  
  20.     }  
  21.     private native int getProcessId();  
  22.       
  23.     // ...  
  24. }  

OK,这里可以看到getVmId()返回过来的字符串确实是"pid@hostname"形式的。再追下去,看看native一侧是如何获取PID的话: 
j2se/src/share/native/sun/management/VMManagementImpl.c 
C代码   收藏代码
  1. JNIEXPORT jint JNICALL  
  2. Java_sun_management_VMManagementImpl_getProcessId  
  3.   (JNIEnv *env, jobject dummy)  
  4. {  
  5.     jlong pid = jmm_interface->GetLongAttribute(env, NULL,  
  6.                                                 JMM_OS_PROCESS_ID);  
  7.     return (jint) pid;  
  8. }  

于是继续跟, 
hotspot/src/share/vm/services/management.cpp 
C++代码   收藏代码
  1. static jlong get_long_attribute(jmmLongAttribute att) {  
  2.   switch (att) {  
  3.   // ...  
  4.   case JMM_OS_PROCESS_ID:  
  5.     return os::current_process_id();  
  6.     
  7.   // ...  
  8.     
  9.   default:  
  10.     return -1;  
  11.   }  
  12. }  
  13.   
  14. JVM_ENTRY(jlong, jmm_GetLongAttribute(JNIEnv *env, jobject obj, jmmLongAttribute att))  
  15.   if (obj == NULL) {  
  16.     return get_long_attribute(att);  
  17.   } else {  
  18.     // ...  
  19.   }  
  20.   return -1;  
  21. JVM_END  


接下来os::current_process_id()的实现就是每个操作系统不同的了。 
在Linux上是: 
hotspot/src/os/linux/vm/os_linux.cpp 
C++代码   收藏代码
  1. static pid_t _initial_pid = 0;  
  2.   
  3. int os::current_process_id() {  
  4.   
  5.   // Under the old linux thread library, linux gives each thread  
  6.   // its own process id. Because of this each thread will return  
  7.   // a different pid if this method were to return the result  
  8.   // of getpid(2). Linux provides no api that returns the pid  
  9.   // of the launcher thread for the vm. This implementation  
  10.   // returns a unique pid, the pid of the launcher thread  
  11.   // that starts the vm 'process'.  
  12.   
  13.   // Under the NPTL, getpid() returns the same pid as the  
  14.   // launcher thread rather than a unique pid per thread.  
  15.   // Use gettid() if you want the old pre NPTL behaviour.  
  16.   
  17.   // if you are looking for the result of a call to getpid() that  
  18.   // returns a unique pid for the calling thread, then look at the  
  19.   // OSThread::thread_id() method in osThread_linux.hpp file  
  20.   
  21.   return (int)(_initial_pid ? _initial_pid : getpid());  
  22. }  
  23.   
  24. // this is called _before_ the most of global arguments have been parsed  
  25. void os::init(void) {  
  26.   
  27.   // With LinuxThreads the JavaMain thread pid (primordial thread)  
  28.   // is different than the pid of the java launcher thread.  
  29.   // So, on Linux, the launcher thread pid is passed to the VM  
  30.   // via the sun.java.launcher.pid property.  
  31.   // Use this property instead of getpid() if it was correctly passed.  
  32.   // See bug 6351349.  
  33.   pid_t java_launcher_pid = (pid_t) Arguments::sun_java_launcher_pid();  
  34.   
  35.   _initial_pid = (java_launcher_pid > 0) ? java_launcher_pid : getpid();  
  36.   // ...  
  37. }  


在Windows上是: 

C++代码   收藏代码
  1. static int _initial_pid = 0;  
  2.   
  3. int os::current_process_id()  
  4. {  
  5.   return (_initial_pid ? _initial_pid : _getpid());  
  6. }  
  7.   
  8. // this is called _before_ the global arguments have been parsed  
  9. void os::init(void) {  
  10.   _initial_pid = _getpid();  
  11.   // ...  
  12. }  


================================================================= 

好吧其实我是在HotSpot的源码里搜pid然后慢慢找出JMM代码里有调用过os::current_process_id(),然后才一步步向上找到对应的Java API。刚才问毕玄老大有没有见过在Java代码里获取PID的办法,才得知原来 以前有人总结过几种办法,其中第一个就是本文提到的这个。嘛,需求是一直有的,这种功能自然是早该有人捣腾过了。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值