hexo增加mermaid
_config.next.yml文件
1 | # Mermaid tag |

例子
flowchart
A-- This is the text! ---B
sequenceDiagram
Alice->>John: Hello John, how are you?
John-->>Alice: Great!
Alice-)John: See you later!
1 | # Mermaid tag |

flowchart
A-- This is the text! ---B
sequenceDiagram
Alice->>John: Hello John, how are you?
John-->>Alice: Great!
Alice-)John: See you later!
因为两个网卡都在同一个网段,所以如果这个时候直接和我们的主机(IP192.168.0.2)进行ping,因为优先级的原因,会导致有一个网卡会无法ping通主机。

您可以把这个机制想象成一个严格的“门卫”。当服务器从一个网络接口(比如eth1)收到一个数据包时,这个“门卫”会去查路由表,问:“要回复这个数据包,我们应该从哪个接口发出去?”
eth1),则验证通过,数据包被正常接收处理。eth1进入,但路由表显示回复它的最佳路径是eth0,这时“门卫”就会拒绝这个数据包,导致ping不通 。关键在于,系统检查的是“反向路径”是否最优,而非是否可达 。 ## 问题诊断与解决步骤首先,您需要查看系统当前的反向路由检查设置。在终端中执行以下命令: 1
2
3cat /proc/sys/net/ipv4/conf/all/rp_filter
cat /proc/sys/net/ipv4/conf/eth0/rp_filter # 请将 eth0 替换为您的实际网卡名
cat /proc/sys/net/ipv4/conf/eth1/rp_filter # 请将 eth1 替换为您的实际网卡名
如果这些值被设置为 1(严格模式),那么它很可能就是导致您一个IP不通的原因。 ### 2. 关闭反向路由检查
重要提示:关闭此功能会降低系统对IP地址欺骗(IP Spoofing)的防御能力 。请确保您的服务器处于受信任的内网环境中。
方法一:临时关闭(重启后失效)
适用于快速验证问题。执行命令: 1
2
3echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter
echo 0 > /proc/sys/net/ipv4/conf/eth0/rp_filter
echo 0 > /proc/sys/net/ipv4/conf/eth1/rp_filter
通过修改系统配置文件,使设置永久生效。
/etc/sysctl.conf文件: 1 | vi /etc/sysctl.conf |
1 | net.ipv4.conf.all.rp_filter = 0 |
1 | sysctl -p |
如果担心完全关闭的安全风险,折中的办法是启用松散模式(Loose Mode),将值设置为 2。这样既允许了非对称路径的存在(您的包从A进,从B出),又保留了基本的源地址验证 。修改方法同上,只需将配置文件中的值改为2: 1
2net.ipv4.conf.all.rp_filter = 2
net.ipv4.conf.default.rp_filter = 2
虽然反向路由检查是最常见的原因,但如果调整后问题依旧,还可以关注以下几点:
ip route show或 route -n确认两个IP地址的路由表项是否正确。arp -a或 ip neighbour检查ARP表项是否正确 。转载:https://zhuanlan.zhihu.com/p/1985799288740136627
ARM 相关缩略语大全(按大类系统整理)
本文系统性整理 ARM 架构中常见缩略语,覆盖 CPU / Core / AMBA / CoreSight / GIC / 内存系统 / 异构与加速 / 安全 / 虚拟化 / SoC 互连 等多个层面,适合 内核、驱动、SoC、性能分析、芯片架构 方向长期查阅。
一、ARM 架构与指令集(Architecture & ISA)
| 缩略语 | 全称 | 说明 |
| ARM | Advanced RISC Machines | ARM 公司 / 架构统称 |
| ISA | Instruction Set Architecture | 指令集架构 |
| AArch32 | ARM Architecture 32-bit | ARMv7 及兼容 32 位执行态 |
| AArch64 | ARM Architecture 64-bit | ARMv8+ 的 64 位执行态 |
| ARMv7-A | ARM Architecture v7 Application | 经典 32 位应用处理器架构 |
| ARMv8-A | ARM Architecture v8 Application | 引入 AArch64 |
| ARMv9-A | ARM Architecture v9 Application | 引入 SVE2 / CCA |
| Thumb | Thumb Instruction Set | 16-bit 压缩指令集 |
| Thumb-2 | Thumb-2 Instruction Set | 16/32 混合指令 |
| NEON | Advanced SIMD | ARM 向量 SIMD 扩展 |
| SVE | Scalable Vector Extension | 可变向量长度 SIMD |
| SVE2 | SVE v2 | 面向通用计算与 DSP |
| SME | Scalable Matrix Extension | ARMv9 矩阵扩展 |
二、ARM CPU Core 家族(Processor Cores)
Cortex 系列
| 缩略语 | 全称 | 说明 |
| Cortex-A | Cortex Application | 应用处理器(Linux / Android) |
| Cortex-R | Cortex Real-time | 实时处理器 |
| Cortex-M | Cortex Microcontroller | 微控制器 |
Cortex-A 常见核心
| Core | 特点 | 应用 |
| A53 | 小核,低功耗 | 手机 / 嵌入式 |
| A55 | A53 后继 | big.LITTLE |
| A57 | 高性能 | 服务器 / 手机 |
| A72 | 高 IPC | SoC 主核 |
| A73 | 优化能效 | 移动平台 |
| A75 | ARMv8.2 | DynamIQ |
| A76 | 高性能单核 | 旗舰 SoC |
| A78 | 高能效 | 新一代移动 |
| X1 / X2 / X3 | Cortex-X | 极致性能 |
Neoverse(服务器)
| Core | 定位 |
| N1 | 通用服务器 |
| N2 | ARMv9 数据中心 |
| V1 | SVE 向量优化 |
| E1 | 边缘高吞吐 |
三、异常级别与特权模型(Exception Levels)
| 缩略语 | 全称 | 说明 |
| EL0 | Exception Level 0 | 用户态 |
| EL1 | Exception Level 1 | 内核态 |
| EL2 | Exception Level 2 | Hypervisor |
| EL3 | Exception Level 3 | Secure Monitor |
| PL | Privilege Level | 特权等级 |
| SPSR | Saved Program Status Register | 异常返回寄存器 |
| ESR | Exception Syndrome Register | 异常原因 |
四、内存系统(Memory System & MMU)
| 缩略语 | 全称 | 说明 |
| MMU | Memory Management Unit | 内存管理单元 |
| SMMU | System Memory Management Unit | 系统内存管理单元 |
| MPU | Memory Protection Unit | 无 MMU 系统 |
| TLB | Translation Lookaside Buffer | 地址翻译缓存 |
| ASID | Address Space ID | 进程地址空间标识 |
| VA | Virtual Address | 虚拟地址 |
| PA | Physical Address | 物理地址 |
| IPA | Intermediate Physical Address | 虚拟化中间地址 |
| TTBR | Translation Table Base Register | 页表基址 |
| MAIR | Memory Attribute Indirection Register | 内存属性 |
| ATS | Address Translation Service | PCIe 地址翻译 |
五、AMBA 总线与互连(Bus & Interconnect)
AMBA 总线协议
| 缩略语 | 全称 | 说明 |
| AMBA | Advanced Microcontroller Bus Architecture | ARM 总线体系 |
| AXI | Advanced eXtensible Interface | 高性能总线 |
| AXI4 | AXI version 4 | 主流 SoC 总线 |
| AXI-Lite | AXI Lite | 寄存器访问 |
| AHB | Advanced High-performance Bus | 中速总线 |
| APB | Advanced Peripheral Bus | 低速外设 |
| CHI | Coherent Hub Interface | Cache Coherent 总线 |
| ACE | AXI Coherency Extensions | 一致性扩展 |
互连与一致性
| 缩略语 | 说明 | |
| CCN | Cache Coherent Network | 一致性网络 |
| CMN | Coherent Mesh Network | Mesh 架构 |
| DVM | Distributed Virtual Memory | TLB 一致性 |
| Snoop | Cache Snoop | Cache 探测 |
六、GIC 中断控制器(Generic Interrupt Controller)
| 缩略语 | 全称 | 说明 |
| GIC | Generic Interrupt Controller | ARM 中断控制器 |
| GICv2 | GIC version 2 | ARMv7 常用 |
| GICv3 | GIC version 3 | ARMv8 主流 |
| GICv4 | GIC version 4 | 虚拟化直通 |
| SGI | Software Generated Interrupt | 软件中断 |
| PPI | Private Peripheral Interrupt | CPU 私有中断 |
| SPI | Shared Peripheral Interrupt | 共享中断 |
| ITS | Interrupt Translation Service | MSI 映射 |
| LPI | Locality-specific Peripheral Interrupt | 大规模中断 |
七、CoreSight 调试与追踪(Debug & Trace)
| 缩略语 | 全称 | 说明 |
| CoreSight | ARM Debug & Trace 架构 | 调试体系 |
| JTAG | Joint Test Action Group | 硬件调试接口 |
| SWD | Serial Wire Debug | 精简调试 |
| ETM | Embedded Trace Macrocell | 指令追踪 |
| PTM | Program Trace Macrocell | 程序追踪 |
| STM | System Trace Macrocell | 系统事件追踪 |
| ITM | Instrumentation Trace Macrocell | 软件埋点 |
| DWT | Data Watchpoint and Trace | 数据监控 |
| CTI | Cross Trigger Interface | 模块联动 |
| TPIU | Trace Port Interface Unit | Trace 输出 |
八、安全架构(Security)
| 缩略语 | 全称 | 说明 |
| TrustZone | ARM TrustZone | 安全隔离 |
| Secure World | 安全世界 | TEE |
| Normal World | 普通世界 | REE |
| TEE | Trusted Execution Environment | 安全 OS |
| OP-TEE | Open Portable TEE | 开源 TEE |
| CCA | Confidential Compute Architecture | ARMv9 |
| RME | Realm Management Extension | Realm 隔离 |
九、虚拟化(Virtualization)
| 缩略语 | 说明 |
| HYP | Hypervisor 模式 |
| VHE | Virtualization Host Extensions |
| Stage-2 | 二级地址翻译 |
| VGIC | Virtual GIC |
| VCPU | Virtual CPU |
| VMID | Virtual Machine ID |
十、SoC / 系统级常见缩略语
| 缩略语 | 说明 |
| SoC | System on Chip |
| PMU | Performance Monitor Unit |
| DVFS | Dynamic Voltage and Frequency Scaling |
| QoS | Quality of Service |
| NoC | Network on Chip |
| SRAM | Static RAM |
| DRAM | Dynamic RAM |
| LPDDR | Low Power DDR |
十一、软件与生态
| 缩略语 | 说明 |
| PSCI | Power State Coordination Interface |
| SMC | Secure Monitor Call |
| HVC | Hypervisor Call |
| UEFI | Unified Extensible Firmware Interface |
| ACPI | Advanced Configuration and Power Interface |
| DT / DTS | Device Tree / Source |
介绍几款Windows下的性能分析工具。
Windows自带的,详细使用方法,参考官方链接 
下载后即可使用的工具集,官方下链接下载,如常用的process monitor也包括在内 
这个是Windows下的性能工具合集,见官网,Windows 性能工具包包含两个独立的工具:Windows Performance Recorder (WPR) 和 Windows Performance Analyzer (WPA) 此外,还保留了对以前的命令行工具 Xperf 的支持。 但是,不再支持 Xperfview。 所有记录都必须使用 WPA 来打开和分析。
需要使用WPR收集数据,再使用WPA分析数据。 
perfview下载地址。Perfview是一个开源的CPU和内存性能分析工具,也包括一些针对.NET的分析功能,例如GC分析,JIT分析,甚至ASP.NET中的请求统计等等。Perfview是一个Windows应用程序,但也能对在Linux系统上采集的数据进行分析(参考)。Perfview免安装,而且只是一个14M的.exe文件,非常容易部署到需要进行性能分析的机器上,例如生产环境的服务器。而且在性能数据收集的过程中不需要重启应用程序或者服务器,而且收集的性能数据日志(.etl文件)可以被拷贝到其他Windows机器上,再进行分析工作,对业务的影响非常少。 
系统资源监控工具,支持windows10、windows11,官网地址。 
参考https://github.com/ituring/tcp-book/?tab=readme-ov-file
https://www.virtualbox.org/ 1
2
3VirtualBox 图形用户界面
版本 7.0.22 r165102 (Qt5.15.2)
Copyright © 2024 Oracle and/or its affiliates
https://developer.hashicorp.com/vagrant 1
2PS C:\Users\dushenda> vagrant.exe -v
Vagrant 2.4.9
https://learn.microsoft.com/en-us/powershell/scripting/install/install-powershell-on-windows?view=powershell-7.5 1
2PS C:\Users\dushenda> pwsh.exe -v
PowerShell 7.1.4
1 | # 在启动 Vagrant 前设置 DISPLAY |
1 | Vagrant.configure("2") do |config| |
1 | $ git clone https://github.com/ituring/tcp-book.git |
使用以下的命令,ssh连接到Guest操作系统上。在登录消息显示之后,命令行提示会变成vagrant@guest1:~$。
在powershell中运行vagrant ssh guest1。
1 | $ vagrant ssh guest1 |
启动Wireshark。 1
vagrant@guest1:~$ wireshark

当确认已经准备好VirtualBox和Vagrant的环境之后,请将此Github代码库克隆到任意目录。打开其中的ns3/vagrant目录,执行vagrant up命令。如此一来,就在虚拟机上完成了安装Ubuntu 16.04,并搭建ns-3的过程。另外,在2019年4月1日的时间点,第5章和第6章所使用的CUBIC和BBR模块不支持ns-3.28以上版本,因此本书使用ns-3.27版本。由于搭建ns-3环境相当花时间,请务必耐心等待 。
1 | $ git clone https://github.com/ituring/tcp-book.git |
在powershell中运行vagrant ssh 
1 | $ cat /proc/meminfo |
Page Cache = Buffers + Cached + SwapCached = Active(file) + Inactive(file) + Shmem + SwapCached

/proc/meminfo的解释见如下网站https://www.kernel.org/doc/Documentation/filesystems/proc.rst
在 Page Cache 中,Active(file)+Inactive(file) 是 File-backed page(与文件对应的内存页),是你最需要关注的部分。因为你平时用的 mmap() 内存映射方式和 buffered I/O 来消耗的内存就属于这部分。
而 SwapCached 是在打开了 Swap 分区后,把 Inactive(anon)+Active(anon) 这两项里的匿名页给交换到磁盘(swap out),然后再读入到内存(swap in)后分配的内存。由于读入到内存后原来的 Swap File 还在,所以 SwapCached 也可以认为是 File-backed page,即属于 Page Cache。这样做的目的也是为了减少 I/O。

SwapCached 只在 Swap 分区打开的情况下才会有,建议在生产环境中关闭 Swap 分区,因为 Swap 过程产生的 I/O 会很容易引起性能抖动。
下面解释一下free输出
buff/cache = Buffers + Cached + SReclaimable 1
2
3
4$ free -k
total used free shared buff/cache available
Mem: 7926580 7277960 492392 10000 156228 430680
Swap: 8224764 380748 7844016
| 指标 | 数据来源 (参考 /proc/meminfo) |
主要作用 |
|---|---|---|
Buffers |
Buffers值 |
临时存放原始磁盘块数据,用于优化对磁盘(块设备)的直接读写操作。 |
Cached |
Cached值 |
缓存从文件读取的数据(即页缓存/Page Cache),使得再次访问这些文件时可以直接从内存快速读取,无需访问磁盘。 |
SReclaimable |
SReclaimable值 |
属于 Slab 分配器 的一部分,记录内核对象(如目录项、inode 等)缓存中可以被回收占用的内存。指可以被回收的内核内存,包括 dentry 和 inode 等。 |
实际场景中的表现:通过 vmstat等工具可以观察它们的变化。例如,当使用 dd命令向磁盘分区直接写入大量数据时(如 dd if=/dev/urandom of=/dev/sdb1 …),会观察到 Buffers显著增长,因为这是对原始磁盘块的操作。而当读写普通文件时(如 dd if=/dev/urandom of=/tmp/file …),则会看到 Cached部分明显增加,因为这些数据被文件系统缓存了。
先看如何使用pagecache的方法
pagecache的意义就是加速:标准 I/O 和内存映射会先把数据写入到 Page Cache,这样做会通过减少 I/O 次数来提升读写效率。
我们看一个具体的例子。首先,我们来生成一个 1G 大小的新文件,然后把 Page Cache 清空,确保文件内容不在内存中,以此来比较第一次读文件和第二次读文件耗时的差异。具体的流程如下。
1 | dd if=/dev/zero of=./dd.out bs=4096 count=((1024*256)) |
清空 Page Cache,需要先执行一下 sync 来将脏页同步到磁盘再去 drop cache。
1 | sync && echo 3 > /proc/sys/vm/drop_caches |
读两次文件,计算耗时 1
2
3
4
5
6
7
8
9$ time cat /home/yafang/test/dd.out &> /dev/null
real 0m5.733s
user 0m0.003s
sys 0m0.213s
$ time cat /home/yafang/test/dd.out &> /dev/null
real 0m0.132s
user 0m0.001s
sys 0m0.130s
通过这样详细的过程你可以看到,第二次读取文件的耗时远小于第一次的耗时,这是因为第一次是从磁盘来读取的内容,磁盘 I/O 是比较耗时的,而第二次读取的时候由于文件内容已经在第一次读取时被读到内存了,所以是直接从内存读取的数据,内存相比磁盘速度是快很多的。这就是 Page Cache 存在的意义:减少 I/O,提升应用的 I/O 速度。
Page Cache 的产生有两种不同的方式:

buffered/mmap都能产生 Page Cache,但是二者的还是有些差异的:标准 I/O 是写的 (write(2)) 用户缓冲区 (Userpace Page 对应的内存),然后再将用户缓冲区里的数据拷贝到内核缓冲区 (Pagecache Page 对应的内存);如果是读的 (read(2)) 话则是先从内核缓冲区拷贝到用户缓冲区,再从用户缓冲区读数据,也就是 buffer 和文件内容不存在任何映射关系。
对于存储映射 I/O 而言,则是直接将 Pagecache Page 给映射到用户地址空间,用户直接读写 Pagecache Page 中内容。
显然,存储映射 I/O 要比标准 I/O 效率高一些,毕竟少了“用户空间到内核空间互相拷贝”的过程。这也是很多应用开发者发现,为什么使用内存映射 I/O 比标准 I/O 方式性能要好一些的主要原因。
我们来用具体的例子演示一下 Page Cache 是如何“诞生”的,就以其中的标准 I/O 为例,因为这是我们最常使用的一种方式,如下是一个简单的示例脚本:
使用命令组合 1
2
3
4
5
6
7
8
9
10
11
12# 安装工具
yum install blktrace -y
# 采集数据:(监控30秒)
blktrace -d /dev/nvme0n1 -w 30
# 合并数据:
blkparse -i nvme0n1 -d nvme_trace.bin。
# 解析分析:,重点查看 "ALL" 统计区和 "Device Overhead" 分布
btt -i nvme_trace.bin
# 组合命令
blktrace -d /dev/sdf -w 5 -o - | blkparse -i - -d btt.bin > blkparse.txt
btt -i btt.bin1
2# 追踪设备/dev/sda并输出blkparse数据
btrace /dev/sda
实际上btrace是调用了blktarce和blkparse组合出来的脚本文件,位于/usr/bin/btrace
一个I/O请求,从应用层到底层块设备,路径如下图所示:
我们将IO路径简化一下:
一个I/O请求进入block layer之后,可能会经历下面的过程:
blktrace 能够记录下IO所经历的各个步骤:
我们一起看下blktrace的输出长什么样子: 
blkparse的输出包含了每个 I/O 请求事件的详细信息,理解这些字段是分析的关键:
| 字段 | 含义说明 |
|---|---|
| 设备号 (Major, Minor) | 如 8,0通常指 /dev/sda |
| CPU ID | 处理此事件的 CPU 核心编号 |
| 序列号 | 事件的序列号 |
| 时间戳 | 事件发生的时间(通常为相对时间) |
| 进程ID (PID) | 发起 I/O 操作的进程 ID |
| 事件类型 (Action) | 核心字段,表示 I/O 请求所处的阶段 |
| RWBS 描述符 | 描述 I/O 类型:R(读)/W(写)/B(屏障)/S(同步) |
| 扇区信息 | 如 2048 + 8,表示起始扇区号及连续扇区数 |
| 进程名 | 发起 I/O 的进程名称 |
其中,事件类型 (Action) 是理解 I/O 路径的关键,它记录了请求从产生到完成的各个阶段:
根据这些事件的时间戳,可以计算出 I/O 路径各阶段的耗时,例如:
iostat中的 await。其中第六个字段非常有用:每一个字母都代表了IO请求所经历的某个阶段。 1
2
3
4
5
6
7
8
9Q – 即将生成IO请求
|
G – IO请求生成
|
I – IO请求进入IO Scheduler队列
|
D – IO请求进入driver
|
C – IO请求执行完毕
注意,我们心心念念的service time,也就是反应块设备处理能力的指标,就是从D到C所花费的时间,简称D2C。
而iostat输出中的await,即整个IO从生成请求到IO请求执行完毕,即从Q到C所花费的时间,我们简称Q2C。
我们知道Linux 有I/O scheduler,调度器的效率如何,I2D是重要的指标。
注意,这只是blktrace输出的一个部分,很明显,我们还能拿到offset和size,根据offset,我们能拿到某一段时间里,应用程序都访问了整个块设备的那些block,从而绘制出块设备访问轨迹图。
另外还有size和第七个字段(Read or Write),我们可以知道IO size的分布直方图。对于本文来讲,我们就是要根据blktrace来获取这些信息。 # 工具使用
我们接下来简单介绍这些工具的使用,其中这三个命令都是属于blktrace这个包的,他们是一家人。
首先通过如下命令,可以查看磁盘上的实时信息:
1 | blktrace -d /dev/sdb -o – | blkparse -i – |
这个命令会连绵不绝地出现很多输出,当你输入ctrl+C的时候,会停止。
当然了,你也可以先用如下命令采集信息,待所有信息采集完毕后,统一分析所有采集到的数据。搜集信息的命令如下:
1 | blktrace -d /dev/sdb |
注意,这个命令并不是只输出一个文件,他会根据CPU的个数上,每一个CPU都会输出一个文件,如下所示:
1 | -rw-r--r-- 1 manu manu 1.3M Jul 6 19:58 sdb.blktrace.0 |
有了输出,我们可以通过blkparse -i sdb来分析采集的数据:
1 | 8,16 7 2147 0.999400390 630169 I W 447379872 + 8 [kworker/u482:0] |
注意,blkparse仅仅是将blktrace输出的信息转化成人可以阅读和理解的输出,但是,信息太多,太杂,人完全没法得到关键信息。 这时候btt就横空出世了,这个工具可以将blktrace采集回来的数据,进行分析,得到对人更有用的信息。事实上,btt也是我们的终点。
注意,btt已经可以很自如地生成这部分统计信息,我们可以很容易得到如下的表格:

| 阶段缩写 | 全称与含义 | 性能指标解读 |
|---|---|---|
| Q2Q | Queue to Queue 衡量上一个I/O完成到当前I/O抵达块层的时间间隔,反映了I/O请求的到达速率。 |
间隔时间短表示I/O负载很重,请求密集;间隔时间长则表示I/O负载较轻 。 |
| Q2G | Queue to Get I/O请求进入块层后,等待系统为其分配一个 request结构体的时间。 |
此阶段通常极短。如果时间较长,可能表示系统内存紧张或内核在分配数据结构时遇到瓶颈。 |
| G2I | Get to Insert 获取 request结构体后,准备并将其插入到I/O调度器队列所花费的时间。 |
此阶段也非常短暂。它反映了I/O调度器处理请求的初始开销。 |
| I2D | Insert to Issue 请求在I/O调度器队列中等待以及被调度(合并、排序)后,派发(Issue)到设备驱动程序的时间。这是分析操作系统层面瓶颈的关键指标 。 |
这是判断I/O调度器和系统负载的关键指标。如果I2D时间很长,说明: 1. 存储设备速度跟不上请求速率,队列中有大量请求在排队。 2. I/O调度策略可能不适合当前负载。 |
| D2C | Issue to Complete 请求被提交给设备驱动后,在物理硬件上真正执行所花费的时间(包括在设备自身的缓存和介质上的读写时间)。这是评估硬件性能最直接的指标 。 |
这是判断硬件瓶颈的核心指标。如果D2C时间很长,通常意味着: 1. 存储设备本身性能不足(如机械硬盘随机读写慢)。 2. 设备可能处于高负载或存在故障。 |
| Q2C | Queue to Complete 一个I/O请求在块层处理的总时间。它近似等于 Q2I + I2D + D2C(Q2I可再细分为Q2G+G2I)。 |
这大致相当于 iostat命令输出的 await值。反映了一个I/O请求从进入系统到完成的总延迟。 |
方法如下:
首先blkparse可以将对应不同cpu的多个文件聚合成一个文件:
1 | blkparse -i sdb -d sdb.blktrace.bin |
然后btt就可以分析这个sdb.blktrace.bin了:
1 | ==================== All Devices ==================== |
注意: D2C和Q2C,一个是表征块设备性能的关键指标,另一个是客户发起请求到收到响应的时间,我们可以看出,
D2C 平均在0.000779355 秒,即0.7毫秒 Q2C 平均在0.002522832 秒,即2.5毫秒,
无论是service time 还是客户感知到的await time,都是非常短的,表现非常不俗。但是D2C花费的时间只占整个Q2C的30%, 51%以上的时间花费在I2D。
https://bean-li.github.io/blktrace-to-report/
在wireshark的以下两个部分进行抓包,不建议性能分析时进行,在连通性验证时可以使用(ssh传输可能丢包等)

直接抓包解析 1
ssh <ip> 'tcpdump -ni any -s0 -U -w - udp port 53' | wireshark -k -i -
1
ssh -J <jumpsever ip> <ip> 'tcpdump -ni any -s0 -U -w - udp port 53' | wireshark -k -i -
1
ssh <ip> 'tcpdump -ni any -s0 -U -w- udp port 53' > /tmp/packets.pcap
过滤tcp flags,比如抓syn报文,看是否能握手成功 1
tcpdump -ni any 'tcp[tcpflags] == tcp-syn'
sar -n ETCP 1看到retrans/s不断上升 1
watch -n 0.1 'ss -tpn state syn-sent'
1
tcpdump -ni any 'tcp[tcpflags] == tcp-syn or tcp[13]=18'
1
tcpdump -ni any 'tcp[tcpflags] == tcp-syn or tcp[13] & 4!=0'
1
tcpdump -ni any 'tcp[tcpflags] == tcp-syn or icmp[0] = 3'
1
tcpdump -ni any '(tcp[tcpflags] == tcp-syn or tcp[13]=18) or tcp[13] & 4!=0 or icmp[0] = 3'
1
2
3tcpdump 'tcp[tcpflags] == tcp-syn'
tcpdump 'tcp[tcpflags] == tcp-rst'
tcpdump 'tcp[tcpflags] == tcp-fin'1
2
3tcpdump less 32
tcpdump greater 32
tcpdump <= 1028080的流量,每个文件最大 100MB,最多保留 10个文件,写满后覆盖最旧的文件 1
tcpdump -i any -C 100 -W 10 -w my_capture.pcap port 8080
10000个发往 80端口的包 1
tcpdump -i any -c 10000 -w http_requests.pcap dst port 80
| 参数 | 说明 | 示例 |
|---|---|---|
-i <interface> |
指定抓包网卡。any表示所有网卡。 |
-i any |
port <端口号> |
过滤特定端口的流量(TCP/UDP)。 | port 8080 |
-C <大小> |
按文件大小分割。单位通常为MB(M)。 | -C 100 |
-W <数量> |
限制文件总数,与 -C配合实现循环覆盖。 |
-W 10 |
-w <文件名> |
将抓取的原始数据包写入指定文件。 | -w my_capture.pcap |
-c <数量> |
抓取指定数量的数据包后自动退出。 | -c 10000 |
-s <长度> |
设置每个数据包的抓取长度(快照长度byte),-s 0表示抓取完整数据包。(在长期抓只需要分析报文头很实用) |
-s 0 |
| Command | Example usage | Explanation |
|---|---|---|
-i any |
tcpdump -i any |
Capture from all interfaces; may require superuser (sudo/su) |
-i eth0 |
tcpdump -i eth0 |
Capture from the interface eth0 |
-c count |
tcpdump -i eth0 -c 5 |
Exit after receiving count (5) packets |
-r captures.pcap |
tcpdump -i eth0 -r captures.pcap |
Read and analyze saved capture file captures.pcap |
tcp |
tcpdump -i eth0 tcp |
Show TCP packets only |
udp |
tcpdump -i eth0 udp |
Show UDP packets only |
icmp |
tcpdump -i eth0 icmp |
Show ICMP packets only |
ip |
tcpdump -i eth0 ip |
Show IPv4 packets only |
ip6 |
tcpdump -i eth0 ip6 |
Show IPv6 packets only |
arp |
tcpdump -i eth0 arp |
Show ARP packets only |
rarp |
tcpdump -i eth0 rarp |
Show RARP packets only |
slip |
tcpdump -i eth0 slip |
Show SLIP packets only |
-I |
tcpdump -i eth0 -I |
Set interface as monitor mode |
-K |
tcpdump -i eth0 -K |
Don’t verify checksum |
-p |
tcpdump -i eth0 -p |
Don’t capture in promiscuous mode |
| Filter expression | Explanation |
|---|---|
src host 127.0.0.1 |
Filter by source IP/hostname 127.0.0.1 |
dst host 127.0.0.1 |
Filter by destination IP/hostname 127.0.0.1 |
host 127.0.0.1 |
Filter by source or destination = 127.0.0.1 |
ether src 01:23:45:AB:CD:EF |
Filter by source MAC 01:23:45:AB:CD:EF |
ether dst 01:23:45:AB:CD:EF |
Filter by destination MAC 01:23:45:AB:CD:EF |
ether host 01:23:45:AB:CD:EF |
Filter by source or destination MAC 01:23:45:AB:CD:EF |
src net 127.0.0.1 |
Filter by source network location 127.0.0.1 |
dst net 127.0.0.1 |
Filter by destination network location 127.0.0.1 |
net 127.0.0.1 |
Filter by source or destination network location 127.0.0.1 |
net 127.0.0.1/24 |
Filter by source or destination network location 127.0.0.1 with the tcpdump subnet mask of length 24 |
src port 80 |
Filter by source port = 80 |
dst port 80 |
Filter by destination port = 80 |
port 80 |
Filter by source or destination port = 80 |
src portrange 80-400 |
Filter by source port value between 80 and 400 |
dst portrange 80-400 |
Filter by destination port value between 80 and 400 |
portrange 80-400 |
Filter by source or destination port value between 80 and 400 |
ether broadcast |
Filter for Ethernet broadcasts |
ip broadcast |
Filter for IPv4 broadcasts |
ether multicast |
Filter for Ethernet multicasts |
ip multicast |
Filter for IPv4 multicasts |
ip6 multicast |
Filter for IPv6 multicasts |
ip src host mydevice |
Filter by IPv4 source hostname mydevice |
arp dst host mycar |
Filter by ARP destination hostname mycar |
rarp src host 127.0.0.1 |
Filter by RARP source 127.0.0.1 |
ip6 dst host mywatch |
Filter by IPv6 destination hostname mywatch |
tcp dst port 8000 |
Filter by destination TCP port = 8000 |
udp src portrange 1000-2000 |
Filter by source TCP ports in 1000–2000 |
sctp port 22 |
Filter by source or destination port = 22 |
| Example | Explanation |
|---|---|
tcpdump -i eth0 -A |
Print each packet (minus its link level header) in ASCII. Handy for capturing web pages. [![Screenshot with ASCII (sudo tcpdump twitter) |
tcpdump -D |
Print the list of the network interfaces available on the system and on which tcpdump can capture packets. |
tcpdump -i eth0 -e |
Print the link-level header on each output line, such as MAC layer addresses for protocols such as Ethernet and IEEE 802.11. |
tcpdump -i eth0 -F /path/to/params.conf |
Use the file params.conf as input for the filter expression. (Ignore other expressions on the command line.) |
tcpdump -i eth0 -n |
Don’t convert addresses (i.e., host addresses, port numbers, etc.) to names. |
tcpdump -i eth0 -S |
Print absolute, rather than relative, TCP sequence numbers. (Absolute TCP sequence numbers are longer.) |
tcpdump -i eth0 --time-stamp-precision=nano |
When capturing, set the timestamp precision for the capture to tsp: • micro for microsecond (default) • nano for nanosecond. |
tcpdump -i eth0 -t |
Omit the timestamp on each output line. |
tcpdump -i eth0 -tt |
Print the timestamp, as seconds since January 1, 1970, 00:00:00, UTC, and fractions of a second since that time, on each dump line. |
tcpdump -i eth0 -ttt |
Print a delta (microsecond or nanosecond resolution depending on the --time-stamp-precision option) between the current and previous line on each output line. The default is microsecond resolution. |
tcpdump -i eth0 -tttt |
Print a timestamp as hours, minutes, seconds, and fractions of a second since midnight, preceded by the date, on each dump line. |
tcpdump -i eth0 -ttttt |
Print a delta (microsecond or nanosecond resolution depending on the --time-stamp-precision option) between the current and first line on each dump line. The default is microsecond resolution. |
tcpdump -i eth0 -u |
Print undecoded network file system (NFS) handles. |
tcpdump -i eth0 -v |
Produce verbose output. When writing to a file ( -w option) and at the same time not reading from a file (-r option), report to standard error, once per second, the number of packets captured. |
tcpdump -i eth0 -vv |
Additional verbose output than -v |
tcpdump -i eth0 -vvv |
Additional verbose output than -vv |
tcpdump -i eth0 -x |
Print the headers and data of each packet (minus its link level header) in hex. |
tcpdump -i eth0 -xx |
Print the headers and data of each packet, including its link level header, in hex. |
tcpdump -i eth0 -X |
Print the headers and data of each packet (minus its link level header) in hex and ASCII. |
tcpdump -i eth0 -XX |
Print the headers and data of each packet, including its link level header, in hex and ASCII. |
| Command | Example | Explanation |
|---|---|---|
-w captures.pcap |
tcpdump -i eth0 -w captures.pcap |
Output capture to a file captures.pcap |
-d |
tcpdump -i eth0 -d |
Display human-readable form in standard output |
-L |
tcpdump -i eth0 -L |
Display data link types for the interface |
-q |
tcpdump -i eth0 -q |
Quick/quiet output. Print less protocol information, so output lines are shorter. |
-U |
tcpdump -i eth0 -U -w out.pcap |
Without -w option Print a description of each packet’s contents. With -w option Write each packet to the output file out.pcap in real time rather than only when the output buffer fills. |
| Operator | Syntax | Example | Description |
|---|---|---|---|
AND |
and, && |
tcpdump -n src 127.0.0.1 and dst port 21 |
Combine filtering options joined by “and” |
OR |
or, \| |
tcpdump dst 127.0.0.1 or src port 22 |
Match any of the conditions joined by “or” |
EXCEPT |
not, ! |
tcpdump dst 127.0.0.1 and not icmp |
Negate the condition prefixed by “not” |
LESS |
less, <, (<=) |
tcpdump dst host 127.0.0.1 and less 128 |
Shows packets shorter than (or equal to) 128 bytes in length. < only applies to length 32, i.e., <32. |
GREATER |
greater, >, (>=) |
tcpdump dst host 127.0.0.1 and greater 64 |
Shows packets longer than (or equal to) 64 bytes in length. > only applies to length 32, i.e., >32. |
EQUAL |
=, == |
tcpdump host 127.0.0.1 = 0 |
Show packets with zero length |
| Example | Explanation |
|---|---|
tcpdump -r outfile.pcap src host 10.0.2.15 |
Print all packets in the file outfile.pcap coming from the host with IP address 10.0.2.15 |
tcpdump -i any ip and not tcp port 80 |
Listen for non-HTTP packets (which have TCP port number 80) on any network interface |
tcpdump -i eth0 -n >32 -w pv01.pcap -c 30 |
Save 30 packets of length exceeding 32 bytes to captures.pcap without DNS resolution on the eth0 network interface |
tcpdump -AtuvX icmp |
Capture ICMP traffic and print ICMP packets in hex and ASCII and the following features: With: • headers • data • undecoded NFS handles Without: • link level headers • timestamps. |
tcpdump 'tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)' |
Print all IPv4 HTTP packets to and from port 80, i.e. print only packets that contain data, not, for example, SYN and FIN packets and ACK-only packets. |
一种追踪内核态和用户态的新技术
1 | bpftrace -l | grep kprobe |
跟踪 net/ipv4/netfilter/ip_tables.c looks promising 的两个函数: compat_do_ipt_get_ctl 和 do_ipt_get_ctl。
1 | ~# bpftrace -e 'kprobe:do_ipt_get_ctl { printf("function was called!\n"); }' |
compat_do_ipt_get_ctl 函数签名如下
1 | static int compat_do_ipt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) |
建立test.bpf文件 1
2
3
4
5
6#include <net/sock.h>
kprobe:do_ipt_get_ctl
{
printf("called by %s (pid: %d). and: %d\n", comm, pid, ((sock *)arg0)->__sk_common.skc_family);
}
执行结果如下
1 | ~# bpftrace test.bpf |
2 的含义解释如下
1 | /usr/src/linux-headers-4.19.0-8-common/include/linux/socket.h |
1 | bpftrace -e 'kprobe:vfs_open { printf("open path: %s\n", str(((path *)arg0)->dentry->d_name.name)); }' |
使用默认变量retval。
使用 kretprobe跟踪内核函数的返回值。例如,跟踪 vfs_read的返回值(读取的字节数或错误码): 1
bpftrace -e 'kretprobe:vfs_read { printf("vfs_read returned: %d\n", retval); }'
使用 uretprobe跟踪用户空间函数的返回值。例如,跟踪一个名为 myfunc的函数的返回值: 1
bpftrace -e 'uretprobe:/path/to/binary:myfunc { printf("myfunc returned: %d\n", retval); }'
测量函数执行时间或关联参数与返回值。这可以通过在函数入口(如 kprobe/uprobe)记录时间戳(tid是内置变量,代表线程id),然后在返回探针中计算差值来实现。例如: 1
2
3
4
5
6bpftrace -e 'kprobe:vfs_read { @start[tid] = nsecs; }
kretprobe:vfs_read /@start[tid]/ {
$duration = nsecs - @start[tid];
printf("vfs_read took %d ns, returned %d\n", $duration, retval);
delete(@start[tid]);
}'
可以在返回探针上添加过滤条件,例如只处理特定进程或返回值范围的调用。使用 /<filter>/语法。例如,只打印返回值大于0的 vfs_read调用: 1
bpftrace -e 'kretprobe:vfs_read /retval > 0/ { printf("vfs_read returned: %d\n", retval); }'
在 bpftrace 中,符号 @用于定义和操作 Map 变量(或称映射变量)。这是 bpftrace 实现高效数据聚合的核心机制,你可以把它理解为一个在内核中运行的、功能强大的迷你数据库
Map 的主要作用是在不同的事件探针(probe)之间存储、共享和聚合数据。当某个事件触发时,你可以将数据记录到 Map 中;当另一个相关事件触发时,再从中读取或更新数据。这使得实现复杂的追踪逻辑成为可能。
| 特性 | 说明 |
|---|---|
| 数据持久化 | Map 中的数据在多个探针事件之间保持存在,不像普通变量那样每次事件触发后就被重置。(全局变量) |
| 键值对结构 | Map 使用键(key)来索引值(value),格式为 @map_name[key] = value。键可以是单一值,也可以是多个值的组合(如 @a[pid, comm])。kv结构 |
| 自动输出 | 默认情况下,当 bpftrace 程序退出时(例如你按下 Ctrl-C),所有非空的 Map 内容会自动打印到屏幕上。 |
| 操作/函数 | 说明 | 示例 |
|---|---|---|
| 赋值 | 直接给 Map 赋值。 | @start_time[tid] = nsecs(记录线程的开始时间) |
计数 (count()) |
统计事件发生的次数。 | @syscall_count[comm] = count()(统计每个进程名的系统调用次数) |
求和 (sum()) |
对数值进行累加。 | @total_bytes[pid] = sum(args->ret)(累计每个进程读取的总字节数) |
统计 (avg(), min(), max()) |
计算平均值、最小值、最大值。 | @response_time = avg($latency) |
直方图 (hist()) |
非常实用,生成2的幂次方的直方图,直观展示数据分布。 | @latency_ns = hist(nsecs - @start[tid])(可视化读操作的延迟分布) |
线性直方图 (lhist()) |
生成自定义区间的线性直方图。 | @read_sizes = lhist(args->ret, 0, 10000, 1000)(统计读取大小的分布) |
数据清理 (delete()) |
从 Map 中删除特定的键值对,防止内存无限增长。 | delete(@start_time[tid])(在处理完一个事件后清理对应的开始时间) |
| 变量类型 | 前缀 | 作用域与用途 |
|---|---|---|
| Map 变量 | @ |
全局。用于在探针之间持久化存储和聚合数据。 |
| 内置变量 | 无 | 只读。提供事件上下文信息,如 pid(进程ID)、comm(命令名)、retval(函数返回值)等。 |
| 暂存变量 | $ |
局部临时。用于单次探针触发过程中的中间计算,例如 $duration = nsecs - @start[tid]。 |
| 函数原型 | 核心作用与参数说明 | 典型应用场景 |
|---|---|---|
count() |
计数。统计事件被触发的次数。无参数。 | 统计系统调用次数、函数调用次数等。 |
sum(int n) |
求和。对参数 n的值进行累加。 |
计算总的字节读写量、总耗时等。 |
avg(int n) |
求平均值。计算参数 n的平均值。 |
计算平均延迟、平均数据包大小等。 |
min(int n) |
求最小值。记录参数 n的最小值。 |
追踪最小延迟、最小数据块大小。 |
max(int n) |
求最大值。记录参数 n的最大值。 |
追踪最大延迟、最大数据块大小。 |
stats(int n) |
统计摘要。返回参数 n的计数、平均值、总和、最小值、最大值。 |
获取一个指标的全面统计信息。 |
hist(int n) |
对数直方图。按2的幂次方区间(如 [4-8), [8-16))展示参数 n的分布。 |
直观展示延迟、数据大小的分布情况,易于发现模式。 |
lhist(int n, int min, int max, int step) |
线性直方图。在指定的线性区间(min到max,步长为step)内展示参数 n的分布。 |
当需要自定义固定区间进行分析时使用。 |
delete(@m[key]) |
删除键值对。从 Map @m中删除指定的 key及其对应的值。 |
清理临时数据,防止 Map 无限增长,常用于配对探针(如 kprobe/kretprobe)。 |
clear(@m) |
清空 Map。清除 Map @m中的所有键值对。 |
在定时器(如 interval)中定期重置统计。 |
zero(@m) |
归零 Map。将 Map @m中所有键的值重置为 0。 |
重置计数或求和等数据,但保留键的结构。 |
统计每个进程调用的系统调用次数
bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[comm] = count(); }'
输出 1
2
3@count[bash]: 15
@count[sshd]: 28
@count[snmpd]: 102
累计所有进程通过 read 系统调用成功读取的字节数
bpftrace -e 'tracepoint:syscalls:sys_exit_read /args->ret > 0/ { @bytes = sum(args->ret); }'
输出 1
@bytes: 1048576
计算每次 read系统调用成功读取的平均字节数。
bpftrace -e 'tracepoint:syscalls:sys_exit_read /args->ret > 0/ { @avg_size = avg(args->ret); }'
输出 1
@avg_size: 512
对 read系统调用的返回值进行全面的统计。
bpftrace -e 'tracepoint:syscalls:sys_exit_read /args->ret > 0/ { @s = stats(args->ret); }'
输出 1
@s: count 100, average 4096, total 409600, min 1, max 8192
显示 read系统调用返回值的分布,区间按2的幂次方划分。
bpftrace -e 'tracepoint:syscalls:sys_exit_read { @bytes = hist(args->ret); }'
输出 1
2
3
4
5
6@bytes:
[0, 1] 12 |@@@@@@@@@@@@@@@@@@@@ |
[2, 4) 18 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ |
[4, 8) 0 | |
...
[128, 256) 1 |@
使用线性直方图统计 read返回值,范围从0到2000,步长为200。
bpftrace -e 'tracepoint:syscalls:sys_exit_read { @bytes = lhist(args->ret, 0, 2000, 200); }'
输出 1
2
3
4
5
6@bytes:
(..., 0) 0 | |
[0, 200) 66 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|
[200, 400) 2 |@ |
...
[2000, ...) 39 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ |
在计算函数耗时后,删除用于存储开始时间的临时键,避免 Map 无限增长。 1
2
3
4
5
6bpftrace -e 'kprobe:vfs_read
{ @start[tid] = nsecs; }
kretprobe:vfs_read /@start[tid]/
{ $duration_ns = nsecs - @start[tid];
@us = hist($duration_ns);
delete(@start[tid]); }'
kprobe和 kretprobe),在操作完成后及时清理资源每5秒打印并清空一次系统调用计数 1
bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[comm] = count(); } interval:s:5 { print(@); clear(@); }'
1
2
3
4
5
6
7
8@[Relay(89295)]: 3
@[mini_init]: 4
@[gmain]: 5
@[Relay(909)]: 6
@[Relay(893)]: 6
@[systemd-udevd]: 37
@[chronyd]: 40
@[bpftrace]: 118
clear(@m)会删除 Map 中的所有键值对。而 zero(@m)则将所有键的值重置为0,但保留键的结构 1 | bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[comm] = count(); } interval:s:5 { print(@); zero(@); }' |
1 | @[gmain]: 5 |
Ctrl-C),所有非空的 Map 变量会自动打印出来。/<filter>/可以设置条件,只有满足条件时才会执行后面的动作,这能有效提升脚本效率和输出内容的针对性comm, pid, nsecs)或临时变量(以 $开头)结合使用,以实现复杂的追踪逻辑