JPDA 架构研究4 - JDWP的传输器

引入:架构

上一篇文章主要分析JDWP层传输的数据包的格式,这篇文章咱们主要关注于JDWP层是如何传输的。jvm


分析:socket

传输的具体实现是根据各个JVM本身实现,可是他们有个共同点就是都是用C/C++实现的,而且都实现了jdwpTransport.h (和上篇同样的这个文件)。实现根据咱们的知识能够得知是以dll文件(或者Unix平台下是so文件)的形式存在。由于咱们从前面了解到,JDWP层有Agent负责在数据包和JVMTI的函数调用之间转换,因此瓜熟蒂落知道,传输层的DLL文件也一定会有一个onload方法,而且让Agent启动时候调用的。ide



分析1:Agent如何访问VM环境呢?函数

答案是经过环境指针(environment pointer), 该指针会在onload()方法中返回给Agent.这个环境指针的定义以下:测试

struct _jdwpTransportEnv;

#ifdef __cplusplus
typedef _jdwpTransportEnv jdwpTransportEnv;

...
struct _jdwpTransportEnv {
    const struct jdwpTransportNativeInterface_ *functions;

因此这里能够看出,环境指针本质就是拿到一组能够访问目标VM环境的native接口。spa


分析2:Agent启动时调用的onload()方法。指针

当Target VM加载了Agent以后,JDWP会根据参数去加载具体的JDWP的实现,Sun 的 JDK 在 Windows 提供 socket 和 share memory 两种传输方式,而在 Linux 上只有 socket 方式)。传输层实现的动态连接库实现必须暴露 jdwpTransport_OnLoad 接口,来对传输层初始化。blog

该方法签名以下:接口

JNIEXPORT jint JNICALL 
jdwpTransport_OnLoad(JavaVM *jvm,
                     jdwpTransportCallback *callback,
                     jint version,
                     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的链接的确被创建。

    /*  3 : Attach */
    jdwpTransportError (JNICALL *Attach)(jdwpTransportEnv* env,
        const char* address,
        jlong attach_timeout,
        jlong handshake_timeout);

从这里能够看出,除了环境指针外,它须要下面3个参数:

address: Debugger的地址和端口

attach_timeout:设置链接超时值,单位毫秒。若是设为0则说明永不超时。

handshake_timeout:设置握手超时值,单位毫秒。若是设为0则说明永不超时。


b.StartListening.它主要用于让传输器处于listen模式,这样它就能够监听来自Debugger的链接了。

  /*  4: StartListening */
    jdwpTransportError (JNICALL *StartListening)(jdwpTransportEnv* env,
        const char* address,
        char** actual_address);

除环境指针外,它还须要1个参数:

address:Debugger的地址和端口

actualAddress:返回值,返回传输器从address参数得到的真实字符串形式的地址。


c.StopListening.它主要用于让传输器离开listen模式,这样它就再也不监听来自Debugger的链接了。

    /*  5: StopListening */
    jdwpTransportError (JNICALL *StopListening)(jdwpTransportEnv* env);


d.Accept.它主要用于创建来自Debugger的链接。

 /*  6: Accept */
    jdwpTransportError (JNICALL *Accept)(jdwpTransportEnv* env,
        jlong accept_timeout,
        jlong handshake_timeout);


e.IsOpen.它用于测试Debugger的链接是否开着。

   /*  7: IsOpen */
    jboolean (JNICALL *IsOpen)(jdwpTransportEnv* env);


f.Close.它用于关闭到Debugger的链接。

   /*  8: Close */
    jdwpTransportError (JNICALL *Close)(jdwpTransportEnv* env);



分类2:用于读来自Debugger的数据包和发送到Debugger的数据包。

a. ReadPacket. 它用于在链接开着的状态,从Debugger读取数据包。

  /*  9: ReadPacket */
    jdwpTransportError (JNICALL *ReadPacket)(jdwpTransportEnv* env,
        jdwpPacket *pkt);

须要注意的是,该方法只对数据包作长度校验,而不作完整性校验。


b.WritePacket.它用于在链接开着的状态,往Debugger写数据包。

  /*  10: Write Packet */
    jdwpTransportError (JNICALL *WritePacket)(jdwpTransportEnv* env,
        const jdwpPacket* pkt);


分类3:辅助功能。

a.GetLastError.它用于返回用字符串表示的上次错误。

    /*  11:  GetLastError */
    jdwpTransportError (JNICALL *GetLastError)(jdwpTransportEnv* env,
        char** error);


b.GetCapabilities.它用于返回JDWP传输器全部支持的能力。

 /*  2 : Get Capabilities */
    jdwpTransportError (JNICALL *GetCapabilities)(jdwpTransportEnv* env,
         JDWPTransportCapabilities *capabilities_ptr);

能力的启用和禁用是经过一组位图位来标示的。


整个传输器的架构以下:

wKioL1SJKfygLz4kAAExNZHnNuU512.jpg