JPDA 架构研究4 - JDWP的传输器
引入: 上一篇文章主要分析JDWP层传输的数据包的格式,这篇文章我们主要关注于JDWP层是如何传输的。 分析: 传输的具体实现是根据各个JVM自己实现,但是他们有个共同点就是都是用C/C++实现的,并且都实现了jdwpTransport.h (和上篇一样的这个文件)。实现根据我们的知识可以得知是以dll文件(或者Unix平台下是so文件)的形式存在。因为我们从前面了解到,JDWP层有Agent负责在数据包和JVMTI的函数调用之间转换,所以顺理成章知道,传输层的DLL文件也必定会有一个onload方法,并且让Agent启动时候调用的。 分析1:Agent如何访问VM环境呢? 答案是通过环境指针(environment pointer), 该指针会在onload()方法中返回给Agent.这个环境指针的定义如下: 1 2 3 4 5 6 7 8 struct _jdwpTransportEnv; #ifdef__cplusplus typedef _jdwpTransportEnvjdwpTransportEnv; ... struct _jdwpTransportEnv{ const struct jdwpTransportNativeInterface_*functions; 所以这里可以看出,环境指针本质就是拿到一组可以访问目标VM环境的native接口。 分析2:Agent启动时调用的onload()方法。 当Target VM加载了Agent之后,JDWP会根据参数去加载具体的JDWP的实现,Sun 的 JDK 在 Windows 提供 socket 和 share memory 两种传输方式,而在 Linux 上只有 socket 方式)。传输层实现的动态链接库实现必须暴露 jdwpTransport_OnLoad 接口,来对传输层初始化。 该方法签名如下: 1 2 3 4 5 JNIEXPORTjintJNICALL jdwpTransport_OnLoad(JavaVM*jvm, jdwpTransportCallback*callback, jintversion, jdwpTransportEnv**env); 从这里可以看出_OnLoad方法需要下面几个入参: jvm: 它让Agent通过GetJavaVM方法来获取JVM信息。 callback:它是一个函数表的指针,传输层用它来进行内存的分配与释放。 version:它让Agent获得期望的JDWPTRANSPORT的版本。 然后返回值就是在第四个参数中,它就是我们想要的环境指针。 如果传输层初始化成功,那么_OnLoad方法就会返回JNI_OK,否则会返回对应的错误码。 分析3:jdwpTransport支持的方法概览。 因为jdwpTransport需要维系着Debugger和Target VM之间的关系,所以它有许多方法。我们从几大类来看。 分类1:用于管理连接 管理连接的方法其主要作用是用于建立和关闭到Debugger的连接。 a. Attach.它主要用于关联到Debugger,建立到Debugger之间的可信链路. 步骤1:连接到指定的地址 步骤2:连接成功,则通过交换 ”JDWP-Handshake"来确保到Debugger的连接的确被建立。 1 2 3 4 5 /*3:Attach*/ jdwpTransportError(JNICALL*Attach)(jdwpTransportEnv*env, const char *address, jlongattach_timeout, jlonghandshake_timeout); 从这里可以看出,除了环境指针外,它需要下面3个参数: address: Debugger的地址和端口 attach_timeout:设置连接超时值,单位毫秒。如果设为0则说明永不超时。 handshake_timeout:设置握手超时值,单位毫秒。如果设为0则说明永不超时。 b.StartListening.它主要用于让传输器处于listen模式,这样它就可以监听来自Debugger的连接了。 1 2 3 4 /*4:StartListening*/ jdwpTransportError(JNICALL*StartListening)(jdwpTransportEnv*env, const char *address, char **actual_address); 除环境指针外,它还需要1个参数: address:Debugger的地址和端口 actualAddress:返回值,返回传输器从address参数获得的真实字符串形式的地址。 c.StopListening.它主要用于让传输器离开listen模式,这样它就不再监听来自Debugger的连接了。 1 2 /*5:StopListening*/ jdwpTransportError(JNICALL*StopListening)(jdwpTransportEnv*env); d.Accept.它主要用于建立来自Debugger的连接。 1 2 3 4 /*6:Accept*/ jdwpTransportError(JNICALL*Accept)(jdwpTransportEnv*env, jlongaccept_timeout, jlonghandshake_timeout); e.IsOpen.它用于测试Debugger的连接是否开着。 1 2 /*7:IsOpen*/ jboolean(JNICALL*IsOpen)(jdwpTransportEnv*env); f.Close.它用于关闭到Debugger的连接。 1 2 /*8:Close*/ jdwpTransportError(JNICALL*Close)(jdwpTransportEnv*env); 分类2:用于读来自Debugger的数据包和发送到Debugger的数据包。 a. ReadPacket. 它用于在连接开着的状态,从Debugger读取数据包。 1 2 3 /*9:ReadPacket*/ jdwpTransportError(JNICALL*ReadPacket)(jdwpTransportEnv*env, jdwpPacket*pkt); 需要注意的是,该方法只对数据包做长度校验,而不做完整性校验。 b.WritePacket.它用于在连接开着的状态,往Debugger写数据包。 1 2 3 /*10:WritePacket*/ jdwpTransportError(JNICALL*WritePacket)(jdwpTransportEnv*env, const jdwpPacket*pkt); 分类3:辅助功能。 a.GetLastError.它用于返回用字符串表示的上次错误。 1 2 3 /*11:GetLastError*/ jdwpTransportError(JNICALL*GetLastError)(jdwpTransportEnv*env, char **error); b.GetCapabilities.它用于返回JDWP传输器所有支持的能力。 1 2 3 /*2:GetCapabilities*/ jdwpTransportError(JNICALL*GetCapabilities)(jdwpTransportEnv*env, JDWPTransportCapabilities*capabilities_ptr); 能力的启用和禁用是通过一组位图位来标示的。 整个传输器的架构如下: 本文转自 charles_wang888 51CTO博客,原文链接:http://blog.51cto.com/supercharles888/1587625,如需转载请自行联系原作者