目录

4.12 磁盘动态追踪

本节我们来介绍磁盘的动态追踪技术

1. Systemtab 磁盘追踪

Dtrace/Systemtab 能从内核角度检查磁盘 I/O 时间,包括:

  1. 块设备接口 I/O
  2. I/O 调度器事件
  3. 目标驱动 I/O
  4. 设备驱动 I/O

1.1 Dtrace

下面列出了用来跟踪磁盘 I/O 的Dtrace provider

层次 稳定 provider 不稳定 provider
应用程序 取决于应用 pid
系统库 pid
系统调用 syscall
VFS fsinfo fbt
文件系统 fbt
块设备接口 io fbt
目标驱动 fbt
设备驱动 fbt

io provider

io provider 使得外界可以从块设备几口的角度进行观察,以支持工作负载特征归纳和延时分析。其提供了如下的探测器:

  1. io:::start: 一个I/O请求被发到设备上
  2. io::🔚 一个 I/O请求在设备上完成(完成中断)
  3. io:::wait-start: 一个线程开始等待一个 I/O 请求
  4. io:::wait-down: 一个线程等完了一个 I/O 请求

探测器有一些稳定的参数提供 I/O 的详细信息,如下所示:

语法 作用 说明
args[0]->b_count I/O 大小(字节数)
args[0]->b_blkno 设备 I/O 偏移量(块)
args[0]->b_flags 位元标志位,包括表示读I/O的B_READ
args[0]->b_error 错误状态
args[0]->dev_statname 设备实例名+实例/小编号
args[0]->dev_pathname 设备路径名
args[0]->fi_pathname 文件系统名
args[0]->fi_fs 文件系统类型

I/O方向(读写) 可以使用表达式 args[0]->b_flags & B_READ ? "read": "write"

1.2 Systemtap

Systemtap 提供了用于跟踪磁盘 I/O 的 ioblock.stp tapset,其包含了下面这些探针:

DTrace Systemtap 描述
io:::start ioblock.request
io:::done ioblock.end

ioblock.request

ioblock.request 内置了下面的变量来提供 I/O 的详细信息。

变量 内容
name probe point 名称
devname 设备名称
ino inode number of the mapped file
sector beginning sector for the entire bio
flags see below
BIO_UPTODATE 0 ok after I/O completion
BIO_RW_BLOCK 1 RW_AHEAD set, and read/write would block
BIO_EOF 2 out-out-bounds error
BIO_SEG_VALID 3 nr_hw_seg valid
BIO_CLONED 4 doesn’t own data
BIO_BOUNCED 5 bio is a bounce bio
BIO_USER_MAPPED 6 contains user pages
BIO_EOPNOTSUPP 7 not supported
rw binary trace for read/write request
vcnt bio vector count which represents number of array element (page, offset, length)
which make up this I/O request
idx offset into the bio vector array
phys_segments number of segments in this bio after physical address coalescing is performed
hw_segments number of segments after physical and DMA remapping hardware
coalescing is performed
size total size in bytes
bdev target block device
bdev_contains points to the device object which contains the partition
(when bio structure represents a partition)
p_start_sect points to the start sector of the partition structure of the device

ioblock.end

除了上面这些变量,ioblock.end 还提供了下面这些变量来提供I/O的结果

变量 内容
bytes_done number of bytes transferred
error 0 on succes

2. 磁盘跟踪示例

下面的 Dtrace 脚本位于 https://github.com/opendtrace/toolkit/blob/master/Disk/seeksize.d

2.1 事件跟踪

以下跟踪的是每一个磁盘I/O请求

1
2
3
4
5
# dtrace:
> dtrace -n 'io:::start {printf("%d %s %d", pid, execname, args[0]->b_bcount);}'

# stap
> stap -ve 'probe ioblock.request {printf("%d %s %d\n", pid(), execname(), size)}'

2.2 按照应用程序名汇总磁盘 I/O 大小

1
2
3
4
5
6
# dtrace:
> dtrace -n 'io:::start {@[execname]=quantize(arg[0]->b_bcount)}'

# stap
> stap -ve 'global s;probe ioblock.request {s[execname()] <<< size}
                     probe end {foreach (k in s){printf("%s\n", k);print(@hist_log(s[k]))};}'

2.3 I/O 寻道汇总

下面的脚本跟踪同一应用程序,同一设备的连续 I/O 之间寻道距离,按照进程输出直方图。

dtrace 脚本

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#!/usr/sbin/dtrace -s
dtrace:::BEGIN
{
	printf("Tracing... Hit Ctrl-C to end.\n");
}

self int last[dev_t];

/*
 * Process io start
 */
io:genunix::start
/self->last[args[0]->b_edev] != 0/
{
	/* calculate seek distance */
	this->last = self->last[args[0]->b_edev];
	this->dist = (int)(args[0]->b_blkno - this->last) > 0 ?
	    args[0]->b_blkno - this->last : this->last - args[0]->b_blkno;

	/* store details */
	@Size[pid, curpsinfo->pr_psargs] = quantize(this->dist);
}

io:genunix::start
{
	/* save last position of disk head */
	self->last[args[0]->b_edev] = args[0]->b_blkno +
	    args[0]->b_bcount / 512;
}

/*
 * Print final report
 */
dtrace:::END
{
	printf("\n%8s  %s\n", "PID", "CMD");
	printa("%8d  %S\n%@d\n", @Size);
}

stap 脚本

未完成

2.3 I/O 延时汇总

下面的脚本跟踪块I/O开始和结束的时间

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# dtrace
> dtrace -n 'io:::start 
             {start[arg0] = timestamp;}
             io:::done /start[arg0]/ 
             {@["block I/O(ns)"] = quantize(timestamp - start[arg0]);start[arg0] = 0}'

# stap
> stap -ve 'global t,s; 
            probe ioblock.request 
            {t[$bio] = gettimeofday_ns();}
            probe ioblock.end
            {if (t[$bio])
                {s <<< gettimeofday_ns() - t[$bio];delete t[$bio];}
            }
           probe end{print(@hist_log(s))}'

3.高级工具

磁盘常用的高级跟踪脚本:

  1. DTrace: https://github.com/opendtrace/toolkit
  2. Systemp: https://sourceware.org/systemtap/SystemTap_Beginners_Guide/mainsect-disk.html