近期要在公司內部做個Linux IO方面的培訓, 整理下手頭的資料給大家分享下
各種IO監視工具在Linux IO 體繫結構中的位置
源自 Linux Performance and Tuning Guidelines.pdf
1 系統級IO監控iostat
iostat -xdm 1 # 個人習慣
avgrq-sz = ( rMB/s + wMB/s) * 2048 / (r/s + w/s) # 2048 為 1M / 512
2 進程級IO監控 iotop 和 pidstat (僅rhel6u系列)
# -u CPU使用率
# -t 以線程為統計單位
# 1 1秒統計一次
block_dump, iodump
iotop.stp
但是也沒有辦法跟業務層的read,write聯絡在一起,同時顆粒度較粗,沒有辦法告訴你,當前進程讀寫了哪些檔案? 耗時? 大小 ?
3 業務級IO監控 ioprofile
ioprofile 命令本質上是 lsof + strace, 具體下載可見 http://code.google.com/p/maatkit/
ioprofile 可以回答你以下三個問題:
1 當前進程某時間內,在業務層面讀寫了哪些檔案(read, write)?
2 讀寫次數是多少?(read, write的調用次數)
3 讀寫資料量多少?(read, write的byte數)
假設某個行為會觸發程式一次IO動作,例如: "一個頁面點擊,導致後台讀取A,B,C檔案"
============================================
./io_event # 假設類比一次IO行為,讀取A檔案一次, B檔案500次, C檔案500次
ioprofile -p `pidof io_event` -c count # 讀寫次數
ioprofile -p `pidof io_event` -c times # 讀寫耗時
ioprofile -p `pidof io_event` -c sizes # 讀寫大小
注: ioprofile 僅支援多線程程式,對單線程程式不支援. 對於單線程程式的IO業務級分析,strace足以。
總結:
ioprofile本質上是strace,因此可以看到read,write的調用軌跡,可以做業務層的io分析(mmap方式無能為力)
4 檔案級IO監控
檔案級IO監控可以配合/補充"業務級和進程級"IO分析
檔案級IO分析,主要針對單個檔案, 回答當前哪些進程正在對某個檔案進行讀寫操作.
1 lsof 或者 ls /proc/pid/fd
2 inodewatch.stp
lsof 告訴你 當前檔案由哪些進程開啟
lsof ../io # io目錄 當前由 bash 和 lsof 兩個進程開啟
lsof 命令 只能回答靜態資訊, 並且"開啟" 並不一定"讀取", 對於 cat ,echo這樣的命令, 開啟和讀取都是瞬間的,lsof很難捕捉
可以用 inodewatch.stp 來彌補
stap inodewatch.stp major minor inode # 主裝置號, 輔裝置號, 檔案inode節點號
stap inodewatch.stp 0xfd 0x00 523170 # 主裝置號, 輔裝置號, inode號,可以通過 stat 命令獲得
5 IO模擬器
iotest.py # 見附錄
開發人員可以 利用 ioprofile (或者 strace) 做詳細分析系統的IO路徑,然後在程式層面做相應的最佳化。
但是一般情況下調整程式,代價比較大,尤其是當不確定修改方案到底能不能有效時,最好有某種類比途徑以快速驗證。
以為我們的業務為例,發現某次查詢時,系統的IO訪問模式如下:
訪問了A檔案一次
訪問了B檔案500次, 每次16位元組, 平均間隔 502K
訪問了C檔案500次, 每次200位元組, 平均間隔 4M
這裡 B,C檔案是交錯訪問的, 既
1 先訪問B,讀16位元組,
2 再訪問C,讀200位元組,
3 回到B,跳502K後再讀16位元組,
4 回到C,跳4M後,再讀200位元組
5 重複500次
strace 檔案如下:
一個簡單樸素的想法, 將B,C交錯讀,改成先批量讀B , 再批量讀C,因此調整strace 檔案如下:
將調整後的strace檔案, 作為輸入交給 iotest.py, iotest.py 按照 strace 檔案中的訪問模式, 類比相應的IO
iotest.py -s io.strace -f fmap
fmap 為對應檔,將strace中的222,333等fd,映射到實際的檔案中
===========================
111 = /opt/work/io/A.data
222 = /opt/work/io/B.data
333 = /opt/work/io/C.data
===========================
6 磁碟磁碟重組
一句話: 只要磁碟容量不常年保持80%以上,基本上不用擔心片段問題。
如果實在擔心,可以用 defrag 指令碼
7 其他IO相關命令
blockdev 系列
=======================================
blockdev --getbsz /dev/sdc1 # 查看sdc1盤的塊大小
block blockdev --getra /dev/sdc1 # 查看sdc1盤的預讀(readahead_kb)大小
blockdev --setra 256 /dev/sdc1 # 設定sdc1盤的預讀(readahead_kb)大小,低版的核心通過/sys設定,有時會失敗,不如blockdev靠譜
=======================================
附錄 iotest.py
ctypes optparse = ===== line line.strip() != =, , dest==, metavar=, , dest==, metavar== options.strace_filename None: parser.error( os.path.exists(options.strace_filename): parser.error( options.fileno_filename None: parser.error( os.path.exists(options.strace_filename): parser.error(=== r i = _match None: _type, _fn, _count, _off = _match.group(1), _match.group(2), _match.group(3), _match.group(4= _off.replace(, ).replace(, ).replace(, ).replace(, = _count.replace(, ).replace(, ).replace(, ).replace(, == i i.strip().startswith(): _split = [j.strip() j i.split( len(_split) != 2: fno, fname = _split[0], _split[1== i = open(i, = str(_f.fileno()) = 4 * 1024 i 1] = rfmap[fmap[i[1]]] to_read = max(to_read, int(i[2 _c_char_buf = _f = act = finish - = int(act[1== c_ulong(int(act[2= c_longlong(int(act[3 = CDLL(= == = parsecmdline() = parse_strace(_strace) = parse_fileno(_fileno) % simulate(_action)