如何查看进程 IO 读写情况?

Linux Kernel 2.6.20 以上的内核支持进程 IO 统计,可以用类似 iotop 这样的工具来监测每个进程对 IO 操作的情况,就像用 top 来实时查看进程内存、CPU 等占用情况那样。但是对于 2.6.20 以下的 Linux 内核版本就没那么幸运了,根据 Stack Overflow 的这篇回帖 给出的方法,VPSee 写了一个简单的 Python 脚本用来在 linux kernel < 2.6.20 下打印进程 IO 状况。

Kernel < 2.6.20

这个脚本的想法很简单,把 dmesg 的结果重定向到一个文件后再解析出来,每隔1秒钟打印一次进程 IO 读写的统计信息,执行这个脚本需要 root:

#!/usr/bin/python
# Monitoring per-process disk I/O activity
# written by http://www.vpsee.com 

import sys, os, time, signal, re

class DiskIO:
    def __init__(self, pname=None, pid=None, reads=0, writes=0):
        self.pname = pname 
        self.pid = pid
        self.reads = 0
        self.writes = 0

def main():
    argc = len(sys.argv)
    if argc != 1:
        print "usage: ./iotop"
        sys.exit(0)

    if os.getuid() != 0:
        print "must be run as root"
        sys.exit(0)

    signal.signal(signal.SIGINT, signal_handler)
    os.system('echo 1 > /proc/sys/vm/block_dump')
    print "TASK              PID       READ      WRITE"
    while True:
        os.system('dmesg -c > /tmp/diskio.log')
        l = []  
        f = open('/tmp/diskio.log', 'r')
        line = f.readline()
        while line:
            m = re.match(\
                '^(\S+)\((\d+)\): (READ|WRITE) block (\d+) on (\S+)', line)
            if m != None:
                if not l:       
                    l.append(DiskIO(m.group(1), m.group(2)))
                    line = f.readline() 
                    continue            
                found = False   
                for item in l:  
                    if item.pid == m.group(2):
                        found = True            
                        if m.group(3) == "READ":
                            item.reads = item.reads + 1 
                        elif m.group(3) == "WRITE":
                            item.writes = item.writes + 1
                if not found:   
                    l.append(DiskIO(m.group(1), m.group(2)))
            line = f.readline()
        time.sleep(1)
        for item in l:
            print "%-10s %10s %10d %10d" % \
                (item.pname, item.pid, item.reads, item.writes)

def signal_handler(signal, frame):
    os.system('echo 0 > /proc/sys/vm/block_dump')
    sys.exit(0)

if __name__=="__main__":
    main()

Kernel >= 2.6.20

如果想用 iotop 来实时查看进程 IO 活动状况的话,需要下载和升级新内核(2.6.20 或以上版本)。编译新内核时需要打开 TASK_DELAY_ACCT 和 TASK_IO_ACCOUNTING 选项。解压内核后进入配置界面:

# tar jxvf linux-2.6.30.5.tar.bz2
# mv linux-2.6.30.5 /usr/src/
# cd /usr/src/linux-2.6.30.5

# make menuconfig

选择 Kernel hacking –> Collect scheduler debugging info 和 Collect scheduler statistics,保存内核后编译内核:

# make; make modules; make modules_install; make install

修改 grub,确认能正确启动新内核:

# vi /boot/grub/menu.lst

出了新内核外,iotop 还需要 Python 2.5 或以上才能运行,所以如果当前 Python 是 2.4 的话需要下载和安装最新的 Python 包。这里使用源代码编译安装:

# tar jxvf Python-2.6.2.tar.bz2
# cd Python-2.6.2
# ./configure
# make; make install

别忘了下载 setuptools:

# mv setuptools-0.6c9-py2.6.egg.sh setuptools-0.6c9-py2.6.egg
# sh setuptools-0.6c9-py2.6.egg

更多信息

如果想知道更多关于 block_dump 的信息,可以看看这篇 监测 Linux 进程的实时 IO 情况。使用 block_dump 的时候,最好能关掉 klogd 进程。

评论 (13 Comments)

  1. 我有个问题

  2. 我运行了上面的脚本,测系统上运行程序,大量的是kjournald进程,我查了这个是ext3的日志进程,蛋有个很奇怪的问题,我要监测的一些计算程序,读写都是0,缺还是被记录下来了,而脚本里判断输出的规则好像是如果有读写才记录下来并显示,为什么会出现读写都是0的条目呢

  3. 嗯,会出现 WRITE 是0的情况,比如下面的就是直接从 dmesg -c 打印出来的:
    pdflush(30744): WRITE block 0 on sdb1
    所以有可能出现读写都是0的情况,比如上面的写是0,读没有,所以这个 iotop 脚本打印出来就是 READ WRITE 都为0了。

  4. 谢谢您的答复。那我还想问问,如果这个write是0,是不是可以说明这个时间点上没有write?或者说,这个脚本的循环,每次判定的时候,也就是1秒1秒这样判定,如果1.5秒时候有一个0.1秒的IO操作,是无法被识别出来的?
    我现在在帮导师做一套计算平台的性能调优,主要是想通过这个东西,找出io瓶颈,之前装了iotop,能囧的没用起来,不能显示进程的,看到您博客里提供的脚本真是如获至宝,十分感谢。

  5. 和这个脚本无关,这个脚本只是来解析 dmesg 打印出来的结果的。抛开这个脚本不谈,让我们从原始数据(dmesg)说起,你直接从 dmesg 打印出结果也会出现 WRITE block 0 的情况,比如这是我系统上的输出:
    pdflush(23123): WRITE block 0 on sdb1
    pdflush(23123): WRITE block 16 on sdb1
    pdflush(23123): WRITE block 104 on sdb1
    pdflush(23123): WRITE block 40884480 on sdb1
    pdflush(23123): WRITE block 43767360 on sdb1
    pdflush(23123): WRITE block 43864648 on sdb1
    默认情况下 pdflush 有2个进程在运行、每隔5秒被唤醒去检查是否需要 flush 脏数据到 sdb1,一种解释可能是 pdflush 被唤醒的时候发现没什么可 flush 的,做了 WRITE 动作但没有 WRITE 任何东西到 sdb1,所以 WRITE block 为 0

  6. 您说的的确是有可能,我这个iotop脚本的记录,同时还有配合了iostat和vmstat的记录数据,在同一时间点上,显示出几个计算程序的读写都为0,而cpu几乎是100%空闲,写操作只有1.17次。我准备在别的机器上再测试一下,这次的数据有点不正常,因为以前也测过数据和这次差别很大

  7. 好了,我查了一下 2.6.18 内核代码,内核用 dmesg 打印 IO 的这段代码在 ll_rw_blk.c 里面:
    # vi linux-2.6.18/block/ll_rw_blk.c

    if (unlikely(block_dump)) {
    char b[BDEVNAME_SIZE];
    printk(KERN_DEBUG “%s(%d): %s block %Lu on %s\n”,
    current->comm, current->pid,
    (rw & WRITE) ? “WRITE” : “READ”,
    (unsigned long long)bio->bi_sector,
    bdevname(bio->bi_bdev,b));
    }

    很明显从上面代码可以看出 WRITE block 0 on sdb1,这里的 0 是 bio->bi_sector,是 sector 不是 WRITE 了多少 blocks 的意思。

  8. 您的意思是这个0代表某个扇区?测试机器只有1个硬盘,难怪我看凡是系统进程以外的进程write里只会出现0,1,2,这三个数字,但是比如kjournald和pdflush这样的系统进程write里的数字都是不同的,多的时候能到2000多。不知道iotop这个程序能不能记录下进程的读写情况,我昨天拿到了一组测试数据,我发现硬盘高的时候,能连续100多秒保持在IO是100%占用,应该是很严重的瓶颈,我在想如果修改kjournald的模式能否有所缓解这种情况?

  9. 嗯,你看看这篇 监测 Linux 进程的实时 IO 情况。使用 block_dump 的时候,最好能关掉 klogd 进程。

  10. 非常感谢您的帮助

  11. 我想问下您,iotop这个工具,在2.6.20内核用起来了吗,我升级之后还是看不到内核,下面报错的问题,我Google上查了说是内核的bug,我想问下,您在什么系统上装的iotop,内核多少,我是redhat5.3

  12. Linux kernel 版本太多我没有一一测过,不过 2.6.30 上(linux-2.6.30.5.tar.bz2)上是可以的。

  13. 谢谢

发表评论