关于io:Linux服务器开发监控之-IO

简介

能够通过如下命令查看与 IO 相干的零碎信息。

tune2fs -l /dev/sda7 ← 读取superblock信息 # blockdev --getbsz /dev/sda7 ← 获取block大小

tune2fs -l /dev/sda7 | grep "Block size" ← 同上 # dumpe2fs /dev/sda7 | grep "Block size" ← 同上 # stat /boot/ | grep "IO Block" ← 同上 # fdisk -l ← 硬盘的扇区大小(Sector Size

)

在 WiKi 中的定义:A “block”, a contiguous number of bytes, is the minimum unit of memory that is read from and written to a disk by a disk driver。

块是文件系统的形象,而非磁盘的属性,个别是 Sector Size 的倍数;扇区大小则是磁盘的物理属性,它是磁盘设施寻址的最小单元。另外,内核中要求 Block_Size = Sector_Size * (2的n次方),且 Block_Size <= 内存的 Page_Size (页大小)。

【文章福利】小编举荐本人的C/C++Linux群:832218493!整顿了一些集体感觉比拟好的学习书籍、视频材料共享在外面,有须要的能够自行添加哦!~

磁盘应用空间

实际上是通过 statvfs() 办法查问磁盘数据,能够通过如下命令查看。

$ python -c 'import os; os.statvfs("/")'

其空间占用大抵如下。

   +--------------------------+----------------+-------------------------------------------------------+
   |                          |                |                                                       |
   +--------------------------+----------------+-------------------------------------------------------+
   |<-- f_bavail(non-root) -->|<-- reserved -->|<------------- f_bused=f_blocks-f_bfree -------------->|
   |<------------- f_bfree(root) ------------->|                                                       |
   |<----------------------------------------- f_blocks ---------------------------------------------->|

前者是非 root 用户曾经应用的占非 root 用户可用空间百分数;后者是保留给 root 用户以及曾经应用磁盘占整个磁盘空间百分数。对于 extN 类的文件系统个别会保留 1%~5% 的磁盘空间给 root 应用,当 reserved 占比拟大时会导致两者的计算差较大。

磁盘类型

次要是要获取以后零碎应用的什么类型的磁盘 (SCSI、IDE、SSD等),甚至是制造商、机器型号、序列号等信息。

$ dmesg | grep scsi

监控指标

简略列举磁盘监控时常见的指标。

IOPS 每秒IO数
  对磁盘来说,一次磁盘的间断读或写称为一次磁盘 IO,当传输小块不间断数据时,该指标有重要参考意义。
Throughput 吞吐量
  硬盘传输数据流的速度,单位个别为 MB/s,在传输大块不间断数据的数据,该指标有重要参考作用。
IO均匀大小
  实际上就是吞吐量除以 IOPS,用于判断磁盘应用模式,个别大于 32K 为程序读取为主,否则随机读取为主。
Utilization 磁盘流动工夫百分比
  磁盘处于活动状态 (数据传输、寻道等) 的工夫百分比,也即磁盘利用率,个别该值越高对应的磁盘资源争用越高。
Service Time 服务工夫
  磁盘读写操作执行的工夫,对于机械磁盘包含了寻道、旋转、数据传输等,与磁盘性能相关性较高,另外,也受 CPU、内存影响。
Queue Length 期待队列长度
  待处理的 IO 申请的数目,留神,如果该磁盘为磁盘阵列虚构的逻辑驱动器,须要除以理论磁盘数,以获取单盘的 IO 队列。
Wait Time 等待时间
  在队列中排队的工夫。

iostat 零碎级

除了能够通过该命令查看磁盘信息之外,还能够用来查看 CPU 信息,别离通过 -d 和 -c 参数管制;可间接通过 iostat -xdm 1 命令显示磁盘队列的长度等信息。

Device: rrqm/s wrqm/s   r/s   w/s  rMB/s  wMB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
sda       0.02   1.00  0.99  1.84   0.03   0.04    46.98     0.01  2.44    0.47    3.49  0.25

0.07
其中参数如下:

rrqm/s wrqm/s
  读写申请每秒合并后发送给磁盘的申请。

r/s w/s
  利用发送给零碎的申请数目。

argrq-sz
  提交给驱动层IO申请的均匀大小(sectors),个别不小于4K,不大于max(readahead_kb, max_sectors_kb);
  可用于判断以后的 IO 模式,越大代表程序,越小代表随机;计算公式如下:
  argrq-sz = (rsec + wsec) / (r + w)

argqu-sz Average Queue Size
  在驱动层的队列排队的均匀长度。

await Average Wait
  均匀的等待时间,包含了在队列中的等待时间,以及磁盘的解决工夫。

svctm(ms) Service Time
  申请发送给IO设施后的响应工夫,也就是一次磁盘IO申请的服务工夫,不过该指标官网说不精确,要勾销。
  对于单块SATA盘,齐全随机读时,根本在7ms左右,既寻道+旋转延迟时间。

%util
  一秒内IO操作所占的比例,计算公式是(r/s+w/s)*(svctm/1000),例如采集工夫为 1s 其中有 0.8s 在处
  理 IO 申请,那么 util 为 80% ;对于一块磁盘,如果没有并发IO的概念,那么这个公式是正确的,但
  是对于RAID磁盘组或者SSD来说,这个计算公式就有问题了,就算这个值超过100%,也不代表存储有瓶颈,
  容易产生误导


iostat 统计的是通用块层通过合并 (rrqm/s, wrqm/s) 后,间接向设施提交的 IO 数据,能够反映零碎整体的 IO 情况,然而间隔应用层比拟远,因为零碎预读、Page Cache、IO调度算法等因素,很难跟代码中的 write()、read() 对应。

简言之,这是零碎级,没方法准确到过程,比方只能通知你当初磁盘很忙,然而没方法通知你是那个过程在忙,在忙什么?

/proc/diskstats
该命令会读取 /proc/diskstats 文件,各个指标具体的含意能够参考内核文档 iostats.txt,其中各个段的含意如下。

filed1  rd_ios
  胜利实现读的总次数;
filed2  rd_merges
  合并写实现次数,通过合并提高效率,例如两次4K合并为8K,这样只有一次IO操作;合并操作是由IO Scheduler(也叫 Elevator)负责。
filed3  rd_sectors
  胜利读过的扇区总次数;
filed4  rd_ticks
  所有读操作所破费的毫秒数,每个读从__make_request()开始计时,到end_that_request_last()为止,包含了在队列中期待的工夫;
filed5  wr_ios
  胜利实现写的总次数;
filed6  wr_merges
  合并写的次数;
filed7  wr_sectors
  胜利写过的扇区总次数;
filed8  wr_ticks
  所有写操作所破费的毫秒数;
filed9  in_flight
  当初正在进行的IO数目,在IO申请进入队列时该值加1,在IO完结时该值减1,留神是在进出队列时,而非交给磁盘时;
filed10 io_ticks
  输出/输入操作破费的毫秒数;
filed11 time_in_queue
  是一个权重值,当有下面的IO操作时,这个值就减少。

须要留神 io_ticks 与 rd/wr_ticks 的区别,后者是把每一个 IO 所耗费的工夫累加在一起,因为硬盘设施通常能够并行处理多个 IO,所以统计值往往会偏大;而前者示意该设施有 IO 申请在解决的工夫,也就是非闲暇,不思考 IO 有多少,只思考当初有没有 IO 操作。在理论计算时,会在字段 in_flight 不为零的时候 io_ticks 放弃计时,为 0 时进行计时。

另外,io_ticks 在统计时不思考以后有几个 IO,而 time_in_queue 是用以后的 IO 数量 (in_flight) 乘以工夫,统计工夫包含了在队列中的工夫以及磁盘解决 IO 的工夫。

重要指标

简略介绍下常见的指标,包含了常常误会的指标。

util
这里重点说一下 iostat 中 util 的含意,该参数能够了解为磁盘在解决 IO 申请的总工夫,如果是 100% 则表明磁盘始终在解决 IO 申请,这也就意味着 IO 在满负载运行。

对于一块磁盘,如果没有并发 IO 的概念,所以这个公式是正确的,然而当初的磁盘或者对于RAID磁盘组以及SSD来说,这个计算公式就有问题了,就算这个值超过100%,也不代表存储有瓶颈,容易产生误导。

举个简化的例子:某硬盘解决单个 IO 须要 0.1 秒,也就是有能力达到 10 IOPS,那么当 10 个 IO 申请顺次程序提交的时候,须要 1 秒能力全副实现,在 1 秒的采样周期里 %util 达到 100%;而如果 10 个 IO 申请一次性提交的话,0.1 秒就全副实现,在 1 秒的采样周期里 %util 只有 10%。

可见,即便 %util 高达 100%,硬盘也依然有可能还有余力解决更多的 IO 申请,即没有达到饱和状态。不过遗憾的是当初 iostat 没有提供相似的指标。

在 CentOS 中应用的是 github sysstat,如下是其计算方法。

rw_io_stat_loop()  循环读取
 |-read_diskstats_stat()            从/proc/diskstats读取状态
 |-write_stats()                    输入采集的监控指标
   |-write_ext_stat()
     |-compute_ext_disk_stats()     计算ext选项,如util
     |-write_plain_ext_stat()

对于该参数的代码具体介绍如下。

#define S_VALUE(m,n,p)  (((double) ((n) - (m))) / (p) * HZ)

void read_diskstats_stat(int curr)
{
 struct io_stats sdev;
 ... ...
 if ((fp = fopen(DISKSTATS, "r")) == NULL)
  return;

 while (fgets(line, sizeof(line), fp) != NULL) {
  /* major minor name rio rmerge rsect ruse wio wmerge wsect wuse running use aveq */
  i = sscanf(line, "%u %u %s %lu %lu %lu %lu %lu %lu %lu %u %u %u %u",
      &major, &minor, dev_name,
      &rd_ios, &rd_merges_or_rd_sec, &rd_sec_or_wr_ios, &rd_ticks_or_wr_sec,
      &wr_ios, &wr_merges, &wr_sec, &wr_ticks, &ios_pgr, &tot_ticks, &rq_ticks);

  if (i == 14) {
   /* Device or partition */
   if (!dlist_idx && !DISPLAY_PARTITIONS(flags) &&
       !is_device(dev_name, ACCEPT_VIRTUAL_DEVICES))
    continue;
   sdev.rd_ios     = rd_ios;
   sdev.rd_merges  = rd_merges_or_rd_sec;
   sdev.rd_sectors = rd_sec_or_wr_ios;
   sdev.rd_ticks   = (unsigned int) rd_ticks_or_wr_sec;
   sdev.wr_ios     = wr_ios;
   sdev.wr_merges  = wr_merges;
   sdev.wr_sectors = wr_sec;
   sdev.wr_ticks   = wr_ticks;
   sdev.ios_pgr    = ios_pgr;
   sdev.tot_ticks  = tot_ticks;
   sdev.rq_ticks   = rq_ticks;
        }
     ... ...
  save_stats(dev_name, curr, &sdev, iodev_nr, st_hdr_iodev);
 }
 fclose(fp);
}

void write_json_ext_stat(int tab, unsigned long long itv, int fctr,
      struct io_hdr_stats *shi, struct io_stats *ioi,
      struct io_stats *ioj, char *devname, struct ext_disk_stats *xds,
      double r_await, double w_await)
{
 xprintf0(tab,
   "{\"disk_device\": \"%s\", \"rrqm\": %.2f, \"wrqm\": %.2f, "
   "\"r\": %.2f, \"w\": %.2f, \"rkB\": %.2f, \"wkB\": %.2f, "
   "\"avgrq-sz\": %.2f, \"avgqu-sz\": %.2f, "
   "\"await\": %.2f, \"r_await\": %.2f, \"w_await\": %.2f, "
   "\"svctm\": %.2f, \"util\": %.2f}",
   devname,
   S_VALUE(ioj->rd_merges, ioi->rd_merges, itv),
   S_VALUE(ioj->wr_merges, ioi->wr_merges, itv),
   S_VALUE(ioj->rd_ios, ioi->rd_ios, itv),
   S_VALUE(ioj->wr_ios, ioi->wr_ios, itv),
   S_VALUE(ioj->rd_sectors, ioi->rd_sectors, itv) / fctr,
   S_VALUE(ioj->wr_sectors, ioi->wr_sectors, itv) / fctr,
   xds->arqsz,
   S_VALUE(ioj->rq_ticks, ioi->rq_ticks, itv) / 1000.0,
   xds->await,
   r_await,
   w_await,
   xds->svctm,
   shi->used ? xds->util / 10.0 / (double) shi->used
      : xds->util / 10.0); /* shi->used should never be zero here */
}


void compute_ext_disk_stats(struct stats_disk *sdc, struct stats_disk *sdp,
       unsigned long long itv, struct ext_disk_stats *xds)
{
 double tput
  = ((double) (sdc->nr_ios - sdp->nr_ios)) * HZ / itv;

 xds->util  = S_VALUE(sdp->tot_ticks, sdc->tot_ticks, itv);
 xds->svctm = tput ? xds->util / tput : 0.0;
 /*
  * Kernel gives ticks already in milliseconds for all platforms
  * => no need for further scaling.
  */
 xds->await = (sdc->nr_ios - sdp->nr_ios)
?  ((sdc->rd_ticks - sdp->rd_ticks) + (sdc->wr_ticks - sdp->wr_ticks)) /
  ((double) (sdc->nr_ios - sdp->nr_ios)) : 0.0;
 xds->arqsz = (sdc->nr_ios - sdp->nr_ios)
?  ((sdc->rd_sect - sdp->rd_sect) + (sdc->wr_sect - sdp->wr_sect)) /
  ((double) (sdc->nr_ios - sdp->nr_ios)) : 0.0;
}

实际上就是 /proc/diskstats 中的 filed10 耗费工夫占比。

await

在 Linux 中,每个 IO 的均匀耗时用 await 示意,包含了磁盘解决工夫以及队列排队工夫,所以该指标不能齐全示意设施的性能,包含 IO 调度器等,都会影响该参数值。一般来说,内核中的队列工夫简直能够忽略不计,而 SSD 不同产品从 0.01ms 到 1.00 ms 不等,对于机械磁盘能够参考 io 。

svctm

这个指标在 iostat 以及 sar 上都有正文 Warning! Do not trust this field any more. This field will be removed in a future sysstat version.,该指标包含了队列中排队工夫以及磁盘解决工夫。

实际上,在 UNIX 中通常通过 avserv 示意硬盘设施的性能,它是指 IO 申请从 SCSI 层收回到 IO 实现之后返回 SCSI 层所耗费的工夫,不包含在 SCSI 队列中的等待时间,所以该指标体现了硬盘设施解决 IO 的速度,又被称为 disk service time,如果 avserv 很大,那么必定是硬件出问题了。

iowait

从 top 中的解释来说,就是 CPU 在 time waiting for I/O completion 中耗费的工夫,而实际上,如果须要期待 IO 实现,理论 CPU 不会始终期待该过程,而是切换到另外的过程继续执行。

所以在 Server Fault 中将该指标定义为如下的含意:

iowait is time that the processor/processors are waiting (i.e. is in an
idle state and does nothing), during which there in fact was outstanding
disk I/O requests.

那么对于多核,iowait 是只在一个 CPU 上,还是会耗费在所有 CPU ?如果有 4 个 CPUs,那么最大是 20% 还是 100% ?

能够通过 dd if=/dev/sda of=/dev/null bs=1MB 命令简略测试下,一般来说,为了进步 cache 的命中率,会始终应用同一个 CPU ,不过局部零碎会将其均分到不同的 CPU 上做平衡。另外,也能够通过 taskset 1 dd if=/dev/sda of=/dev/null bs=1MB 命令将其绑定到单个 CPU 上。

依照二进制模式,从最低位到最高位代表物理 CPU 的 #0、#1、#2、…、#n 号核,例如:0x01 代表 CPU 的 0 号核,0x05 代表 CPU 的 0 号和 2 号核。
例如,将 9865 绑定到 #0、#1 下面,命令为 taskset -p 0x03 9865;将过程 9864 绑定到 #1、#2、#5~#11 号核下面,从 1 开始计数,命令为 taskset -cp 1,2,5-11 9865 。

能够看出,如果是 top <1> 显示各个 CPU 的指标,则是 100% 计算,而总的统计值则依照 25% 统计。

其它 常见问题解决。

问题1:如何获取真正的 serviice time(svctm)
?能够通过 fio 等压测工具,通过设置为同步 IO,仅设置一个线程,以及 io_depth 也设置为 1,压测进去的就是真正的 service time(svctm)。

问题2:怎么取得 IO 最大并行度,或者说如何取得真正的 util% 使用率?
最大并行度 = 压测满(r/s + w/s) * (实在svctm / 1000)
公式根本一样,只是将 svctm 换成了上次计算的值。

问题3:如何判断存在 IO 瓶颈了?
实际上在如上算出实在的最大并行度,能够间接参考 avgqu-sz 值,也就是队列中的值,一般来说超过两倍可能就会存在问题。例如一块机械盘,串行 IO (每次1个IO),那么 avgqu-sz 继续大于 2 既代表继续有两倍读写能力的 IO 申请在期待;或者当 RAIDs、SSD 等并行,这里假设并行度为 5.63,那么 avgqu-sz 继续大于10,才代表继续有两倍读写能力的 IO 申请在期待。

iotop pidstat iodump 过程级
一个 Python 脚本,能够查看官网 guichaz.free.fr/iotop,另一个通过 C 实现的监控可参考 IOPP。

pidstat 用于统计过程的状态,包含 IO 情况,能够查看以后零碎哪些过程在占用 IO 。

—– 只显示IO

pidstat -d 1

上述两者均是统计的 /proc/pid/io 中的信息;另可参考 io/iotop.stp,这是 iotop 的复制版。

iodump 是一个统计每一个过程(线程)所耗费的磁盘 IO 工具,是一个 perl 脚本,其原理是关上无关 IO 的内核记录音讯开关,而后读取音讯而后剖析输入。

echo 1 >/proc/sys/vm/block_dump # 关上无关IO内核音讯的开关 # while true; do sleep 1; dmesg -c ; done | perl iodump # 而后剖析

上述输入的单位为块 (block),每块的大小取决于创立文件系统时指定的块大小。

ioprofile 业务级

ioprofile 命令实质上等价于 lsof + strace,能够查看以后过程。

blktrace
blktrace 是块层 IO 门路监控和剖析工具,作者 Jens Axboe 是内核 IO 模块的维护者,目前就任于 FusionIO,同时他还是驰名 IO 评测工具 fio 的作者,应用它能够深刻理解 IO 通路。

yum install blktrace # 在CentOS中装置

$ make # 解压源码后间接装置
$ man -l doc/blktrace.8 # 查看帮忙

$ grep 'CONFIG_BLK_DEV_IO_TRACE' /boot/config-`uname -r`
大部分实现代码都在 blktrace.c,利用 tracepoint 的个性,注册了一些 trace 关键点,能够查看 Documentation/tracepoint.txt 文件;交互机制利用了 relayfs 个性,看看 Documentation/filesystems/relay.txt 

其源码能够从 brick.kernel.dk 下载,具体应用参考 blktrace User Guide 原理

该工具包含了内核空间和用户空间两局部实现,内核空间里次要是给块层 IO 门路上的关键点增加 tracepoint,而后借助于 relayfs 零碎个性将收集到的数据写到 buffer 去,再从用户空间去收集。

目前,内核空间局部的代码曾经集成到主线代码外面去了,能够看看内核代码 block/blktrace.c 文件是不是存在,编译的时候把对应的这个 trace 选项抉择上就能够了。

此时捞取的信息还比拟原始,能够通过用户空间的 blkparse、btt、seekwatcher 这样的工具来剖析收集到的数据。

留神,应用之前要确保 debugfs 曾经挂载,默认会挂载在 /sys/kernel/debug 。

应用
典型的应用如下,其中 /dev/sdaa、/dev/sdc 作为 LVM volume adb3/vol。

blktrace -d /dev/sda -o - | blkparse -i - -o blkparse.out # 简略用法,Ctrl-C退出 # btrace /dev/sda # 同上 # blktrace /dev/sdaa /dev/sdc & # 离线解决。1. 后盾运行采集

% mkfs -t ext3 /dev/adb3/vol                                      # 2. 做些IO操作
% kill -15 9713                                                   # 3. 进行采集
% blkparse sdaa sdc sdo > events                                  # 4. 解析后查看

在 blktrace 中,-d 示意监控哪个设施,-o – 示意将监控输入到规范输入;在 blkparse 中,-i – 示意从规范输出获取信息,-o 示意将解析的内容记录在 blkparse.out 。

如下是输入的详细信息。

其中 event 对应了事件表;前面一列代表了操作类型,包含了 R(read)、W(write)、B(barrier operation)、S(synchronous operation),其中 event 有如下类型:

详解
仍以如下简略命令为例。

$ blktrace -d /dev/sda -o sda                 # 输入 sda.blktrace.N 文件,N 为物理 CPU 个数。
$ ls /sys/kernel/debug/block/sda              # 查看debugfs中的文件
dropped  msg  trace0  trace1  trace2  trace3
$ blkparse -i sda.blktrace.0                  # 解析成可读内容
$ blkrawverify sda                            # 校验,其中sda为blktrace的-o选项

其中 blktrace 通过 ioctl() 执行 BLKTRACESETUP、BLKTRACESTART、BLKTRACESTOP、BLKTRACETEARDOWN 操作,此时会在 debugfs 目录的 block/DEV 目录下写入数据。

FIO
FIO 是个十分弱小的 IO 性能测试工具,其作者 Jens Axboe 是 Linux 内核 IO 局部的 maintainer,能够毫不夸大的说,如果你把所有的 FIO 参数都搞明确了,基本上就把 Linux IO 协定栈的问题搞的差不多明确了。

一个 IO 压测工具,源码以及二进制文件能够参考 github-axboe,或者间接从 freecode.com 上下载。另外,该工具同时提供了一个图形界面 gfio 。

在 CentOS 中能够通过如下形式装置。

yum --enablerepo=epel install fio

源码编译
能够间接从 github 上下载源码,而后通过如下形式进行编译。

—– 编译,留神依赖libaio

$ make

—– 查看帮忙

$ man -l fio.1

—– 通过命令行指定参数,进行简略测试

$ fio --name=global --rw=randread --size=128m --name=job1 --name=job2

—– 也能够通过配置文件进行测试

$ cat foobar.fio
[global]
rw=randread
size=128m
[job1]
[job2]
$ fio foobar.fio

能够通过命令行启动,不过此时参数较多,能够应用配置文件。

源码解析
其版本通过 FIO_VERSION 宏定义,并通过 fio_version_string 变量定义。

main()
  |-parse_options()
  |  |-parse_cmd_line()                    解析命令行,如-i显示所有的ioengines
  |  |  |-add_job()                        file1: xxxxxx 打印job信息
  |  |-log_info()                          fio-2.10.0
  |-fio_backend()
  |  |-create_disk_util_thread()           用于实时显示状态
  |  |  |-setup_disk_util()
  |  |  |-disk_thread_main()               通过pthread创立线程
  |  |     |-print_thread_status()
  |  |
  |  |-run_threads()                       Starting N processes
  |  |  |-setup_files()                    Laying out IO file(s)
  |  |  |-pthread_create()                 如果配置应用线程,调用thread_main
  |  |  |-fork()                           或者调用创立过程,同样为thread_main
  |  |
  |  |-show_run_stats()
  |     |-show_thread_status_normal()      用于显示最终的状态
  |        |-show_latencies()              显示lat信息
  |        |-... ...                       CPU、IO depth
ioengines 通过 fio_libaio_register() 相似的函数初始化。

其它

ionice
获取或设置程序的 IO 调度与优先级。

ionice [-c class] [-n level] [-t] -p PID...
ionice [-c class] [-n level] [-t] COMMAND [ARG]

----- 获取过程ID为89、91的IO优先级
$ ionice -p 89 9

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理