linux events/0 进程,监测 Linux进程的实时IO情况

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

Kernel < 2.6.20

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

1. #!/usr/bin/python

2. # Monitoring per-process disk I/O activity

3. # written by

4.

5. import sys, os, time, signal, re

6.

7. class DiskIO:

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

9.         self.pname = pname

10.         self.pid = pid

11.         self.reads = 0

12.         self.writes = 0

13.

14. def main():

15.     argc = len(sys.argv)

16.     if argc != 1:

17.         print "usage: ./iotop"

18.         sys.exit(0)

19.

20.     if os.getuid() != 0:

21.         print "must be run as root"

22.         sys.exit(0)

23.

24.     signal.signal(signal.SIGINT, signal_handler)

25.     os.system('echo 1 > /proc/sys/vm/block_dump')

26.     print "TASK              PID       READ      WRITE"

27.     while True:

28.         os.system('dmesg -c > /tmp/diskio.log')

29.         l = []

30.         f = open('/tmp/diskio.log', 'r')

31.         line = f.readline()

32.         while line:

33.             m = re.match(\

34.                 '^(\S+)\((\d+)\): (READ|WRITE) block (\d+) on (\S+)', line)

35.             if m != None:

36.                 if not l:

37.                     l.append(DiskIO(m.group(1), m.group(2)))

38.                     line = f.readline()

39.                     continue

40.                 found = False

41.                 for item in l:

42.                     if item.pid == m.group(2):

43.                         found = True

44.                         if m.group(3) == "READ":

45.                             item.reads = item.reads + 1

46.                         elif m.group(3) == "WRITE":

47.                             item.writes = item.writes + 1

48.                 if not found:

49.                     l.append(DiskIO(m.group(1), m.group(2)))

50.             line = f.readline()

51.         time.sleep(1)

52.         for item in l:

53.             print "%-10s %10s %10d %10d" % \

54.                 (item.pname, item.pid, item.reads, item.writes)

55.

56. def signal_handler(signal, frame):

57.     os.system('echo 0 > /proc/sys/vm/block_dump')

58.     sys.exit(0)

59.

60. if __name__=="__main__":

61.     main()

62.

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

有网友对以上脚本提出问题,问到 WRITE 为什么会出现是 0 的情况,这是个好问题,笔者在这里好好解释一下。首先看看我们怎么样才能实时监测不同进程的 IO 活动状况。

block_dump

Linux 内核里提供了一个 block_dump 参数用来把 block 读写(WRITE/READ)状况 dump 到日志里,这样可以通过 dmesg 命令来查看,具体操作步骤是:

# sysctl vm.block_dump=1

or

# echo 1 > /proc/sys/vm/block_dump

然后就可以通过 dmesg 就可以观察到各个进程 IO 活动的状况了:

# dmesg -c

kjournald(542): WRITE block 222528 on dm-0

kjournald(542): WRITE block 222552 on dm-0

bash(18498): dirtied inode 5892488 (ld-linux-x86-64.so.2) on dm-0

bash(18498): dirtied inode 5892482 (ld-2.5.so) on dm-0

dmesg(18498): dirtied inode 11262038 (ld.so.cache) on dm-0

dmesg(18498): dirtied inode 5892496 (libc.so.6) on dm-0

dmesg(18498): dirtied inode 5892489 (libc-2.5.so) on dm-0

问题

一位细心的网友提到这样一个问题:为什么会有 WRITE block 0 的情况出现呢?笔者跟踪了一段时间,发现确实有 WRITE 0 的情况出现,比如:

# dmesg -c

...

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

...

答案

原来我们把 WRITE block 0,WRITE block 16, WRITE block 104 这里面包含的数字理解错了,这些数字不是代表写了多少 blocks,是代表写到哪个 block,为了寻找真相,笔者追到 Linux 2.6.18 内核代码里,在 ll_rw_blk.c 里找到了答案:

$ vi linux-2.6.18/block/ll_rw_blk.c

1. void submit_bio(int rw, struct bio *bio)

2. {

3.         int count = bio_sectors(bio);

4.

5.         BIO_BUG_ON(!bio->bi_size);

6.         BIO_BUG_ON(!bio->bi_io_vec);

7.         bio->bi_rw |= rw;

8.         if (rw & WRITE)

9.                 count_vm_events(PGPGOUT, count);

10.         else

11.                 count_vm_events(PGPGIN, count);

12.

13.         if (unlikely(block_dump)) {

14.                 char b[BDEVNAME_SIZE];

15.                 printk(KERN_DEBUG "%s(%d): %s block %Lu on %s\n",

16.                         current->comm, current->pid,

17.                         (rw & WRITE) ? "WRITE" : "READ",

18.                         (unsigned long long)bio->bi_sector,

19.                         bdevname(bio->bi_bdev,b));

20.         }

21.

22.         generic_make_request(bio);

23. }

很明显从上面代码可以看出 WRITE block 0 on sdb1,这里的 0 是 bio->bi_sector,是写到哪个 sector,不是 WRITE 了多少 blocks 的意思。还有,如果 block 设备被分成多个区的话,这个 bi_sector(sector number)是从这个分区开始计数,比如 block 0 on sdb1 就是 sdb1 分区上的第0个 sector 开始。

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/31448824/viewspace-2137661/,如需转载,请注明出处,否则将追究法律责任。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值