如何監(jiān)測Linux進程的實時IO讀寫情況
Linux Kernel 2.6.20 以上的內(nèi)核支持進程 IO 統(tǒng)計,可以用類似 iotop 這樣的工具來監(jiān)測每個進程對 IO 操作的情況,就像用 top 來實時查看進程內(nèi)存、CPU 等占用情況那樣。但是對于 2.6.20 以下的 Linux 內(nèi)核版本就沒那么幸運了。筆者寫了一個簡單的 Python 腳本用來在 linux kernel < 2.6.20 下打印進程 IO 狀況。
Kernel < 2.6.20
這個腳本的想法很簡單,把 dmesg 的結果重定向到一個文件后再解析出來,每隔1秒鐘打印一次進程 IO 讀寫的統(tǒng)計信息,執(zhí)行這個腳本需要 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 活動狀況的話,需要下載和升級新內(nèi)核(2.6.20 或以上版本)。編譯新內(nèi)核時需要打開 TASK_DELAY_ACCT 和 TASK_IO_ACCOUNTING 選項。解壓內(nèi)核后進入配置界面:
# 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,保存內(nèi)核后編譯內(nèi)核:
# make; make modules; make modules_install; make install
修改 grub,確認能正確啟動新內(nèi)核:
# vi /boot/grub/menu.lst
出了新內(nèi)核外,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
有網(wǎng)友對以上腳本提出問題,問到 WRITE 為什么會出現(xiàn)是 0 的情況,這是個好問題,筆者在這里好好解釋一下。首先看看我們怎么樣才能實時監(jiān)測不同進程的 IO 活動狀況。
block_dump
Linux 內(nèi)核里提供了一個 block_dump 參數(shù)用來把 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
問題
一位細心的網(wǎng)友提到這樣一個問題:為什么會有 WRITE block 0 的情況出現(xiàn)呢?筆者跟蹤了一段時間,發(fā)現(xiàn)確實有 WRITE 0 的情況出現(xiàn),比如:
# 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 這里面包含的數(shù)字理解錯了,這些數(shù)字不是代表寫了多少 blocks,是代表寫到哪個 block,為了尋找真相,筆者追到 Linux 2.6.18 內(nèi)核代碼里,在 ll_rw_blk.c 里找到了答案:
$ vi linux-2.6.18/block/ll_rw_blk.c
- void submit_bio(int rw, struct bio *bio)
- {
- int count = bio_sectors(bio);
- BIO_BUG_ON(!bio->bi_size);
- BIO_BUG_ON(!bio->bi_io_vec);
- bio->bi_rw |= rw;
- if (rw & WRITE)
- count_vm_events(PGPGOUT, count);
- else
- count_vm_events(PGPGIN, count);
- 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));
- }
- generic_make_request(bio);
- }
很明顯從上面代碼可以看出 WRITE block 0 on sdb1,這里的 0 是 bio->bi_sector,是寫到哪個 sector,不是 WRITE 了多少 blocks 的意思。還有,如果 block 設備被分成多個區(qū)的話,這個 bi_sector(sector number)是從這個分區(qū)開始計數(shù),比如 block 0 on sdb1 就是 sdb1 分區(qū)上的第0個 sector 開始。
原文地址:http://www.vpsee.com/2010/07/monitoring-process-io-activity-on-linux-with-block_dump/
【編輯推薦】