怎样监测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 http://www.vpsee.com
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 开始。
看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注亿速云行业资讯频道,感谢您对亿速云的支持。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。