鱒身(Masu_mi)のブログ

知った事をメモする場所。

/proc/[pid]/io でプロセス個別でI/O速度を測る

サーバ全体のI/Oの使用状況を確認するのには幾つか方法がある。 個別のプロセスのI/O情報を稼働中のサービス内で追いたい場合、デバッガ・プロファイラを走らせる様な重そうな方法以外に手軽な方法が見つからなかった。 作った後にpidstat -d -p [pid] 1 で目的を果たせる事に気付いた。 pidstat は後で調べます。

もしかしたらstrace -c でシステムコールにかかった時間を追う方法は筋が良いかも知れない。 とりあえず/proc/[pid]/io が使えそうだったので整形する簡単なコマンドを作ってみた。

プロセス個別にI/O速度を測るコマンド

プロセス個別の情報を知りたいときは /proc/[pid/ 以下を確認すれば良い。 man 5 proc で調べられるが、環境のmanが古くて /proc/[pid]/io の節が存在しなかったので次のサイトを読んだ。 ref. http://linuxjm.sourceforge.jp/html/LDP_man-pages/man5/proc.5.html

これによると以下が書かれていた。

rchar 読み出し文字数(端末とかも含まれるのでディスクデバイスかは不明)
wchar 書き込み文字数(端末とかも含まれるのでディスクデバイスかは不明)
syscr 読み出しシステムコール数
syscw 書き込みシステムコール数
read_bytes 読み出しバイト数(ストレージ層から取得しようとしたバイト数)
write_bytes 書き込みバイト数(ストレージ層から転送しようとしたバイト数)
cancelled_write_bytes キャンセルされた書き込みバイト数(ページキャッシュのトランケートにより引き起こされる)

プロセスの総転送量ではなく速度が知りたかったので定期的に取得して差を計算するだけで欲しい値が取れる。 下みたいな簡易のコマンドで秒間の速度が測れる。今はもう少し書き換えてJSONでも吐ける様にした。 https://github.com/masu-mi/ioprobe

#!/usr/bin/env python2.7
# coding: utf-8
import os, time, subprocess, re, argparse

def main(pid):
  io_sum = fetch_io(pid)
  pre_val = dict(io_sum)
  order = convert_to_order(io_sum)
  print header(order)
  while True:
    time.sleep(1)
    cur_val = dict(fetch_io(pid))
    print delta(pre_val, cur_val, order)
    pre_val = cur_val

def header(order):
  return "\t".join([term+"/s" for term in order])
def delta(pre, cur, order):
  return "\t".join((str(cur[key] - pre[key]) for key in order))

def convert_to_order(io_sum):
  return [pair[0] for pair in io_sum]
def fetch_io(pid):
  with open(os.path.join("/", "proc", pid, "io")) as f:
    io_sum = [(pair[0], int(pair[1]))
      for pair in (line.rstrip().split(":")
      for line in f.readlines())]
  return io_sum

if __name__ == "__main__":
  parser = argparse.ArgumentParser(description='I/O probe for process.')
  parser.add_argument('pid', metavar='pid', type=int, help='target process\'s pid.')
  args = parser.parse_args()
  main(str(args.pid))

サーバ全体のI/Oを確認する方法

vmstatはサーバのプロセスの稼働状態全体の経過を追う

仮想メモリの情報としてブロックデバイスとのI/Oも含まれる。以下 man から転載。

$ vmstat -n 1
procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 0  0     68 599012 192496 954952    0    0     0     0    0    1  0  0 100  0  0
 0  0     68 599004 192496 954952    0    0     0     0   44   16  0  0 100  0  0
 0  0     68 599004 192496 954952    0    0     0     0   39   24  0  0 100  0  0
io.bo
ブロックデバイスから受け取ったブロック数[blocks/sec]
io.bo
ブロックデバイスに送ったブロック数[blocks/sec]

sar -d でディスク情報を詳細に出力する

sarはシステム情報の何でも屋で-d オプションでディスク情報を出してくれる。

$ sar -d 1
Linux 2.6.xx-xxx.xxx.xx (xxxx.xx.xx.)  2015年02月22日  _x86_64_        (3 CPU)

17:00:08          DEV       tps  rd_sec/s  wr_sec/s  avgrq-sz  avgqu-sz     await     svctm     %util
17:00:09     dev252-0      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00

17:00:09          DEV       tps  rd_sec/s  wr_sec/s  avgrq-sz  avgqu-sz     await     svctm     %util
17:00:10     dev252-0      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
DEV ブロックデバイスを指していて devX-Y(X: メジャー番号, Y: マイナー番号)となる
tps 1秒間に実際にデバイスに発行される転送要求数(論理的に複数要求が1件にまとめられる場合もあり)
rd_sec/s 1秒間の読み込みセクタ数(1セクタ: 512Byte)
wr_sec/s 1秒間の読み込みセクタ数(1セクタ: 512Byte)
avgrq-sz デバイスに発行された要求の平均サイズ(セクタ数)
avgqu-sz 発行された転送要求のキュー長
await デバイスに発行された転送要求の完了までのレイテンシ(キューイングタイムも含む)
svctm 実際に転送に掛かった時間
%util 転送要求をデバイスに発行しているCPU時間の割合

他のサーバリソースを確認するコマンド

iostat, dstat, iotop がある。ちなみにdstat, iotop はPython製のツール。

iostat CPU, デバイス・パーティッション・NFSへのI/Oの統計情報を表示する
dstat サーバの統計情報をだいたい素敵に表示してくれる
iotop topコマンド風I/O監視ツール