IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> 网络协议,Java虚拟机和PMEM使用学习 -> 正文阅读

[网络协议]网络协议,Java虚拟机和PMEM使用学习

网络协议

协议三要素

  • 语法,即这一段内容要符合一定的规则和格式。例如括号要成对,结束要使用分号等。

  • 语义,即这一段内容要代表某种意义。例如数字减去数字是有意义的,数字减去文本一般来说就 没有意义。

  • 顺序,即先干啥,后干啥。例如可以先加上某个数值,然后再减去某个数值。

一个网址请求的流程:

  • 浏览器输入一个网址后,网址是一个URL,需要DNS或者HTTPDNS去查找,得到这个网址对应的IP地址,也即要访问的目的地址。得到IP地址后,浏览器打包请求,对于普通的浏览请求,一般都会直接采用HTTP协议,而对于一些需要进行数据加密传输的请求,则需要使用HTTPS协议。DNS、HTTP和HTTPS三者都处于应用层。

  • 应用层封装完成以后,浏览器会将包交给下一层即传输层,通过Socket编程来实现。传输层包括无连接的UDP协议和有连接的TCP协议两种协议类型。其中有连接的TCP协议能够通过确认应答和超时重传等方式来保证数据可靠传输。TCP协议中有两个端口,一个是浏览器的监听端口,一个是服务器的监听端口,操作系统会通过端口来判断得到的包应该由谁处理。

  • 传输层封装完毕后,会向下交付把包交给操作系统的网络层。网络层有IP协议,IP协议中有源和目的IP地址,用于源和目的之间的传输标识。

  • 将数据包继续向下交付给MAC层,可以根据ARP协议得到MAC地址。网关也可以根据路由表和目的IP判断路径。路由协议,常用的有OSPFBGP

  • 之后在物理链路上进行传输。

  • 到达目的服务器后,目的服务器发现MAC地址对的上,取下MAC头,发送给网络层,网络层发现IP头对的上,取下IP头,发送给传输层。如果是TCP协议在目的服务器收到之后会回复报文确认应答,保证可靠传输。

网络分层

一般复杂的程序都要分层,这是程序设计的要求

只要是在网络上跑的包,都是完整的。可以有下层没上层,绝对不可能有上层没下层。对TCP协议来说,无论是三次握手还是重试,只要想发出去包,就要有IP层和MAC层,不然是发不出去的。本质上来说, TCP 没发送一个消息, IP 层和 MAC 层的所有机制都要运行一遍。

分层设备举例,比如一个HTTP协议的包经过一个二层设备,二层设备收进去的是整个网络包,其中 HTTP、TCP、 IP、 MAC 都有。二层设备就是只把 MAC 头摘下来,三层设备就是把 MAC 头摘下来以后,再把 IP 头摘下来。

ipconfig 命令

IP地址是一个网卡在网络世界的通讯地址,相当于我们现实世界的门牌号码。IP 地址分 A, B ,C 三类,主要分为两部分,前一部分是网络号,后一部分是主机号。

无类型域间选路(CIDR)

CIDR 打破了原来设计的几类地址的做法,将32位的IP地址一分为二,前面是网络号,后面是主机号。IP地址中间有一个 “ / ” ,斜杠后面有一个数字,比如10.256.256.1/24,就表示在32位的IP地址中,前21位是网络号,后面8位是主机号,这种地址表示形式的就是 CIDR 。

子网掩码,也是一段类似IP地址的数值,将子网掩码IP 地址两者按位计算 AND,就可得到网络号

loopback,简称 lo,又称环回接口,往往会被分配到 127.0.0.1 这个地址。这个地址用于本机通信,经 过内核处理后直接返回,不会在任何网络中出现。

MAC地址

MAC地址,是网卡的物理地址,用十六进制,6个 byte 表示。MAC 地址全局唯一,不会有两个网卡有相同的MAC地址,而且网卡自生产出来,就带着这个地址。

MAC地址的唯一性设计的目的是为了组网的时候,不同的网卡放在一个网络里时,不用担心冲突,从硬件角度来说,保证不同的网卡有不同的标识。

  • 为什么不直接用MAC地址去通信?

    一个网络包要从一个地方传到另一个地方,除了要有确定的地址,还需要有定位功能。MAC地址就类似于一个门牌号或者身份证,是一个唯一的表示,定位范围非常有限,可以用 IP 地址作为一种定位手段去寻找。MAC地址的通信范围比较小,局限在一个子网里面。

ipconfig 其他显示信息

<BROADCAST, MULTICAST, UP, LOWER_UP>, 称 net_device flags,网络设备的状态标识。UP 表示网卡处于启动的状态;BROADCAST 表示这个网卡有广播地址,可以发送广播包;MULTICAST 表示网卡可以发送多播包;LOWER_UP 表示 L1 是启动的,也即网线插着

qdisc pffo_fast,qdisc 全称 queueing discipline(排队规则)。内核如果需要通过某个网络接口发送数据包,都需按照此接口配置的 qdisc 把数据包加入队列。最简单的 qdisc 是 pffo,不对进入的数据包做任何的处理,数据包采用先入先出的方式通过队列。pffo_fast 的队列包括三个波段,在每个波段中使用先进先出规则。三个波段优先级也不相同。band 0的优先级最高,band 2的最低。如果band 0里面有数据包,系统就不会处理band 1里面的数据包,band 1和band 2之间也是一样。数据包是按照服务类型(Type of Service,TOS)被分配到三个波段里。TOS 是 IP 头里面的一个字段,代表了当前的包是高优先级的,还是低优先级的。

DHCP与PXE

在 Linux 中,如果源地址与目的地址是一个跨网段的调用,便不会直接将包发送到网络上,而是尝试将包发送到网关。

动态主机配置协议(DHCP)

如果是数据中心里面的服务器,IP一旦配置好,基本不会变,而DHCP的方式就相当于都是自动配置好的,不用的时候直接删除即可。

DHCP 的工作方式

  1. 当一台机器新加入一个网络时,只知道自己的MAC地址,其会首先发布一个广播包,告知自己的MAC地址,并寻求获租一个IP地址,这一步称为 DHCP Discover

  2. 如果网络管理员在网络中配置了 DHCP Server,即相当于这些IP的管理员,其能够立即接收到新加入的机器的信息。当一台机器带着自己的 MAC 地址加入一个网络的时候,MAC是它唯一的身份,如果连这个都重复了,就没办法配置了。只有MAC唯一,IP管理员才能知道这是一个新机器,需要租给它一个IP地址,这个过程称为 DHCP Offer。同时,DHCP Server为此机器保留为它提供的IP地址,从而不会为其他DHCP客户分配此IP地址。

  3. DHCP Server 仍然使用广播地址作为目的地址,新加入的机器可以根据广播信息中的 Mac 地址得到 IP 地址。如果有多个DHCP Server,这台新机器会收到多个IP地址,新机器会选择其中一个DHCP Offer,一般是最先到达的那个,并且会向网络发送一个 DHCP Request 广播数据包,包中包含客户端的MAC地址、接受的租约中的IP地址、提供此租约的DHCP服务器地址等,并告诉所有DHCP Serve 其将接受哪一台服务器提供的IP地址,并告诉其他DHCP服务器,请求撤销它们提供的IP地址,以便提供给下一个IP租用请求者。此时,由于还没有得到 DHCP Server的最后确认,客户端仍使用 0.0.0.0 为源IP地址、255.255.255.255为目标地址进行广播。在BOOTP里面,接受某个DHCP Server的分配的IP。

  4. 当 DHCP Server接收到客户机的 DHCP request之后,会广播返回给客户机一个DHCP ACK消息包,表明已经接受客户机的选择,并将这一IP地址的合法租用信息和其他的配置信息都放入该广播包,发给客户机

  5. 最终租约达成的时,还需要再广播一下

IP地址的收回和续租

客户机会在租期过去 50% 的时候,直接向为其提供IP地址的DHCP Server发送 DHCP request消息包 。客户机接收到该服务器回应的 DHCP ACK消息包 ,会根据包中所提供的新的租期以及其他已经更新的TCP/IP参数,更新自己的配置,即完成 IP 租用更新。

预启动执行环境(PXE)

安装操作系统的过程,只能在BIOS启动之后。因为没安装系统之前,连启动扇区都没有。因而这个过程叫做预启动执行环境(Pre-boot Execution Environment),简称PXE

PXE 协议分为客户端和服务器端,由于还没有操作系统,只能先把客户端放在BIOS里面。当计算机启动时,BIOS 把 PXE客户端调入内存里面,就可以连接到服务端做一些操作。在使用PXE时,通过配置DHCP Server的 next-server,指向 PXE 服务器的地址和初始启动文件,PXE客户端启动后,发送DHCP请求之后,除了能得到一个 IP 地址,还可以知道 PXE 服务器在哪里,也可以知道如何从PXE服务器上下载某个文件,去初始化操作系统。

解析PXE的工作过程

  1. 启动PXE客户端。第一步是通过DHCP协议告诉DHCP Server,DHCP Server便租给它一个IP地址,同时也给它PXE服务器的地址、启动文件pxelinux.0。

  2. PXE客户端知道要去PXE服务器下载这个文件后,就可以初始化机器。便开始下载,下载使用 TFTP协议。因此PXE服务器上,还需要一个TFTP服务器。PXE客户端向TFTP服务器请求下载这个文件

  3. PXE客户端收到这个文件后,就开始执行这个文件。这个文件会指示PXE客户端,向TFTP服务器请求计算机的配置信息pxelinux.cfg。TFTP服务器会给PXE客户端一个配置文件,里面会说内核在哪、initramfs在哪,PXE客户端会请求这些文件。

  4. 启动Linux内核。

物理层和MAC层

物理层(第一层)

物理层主要物理链路上的传输,比如用一根网线直接连两台电脑,即构成一个局域网LAN。集线器Hub则可以将多台物理设备连接起来,与交换机不同的是,其没有大脑,完全工作在物理层,会将自己收到的每一个字节都复制到其他端口上。

数据链路层(第二层)

数据链路层(MAC层)主要控制在往媒体上发送数据时,往哪发,谁先发,谁后发的问题,防止产生混乱。主要包括

  1. 解决往哪发,主要用到链路层地址,也称MAC地址

    • 第二层网络包格式最开始就是目标的MAC地址和源的MAC地址

    • 之后是类型,大部分类型都是IP数据包

    • 之后是要传输的封装数据

    • 最后是CRC,即循环冗余检测,通过XOR异或算法,来计算整个包是否在发送过程中出现错误

    img

  2. 多路访问,主要包括信道划分,轮流协议,随机接入协议等

已知IP地址,想要求其MAC地址,需要用到ARP地址解析协议

  • 具体过程就是,源发送一个广播包,询问该IP地址的MAC地址,该机器收到后回复其IP地址。为了避免每次都用ARP请求,机器本地也会进行ARP缓存。当然机器会不断地上线下线,IP也可能会变,所以ARP的MAC地址缓存过一段时间就会过期。

交换机与VLAN

一般来说,对于一个网络来说,一台交换机不够用,需要把多台交换机连接起来,形成一个拓扑结构,交换机之间也会广播接收到的ARP 请求,进行消息的传递。

但当交换机数目越来越多,整个拓扑结构复杂之后,很容易出现环路问题,即两个交换机的端口可能连接到同样的局域网,两个交换机之间会相互广播,来回接收并修改记录的信息。并且一个包会转来转去,每台机器都会发广播包,交换机转发也会复制广播包,当广播包越来越多的时候,会最终导致堵塞。

STP协议

生成树的算法成为STP算法,利用STP协议来设置交换机

img

STP协议概念

  • Root Bridge根交换机。是某棵树的根节点,最大的。

  • Designated Bridges指定交换机。对于树来说,就是一棵树的树枝。其他交换机通过这个交换机到达根交换机,也就相当于拜他做了大哥。是树枝,不是叶子,叶子往往是主机。

  • Bridge Protocol Data Units (BPDU)网桥协议数据单元。“相互比较实力”的协议。两个交换机相连的时需要互相比一下。BPDU只有根节点能发,已经隶属于某个根的交换机只能传达根的指示。

  • Priority Vector优先级向量。参考文章将其比喻为实力 (值越小越牛)。即一组ID数目,[Root Bridge ID, Root Path Cost, Bridge ID, and Port ID]。先看Root Bridge ID,一样的话就是一个根;再比Root Path Cost,也即距离根的距离;最后比Bridge ID,比自己的ID。

STP工作过程

  • 首先根据网络管理员的设置不同的优先级,各个交换机会发送BPDU,根据优先级来决定谁是根节点

  • 之后确定的根节点继续发送BPRU,其他节点只有在收到根节点发送的BPRU之后,才会转发一下

  • 其余节点进行归类合并,一般有四种情形:

    • 一个已经成为部分节点的根节点的一个交换机与另一交换机PK失败,会带领所有子节点成为胜者的子节点

    • 根节点与其子节点相遇,会直接将子节点直属

    • 根节点与其他根节点的子节点相遇,如果PK胜,会将该节点变成自己的子节点

    • 不同根节点的子节点相遇,会根据结果确定去哪一个根节点

解决广播和安全问题

两种方法,一种是物理隔离,另一种是虚拟隔离,即 VLAN 也称虚拟局域网

使用VLAN,一个交换机上会连属于多个局域网的机器,交换机怎么区分哪个机器属于哪个局域网?

img

只需要在原来的二层的头上加一个TAG,里面有一个VLAN ID,一共12位。

ICMP与ping

ping基于 ICMP 工作,ICMP全称Internet Control Message Protocol,互联网控制报文协议。

ICMP报文是封装在 IP 包里,格式如下所示:

img

ICMP报文有很多的类型,不同的类型有不同的代码。最常用的类型是主动请求为8,主动请求的应答为0

查询报文类型

ping 就是查询报文,是一种主动请求,并且获得主动应答的ICMP协议。ping发的包也符合ICMP协议格式,但在后面增加了自己的格式。

对ping的主动请求,进行网络抓包,称为ICMP ECHO REQUEST。同理主动请求的回复,称为ICMP ECHO REPLY。比起原生的ICMP,多了两个字段,一个是标识符,另一个是序号

在选项数据中,ping还会存放发送请求的时间值,来计算往返时间,说明路程的长短。

差错报文类型

  • 终点不可达;具体的原因表示:

    • 网络不可达代码为0

    • 主机不可达代码为1

    • 协议不可达代码为2

    • 端口不可达代码为3

    • 需要进行分片但设置了不分片位代码为4。

  • 源站抑制,即让源站繁忙发送速度

  • 时间超时,超过网络包的生存时间还是没到

  • 路由重定向,让下次发给另一个路由器

ping查询报文类型的使用

img

发送和接收过程如下:

  • ping 命令执行的时候,源主机首先会构建一个ICMP请求数据包,ICMP数据包内包含多个字段。最重要的包括类型字段和顺序号

  • 然后,由 ICMP 协议将这个数据包连同目的地址一起交给IP层。IP 层将目的地址和源地址以及其他信息,构建一个 IP 数据包

  • 通过 ARP 映射等得到MAC地址,在数据链路层构建一个包含源和目的MAC地址的MAC数据帧

  • 目的主机收到数据帧后,会检查目的MAC地址,并与本机MAC地址比较,扶额和就接受,否则丢弃。接收后提取IP数据包,IP 层检查后将有用信息提取交给 ICMP 协议

  • 目的主机会构建一个 ICMP 应答包,应答数据包的类型字段为 0,顺序号为接收到的请求数据包中的顺序号,然后再发送出去给源发送主机

  • 在规定的时候间内,源主机如果没有接到 ICMP 的应答包,则说明目标主机不可达;如果接收到了 ICMP 应答包,则说明目标主机可达。源主机会检查,用当前时刻减去该数据包最初从源主机上发出的时刻,就是 ICMP 数据包的时间延迟。

Traceroute:差错报文类型的使用

Traceroute 的其中一个作用就是故意设置特殊的 TTL,来追踪去往目的地时沿途经过的路由器。

Traceroute 的参数指向某个目的 IP 地址,它会发送一个UDP的数据包。将TTL设置成1,一旦遇到一个路由器,就表示出错了,然后会返回一个ICMP包,即网络差错包,类型是时间超时。之后将 TTL 设置为2,则到第二个路由器就出错。如此反复,直到到达目的主机,Traceroute 即获取所有路由器IP。

  • 判断UDP有没有到达目的主机?

    Traceroute 发送一份UDP数据报给目的主机,但会选择一个不可能的值作为UDP端口号(大于30000)。当该数据报到达时,将使目的主机 UDP 模块产生一份 端口不可达的错误ICMP报文。如果数据报没有到达,则可能是超时。

Traceroute 另一个作用是故意设置不分片,从而确定路径的MTU。

网关

在接入网络时,配置类IP地址和网关之后,就能指定目标地址进行访问。在解析到IP地址时,无论是TCP还是UDP协议,都需要分析判断源IP地址和目标IP地址。判断两者是都在同一个网段中。

  • 如果在同一个网段,直接将源地址和目标地址放入IP头中,然后通过ARP获得MAC地址,将源MAC和目的MAC放入MAC头中,直接发出去

  • 如果不是同一网段,就需要发往默认网关Gateway。Gateway的地址一定是和源IP地址是一个网段的。往往不是第一个,就是第二个。

网关往往是一个路由器,是一个三层转发的设备。就是把MAC头和IP头都取下来,然后根据里面的内容,看看接下来把包往哪里转发的设备。

直接将网关称作路由器并不完全准确,假设路由器有五个网卡或者网口,其就相当于有五个不同局域网的网关.

静态路由

静态路由,其实就是在路由器上,配置一条一条规则。每当要选择从哪个口出去的时候,就一条一条的匹配规则,找到符合的规则,按规则中的设置,从某个口抛出去,找下一跳IPX。

MAC地址是一个局域网内才有效的地址。只要过网关,就必定会改变,因为已经换了局域网。而IP地址则有可能变,有可能不变,不改变IP地址的网关,称为转发网关;改变IP地址的网关,称为NAT网关。一般在传递过程中,两种情况都可能有,但是第二种NAT更为常见。

NAT更为常见的原因是,一般来说一个国际通用的IP身份都很昂贵,对于一台家用路由器来说,其只需要记录下传递到该路由器的国际地址访问代表连接到这台路由器的家用实际地址即可,当收到来自这个国际地址的访问时,只需要查找就能正确转发,因此,一般来说家用路由器的包发出去的时候,都被家用路由器NAT成了运营商的地址,所以很多192.168....。

路由协议

路由器:一台拥有多张网卡的网络设备,当一个入口的网络包送到路由器时,它会根据存储在本地的转发信息库即路由表,来决定如何正确地转发流量。

路由表中包含有多条路由规则,一般来说每条规则包含最少三项信息:目的网络、出口设备和下一跳网关。上述可以通过 route 命令和 ip route 命令都可以进行查询或者配置。直接修改路由表的三项信息进行配置的属于根据目的IP地址来配置路由

一般来说,在现实中网络环境更为复杂,除了可以根据目的 IP 地址配置路由外,也可以根据多个参数来配置路由,属于策略路由。可以配置多个路由表,根据源IP地址、入口设备、TOS等选择路由表,然后在路由表中查找路由。从而使得来自不同来源的包走不同的路由。

上述配置方法本质上都是属于静态路由,一旦网络发生变化,修改较为复杂。

动态路由

动态路由可以根据路由协议算法生成动态路由表,随网络运行状况的变化而变化。

对于一个网络拓扑图来说,路由肯定希望找到一种从源到目的地的最短路径,一般有如下方法:

  • 距离矢量路由算法,基于Bellman-Ford算法。每个路由器都保存一个路由表,包含多行,每行对应网络中的一个路由器,每一行包含两部分信息,一个是要到目标路由器,走哪条路出去,另一个是到目标路由器的距离。每个路由器都有全局信息,也都知道自己和邻居之间的距离,每过几秒,每个路由器都将自己所知的到达所有的路由器的距离告知邻居,每个路由器也能从邻居那里得到相似的信息,然后每个路由器根据收集信息,计算和其他路由器的距离。缺点是:

    • 好消息传得快,坏消息传的慢

    • 需要发送全局路由表,耗费资源,不适用于大型网络

  • 链路状态路由算法,基于 Dijkstra 算法。当一个路由器启动的时候,首先是发现邻居,然后计算和邻居的距离,发送一个 echo,要求马上返回,除以二就是距离。然后将自己和邻居之间的链路状态包广播出去,发送到整个网络的每个路由器。每个路由器就能据此在本地建立一个完整的图,并采用 Dijkstra 算法,找到两点之间的最短路径。

动态路由协议

  1. 基于链路状态路由算法的OSPF(开放式最短路径优先),主要用在数据中心内部,用于路由决策,因而称为内部网关协议 IGP。重点是找到最短路径,当 OSPF 发现多个最短路径时,可以在多个路径中进行负载均衡,即等价路由

  2. 基于距离矢量路由算法的BGP,外部网关路由与内部网关路由不同的是,内部只需要考虑距离,一般来说距离最短即最优,而外部路由不止要考虑距离,还要考虑到不同路由器之间是否允许该类型的数据包传输通过。

    网络可以根据自治范围分成不同类型的自治系统 AS

    • Stub AS:对外只有一个连接。这类AS不会传输其他AS的包。例如,个人或者小公司的网络。

    • Multihomed AS:可能有多个连接连到其他的AS,但是大多拒绝帮其他的AS传输包。例如一些大公司的网络。

    • Transit AS:有多个连接连到其他的AS,并且可以帮助其他的AS传输包。例如主干网。

    对于每个自治系统AS来说,其都有对应的边界路由器,通过它可以与外网其他网络建立联系。

    BGP 可以分为两类,eBGP和iBGP

    自治系统间进行消息传递需要用到边界路由器,而边界路由器之间使用 eBGP 广播路由。而边界路由器得到的路由需要通过 iBGP 来导入到内部网络,使得自治系统内部的路由器能够找到到达外网目的地的最优的边界路由器。

    BGP协议使用路径矢量路由协议,解决了前述距离矢量路由协议的缺点

    • 首先在 BGP 内,除了下一跳hop之外,还包括了自治系统AS的路径,从而可以避免坏消息传的慢的问题,因为边界路由器知道内部路由器可以到达其他自治系统是通过自己,一旦自己都无法到达其他自治系统,则内部的肯定也到达不了,就不用再假设尝试了,可以快速收敛。

    • 在路径中可以将一个自治系统看成一个整体,不再区分在这个自治系统内部的不同路由器,只记住边界路由器,整个的系统量级就得到了简化,自治系统的数目也相对有限,不可能是很巨大的规模,即使选择去发送全局信息,相对的信息传输量和广播量也大幅下降。

Java Agent与字节码注入

Java agent是Java语言的一个特性,能够实现Java字节码的注入,本质上是通过 c agent 来实现的,C agent 是一个事件驱动的工具实现接口,通常会在 C agent 加载后的入口方案 Agent_OnLoad 处注册各个事件的钩子方法。当 Java 虚拟机触发了这些事件时,便会调用对应的钩子方法

premain 方法

premain 方法指在main方法之前执行的方法,Java 虚拟机能识别的 premain 方法接收的是字符串类型的参数,不是main方法的字符串数组。

如果想以 Java agent 的方式运行 premain 方法,需要将其打包成 jar 包,并在 MANIFEST.MF 配置文件中制定 Premain-class,之后在命令行中指定 Java agent 方式运行。

除了在命令行中指定 Java agent 方式运行之外,还可以通过 Attach API 远程加载

使用 Attach API 远程加载的 Java agent 不会先于 main 方法执行,取决于另一虚拟机调用 Attach API 的时机。且运行的也不再是 premain 方法,而是名为 agentmain 的方法。

Java 虚拟机不限制 Java agent 的数量。 java 命令后可以附上多个 -javaagent 参数,或者远程 attach 多个 Java agent,Java 虚拟机会按照定义顺序,或者 attach 的顺序逐个执行。

Java agent 的 instrumentation 机制,允许应用程序拦截类加载事件,并且更改该类的字节码。字节码注入即基于这一机制。

字节码注入

首先,在代码的 premain 类型的方法中需要有一个 Instrumentation 类型的参数,可以用其来注册类加载事件的拦截器。该拦截器需要实现 ClassFileTransformer 接口,并重写其中的 transform 方法。

transform 方法接收一个 byte 数组类型的参数,代表正在被加载的类的字节码,其会返回一个 比优特数组,代表更新后的类的字节码。方法返回之后,Java 虚拟机会使用返回的 byte 数组,来完成类加载。但如果返回为 null 或者抛出异常,则 Java 虚拟机会使用原来的 byte 数组完成类加载工作。

基于上述类加载事件的拦截功能,可以实现字节码注入,即往正在被加载的类中插入额外的字节码

Java agent 还可以通过 redefine 和 retransform 对已加载的类进行修改,需要用户传入所要 redefine 或者 retransform 的类实例

  • redefine 指舍弃原本的字节码,并替换成由用户提供的 byte 数组,一般用于修复出错的字节码

  • retransform 针对所传入的类,重新调用所有已注册的 ClassFileTransformer 的 transform 方法,主要应用场景:

    • 执行 premain 或者 agentmain 方法前,Java 虚拟机可能已经加载了不少类,但类的加载事件并未被拦截,也没被注入。使用 retransform 功能可以注入这些已加载但未注入的类。

    • 定义了多个 Java agent,多个注入的情况下,可能需要移除其中的部分注入。当调用 Instrumentation.removeTransformer 去除某个注入类后,可以调用 retransform 功能,重新从原始 byte 数组开始注入。

Java agent 的这些功能都是通过 JVMTI agent,即 C agent 来实现的。

基于字节码注入的 profiler

通过字节码注入可以实现代码覆盖工具,或者各种类型的 profiler,通常会定义一个运行时类·,并在某一程序行为的周围,注入对该运行时类中方法的调用,以表示该程序行为正要或者已经发生。

但在字节码注入过程中,因为注入位置不当等原因有可能会造成程序死循环,解决问题的关键在于设置一个线程私有的标识位,用以区分应用代码的上下文以及注入代码的上下文。当即将执行注入代码时,根据标识位判断是否已经位于注入代码的上下文之中。如果不是,则设置标识位并正常执行注入代码;如果是,则直接返回,不再执行注入代码。

字节码注入的命名空间可能存在问题,需要注意注入的依赖版本,否则可能会出现版本不一致的情况。

Intel PMEM的使用

傲腾持久化内存(AEP)

工作模式

  1. Memory Mode:主要适用于 需要大容量内存、不需要更改应用的场景。此时易失性内存DRAM+AEP 一起呈现为大容量内存,总容量为 AEP 的容量,DRAM 被用作cache,对系统不可见,该模式下内存是非持久性的,即断电后内容就丢失。

  2. App Direct Mode:应用直访模式下 软件和应用可直接和DCPM交互,减少堆栈复杂性。此时 AEP 呈现为 pmem持久性内存设备(/dev/pmem),系统看到的内存是DRAM,应用通过操作 pmem 设备来读写AEP。目前主流的文件系统ext4, xfs 都支持Direct Access的选项(-o dax), 当挂载完成后并且映射完地址空间后,读写就通过Load/Store的内存指令来进行,绕过了传统的文件系统或者块设备访问路径。该模式下具有持久性,还可字节可寻址(如内存)、高速缓存连贯性、低延迟,重启关机期间也可保留数据。

  3. 双重模式:部分处于内存模式,其余部分处于应用直接访问模式,应用可以使用高性能存储,避免在I/O总线上来回传输数据。

持久化内存

持久化内存(PMEM),也称非易失性内存(NVM)或内存级存储器(SCM),填补了现有层级性能和容量差距。

在这里插入图片描述

传统的数据被 read和write 到易失性内存(DRAM)上,然后被刷回到非易失性存储器(SSD、HDD)上。当一个应用程序启动的时候,数据需要先从非易失性存储器(SSD、HDD)上读到易失性内存(DRAM)才能被访问。

傲腾持久内存 (PMem) 与 DRAM 有一些相似之处:封装在 DIMMS(DIMM全称Dual-Inline-Memory-Modules,中文名叫双列直插式存储模块,是指奔腾CPU推出后出现的新型内存条)中,与 DRAM 驻留在同一总线/通道上,并且可以采取与 DRAM 相同的方式来存储易失性数据。

不同之处:持久内存的容量远远高于传统的 DRAM,甚至可以在不通电的情况下以持久模式存储数据,通过增加安全性来保证数据不受损。但总的来说持久内存模块的速度不如DRAM模块那么快

PMEM编程

AEP PMEM 设备映射到地址空间后,读写通过内存 load/store 进行,对于store命令,数据并不会马上写到 PMEM,而是会先在CPU cache里,然后刷回到内存,即使已经刷回到内存,实际上也可能在内存控制器的请求队列 (WPQ) 里。因此应用程序如果要确保数据已经持久化(即保证落盘),需要有额外的处理。

参考文章中假定定义结构体与更新操作

struct Students{
 ?  struct Student* student[MAX_STUDENT_CNT];
 ?  uint32_t student_cnt;
};
?
void push_back(struct Students* students, char* name) {
 ?  struct Student* stu = new_student(name);
 ?  student[student] = stu;
 ?  student_cnt++;
}

一般来说,数据刚开始的时候会被存储在 CPU 的多级 cache 中,但CPU不可能每次有更新的时候就把脏数据歇会,常规方法也很难预测 CPU 把哪一部分cache写回了。理论上如果此时掉电,会出现cache中数据丢失。并且对于上面的示例代码来说,即使 student 字段最先赋值,但 CPU 可能选择将其最后歇写回。为了解决这一问题,需要使用 flush 系的 CLFLUSH、CLFLUSHOPTCLWB,SFENCE

CLFLUSH 会命令 CPU 将对应 cacheline 逐出,强制性写回介质,但是这是一个同步指令,将会阻塞流水线,损失了一定的运行速度,也有同样功能的对应的新指令 CLFLUSHOPT 和 CLWB,是两个异步的指令,都能写回介质,区别在前者会清空 cacheline,后者则会保留,大部分场景下CLWB可能有更高的性能。

但异步的代价是对于cache下刷的顺序依旧不可预测,对应到实际场景就是两个量后赋值的那个依然有可能先下刷,因此需要使用SFENCE,SFENCE强制 sfence 指令前的写操作必须在 sfence 指令后的写操作前完成。

总结归纳这几个指令就是:

  • CLFLUSH: 同步的,能把指定缓存行(Cache Line)从所有级缓存中淘汰,若该缓存行中的数据被修改过,则将该数据写入主存

  • CLFLUSHOPT:异步的,作用与 CLFLUSH 相似但其之间的指令级并行度更高,在访问不同 CacheLine 时,CLFLUHOPT 可以乱序执行

  • CLWB:将指定的 Cache line 脏数据刷回内存,作用与 CLFLUSHOPT 相似,但在将缓存行中的数据写回之后,该缓存行仍将呈现为未被修改过的状态

  • NTSTORE (non-temporal store): 是一系列用于存储不同字长数据的指令,传输数据时能够绕过缓存,直接将数据写入主存

  • FENCE:也称内存屏障(Memory Barrier),约束其前后访存指令之间的相对顺序,其包括 LFENCE(约束 Load 指令), MFENCE(约束 L/S 指令), SFENCE(约束 Store 指令)。

上述几个指令都是优化cache,除此之外,编译器的优化策略与CPU的乱序执行也可能产生类似效果,因此提供了持久化内存开发套件 PMDK,PMDK 是一个库和工具集合,允许应用程序访问持久内存作为内存映射文件。

主要因为前述方法需要在合适的地方用一些底层CPU指令来实现,缺点是太过于底层且可移植性差, PMDK 套件的目的就是为了简化这些操作。

PMDK 库分成两大类:

  1. Volatile libraries。不关心数据的持久化,可以通过 persistent memory 扩展内存

  2. Persistent libraries。能够保证数据的 fail-safe

Volatile libraries

  1. libmemkind 提供 malloc 风格的接口,可以将持久化内存当成 DRAM 使用。

  2. libvmemcache 针对持久化内存的特点优化的易失性 LRU 缓存。

Persistent libraries

  1. libpmem 提供比较底层的操作持久化内存的接口,比如 pmem_map 类似 mmap、pmem_memcpy 类似 memcpy

  2. libpmemobj 提供基于持久化内存的对象存储能力。

  3. libpmemkv 是一个基于持久化内存的嵌入式 Key-Value 引擎,基于 B+ 树实现,针对读优化。

  4. libpmemlog 提供 append-only 的日志文件接口。

  5. libpmemblk 提供块存储接口,简单说就是将持久化内存抽象成一个数组。

PMDK中的 libpmem 中提供了API,用以处理所有与编译器的优化策略与CPU的乱序执行有关的可能产生影响的情况。

void pmem_flush(const void *addr, size_t len);
void pmem_drain(void);
void pmem_persist(const void *addr, size_t len);
  • pmem_flush是 flush 系指令的封装,但 libpmem会在装载时获取相关信息自动选择最优的指令

  • pmem_drain则是对sfence的封装

  • pmem_persist,只是连续调用了pmem_flushpmem_drainpmem_drain可能会阻塞一些操作,更好的做法是对数据结构里互不相干的几个字段分别flush,最后一并调用pmem_drain,以将阻塞带来的问题降到最低。

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-08-28 09:42:41  更:2021-08-28 09:44:09 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/28 20:30:04-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计