2017年5月6日土曜日

iotopが意外といいんじゃないか

IOのread、writeがプロセスごとに取れる


ゴールデンウィーーークもそろそろ終了、アルチューハイマーになったこの体を元にもどすのに、リハビリが必要な時期に...どうでもいいですが...

いきなり話、違う話
iostatとかでも取れるんだけど、デバイスごとのIOの統計が上がってくるので、もうちょい細かいレイヤーでそれが見たいときがある。なんか無いかとportageツリーをさまよっていたら、iotopなるものをを発見。

カーネルのオプションが多分必要なので、確認してね
CONFIG_TASK_IO_ACCOUNTING
CONFIG_TASK_DELAY_ACCT
CONFIG_TASKSTATS
CONFIG_VM_EVENT_COUNTERS

で、iotopをインストール

cuomo@ugui7 ~ $ emerge sys-process/iotop

暇なので、kernelの方も覗いてみた


触りだけですが、興味本位に覗いてみました。
やっぱりプロセスごとに取れるのでtask_struct構造体に「struct task_io_accounting」がぶら下がっていて
struct task_struct {
..
..
        struct task_io_accounting ioac;
..
..
};

struct task_io_accounting {
#ifdef CONFIG_TASK_XACCT
        /* bytes read */
        u64 rchar;
        /*  bytes written */
        u64 wchar;
        /* # of read syscalls */
        u64 syscr;
        /* # of write syscalls */
        u64 syscw;
#endif /* CONFIG_TASK_XACCT */

#ifdef CONFIG_TASK_IO_ACCOUNTING
        /*
         * The number of bytes which this task has caused to be read from
         * storage.
         */
        u64 read_bytes;

        /*
         * The number of bytes which this task has caused, or shall cause to be
         * written to disk.
         */
        u64 write_bytes;

        /*
         * A task can cause "negative" IO too.  If this task truncates some
         * dirty pagecache, some IO which another task has been accounted for
         * (in its write_bytes) will not be happening.  We _could_ just
         * subtract that from the truncating task's write_bytes, but there is
         * information loss in doing that.
         */
        u64 cancelled_write_bytes;
#endif /* CONFIG_TASK_IO_ACCOUNTING */
};

これが、プロセスごとに集計されるIOの情報を管理する構造体らしい、「read_bytes」と「write_bytes」がそれ

これが特定の処理を通ったときに加算されていくらしい
*** linux-4.10.11-gentoo/fs/block_dev.c:
__blkdev_direct_IO_simple[242] task_io_account_write(ret);
__blkdev_direct_IO[377]        task_io_account_write(bio->bi_iter.bi_size);

*** linux-4.10.11-gentoo/fs/direct-io.c:
submit_page_section[800]       task_io_account_write(len);

*** linux-4.10.11-gentoo/fs/iomap.c:
iomap_dio_actor[812]           task_io_account_write(bio->bi_iter.bi_size);

*** linux-4.10.11-gentoo/fs/nfs/direct.c:
nfs_file_direct_write[1003]    task_io_account_write(count);

*** linux-4.10.11-gentoo/mm/page-writeback.c:
account_page_dirtied[2436]     task_io_account_write(PAGE_SIZE);

ディスクの書込みだけ抜粋してみた、該当するプロセスがページを書いたり、nfsへ書いたりした場合に、加算されているようです。

nfsの場合って、ネットワーク越しのディスクに書き込んだ場合は、書込みバイト数として加算しない?
などと、思ったりしてます、試してませんので分かりません。

/proc で確認する


* /fs/proc/base.c:
...
def CONFIG_TASK_IO_ACCOUNTING
        ONE("io",       S_IRUSR, proc_tid_io_accounting)

#ifdef CONFIG_TASK_IO_ACCOUNTING
        ONE("io",       S_IRUSR, proc_tid_io_accounting),
...

で定義されてて、seq_printf経由で確認できる
static int do_io_accounting(struct task_struct *task, struct seq_file *m, int whole)
{
        struct task_io_accounting acct = task->ioac;
        unsigned long flags;
        int result;

        result = mutex_lock_killable(&task->signal->cred_guard_mutex);
        if (result)
                return result;

        if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) {
                result = -EACCES;
                goto out_unlock;
        }

        if (whole && lock_task_sighand(task, &flags)) {
                struct task_struct *t = task;

                task_io_accounting_add(&acct, &task->signal->ioac);
                while_each_thread(task, t)
                        task_io_accounting_add(&acct, &t->ioac);

                unlock_task_sighand(task, &flags);
        }
        seq_printf(m,
                   "rchar: %llu\n"
                   "wchar: %llu\n"
                   "syscr: %llu\n"
                   "syscw: %llu\n"
                   "read_bytes: %llu\n"
                   "write_bytes: %llu\n"
                   "cancelled_write_bytes: %llu\n",
                   (unsigned long long)acct.rchar,
                   (unsigned long long)acct.wchar,
                   (unsigned long long)acct.syscr,
                   (unsigned long long)acct.syscw,
                   (unsigned long long)acct.read_bytes,
                   (unsigned long long)acct.write_bytes,
                   (unsigned long long)acct.cancelled_write_bytes);
        result = 0;

out_unlock:
        mutex_unlock(&task->signal->cred_guard_mutex);
        return result;
}

なので、「/proc/PID/io」で確認できる、whole変数の値で、この値をプロセス(thread)のまとまりで合計とってるっぽいのですが、forkとかで子プロセスとか持った場合、親のプロセスを見ると、下のプロセスの統計も加算されてくるのでしょうか?
こんな感じ、PID=16907の情報
ugui7 ~ # cat /proc/16907/io
rchar: 9651817
wchar: 10643512
syscr: 1781138
syscw: 1321495
read_bytes: 1109835776
write_bytes: 2892156928
cancelled_write_bytes: 0

使い方


余計なことばかりで、アレなんですが

Total DISK READ :       0.00 B/s | Total DISK WRITE :     686.88 K/s
Actual DISK READ:       0.00 B/s | Actual DISK WRITE:       0.00 B/s
  TID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN     IO>    COMMAND
17060 be/4 root        0.00 B/s    0.00 B/s  0.00 %  0.27 % [kworker/2:0]
31094 be/4 root        0.00 B/s  400.35 K/s  0.00 %  0.00 % python2.7 -b /usr/lib/python-exec/python2.7/emerge dev-texlive/texlive-latex dev-texlive/texlive-fontutils app-text/texlive
こんな感じで、読み書きが多いプロセスを一番上に出力してくれる、他にもユーザーや、PIDで絞れるオプションがあるので確認して見てください。

どうでもいいですね...