1. 程式人生 > >Linux下的IO監控與分析

Linux下的IO監控與分析

近期要在公司內部做個Linux IO方面的培訓, 整理下手頭的資料給大家分享下

                                各種IO監視工具在Linux IO 體系結構中的位置 

                                源自 Linux Performance and Tuning Guidelines.pdf

1 系統級IO監控

iostat

  iostat -xdm 1    # 個人習慣

%util         代表磁碟繁忙程度。100% 表示磁碟繁忙, 0%表示磁碟空閒。但是注意,磁碟繁忙不代表磁碟(頻寬)利用率高  

argrq-sz    提交給驅動層的IO請求大小,一般不小於4K,不大於max(readahead_kb, max_sectors_kb)

                可用於判斷當前的IO模式,一般情況下,尤其是磁碟繁忙時, 越大代表順序,越小代表隨機

svctm        一次IO請求的服務時間,對於單塊盤,完全隨機讀時,基本在7ms左右,既尋道+旋轉延遲時間

注: 各統計量之間關係

=======================================

%util = ( r/s  +  w/s) * svctm / 1000                        # 佇列長度 =  到達率     *  平均服務時間                     
avgrq-sz = ( rMB/s + wMB/s) * 2048 / (r/s  + w/s)    # 2048 為 1M / 512

=======================================

總結:

iostat 統計的是通用塊層經過合併(rrqm/s, wrqm/s)後,直接向裝置提交的IO資料,可以反映系統整體的IO狀況,但是有以下2個缺點:

1  距離業務層比較遙遠,跟程式碼中的write,read不對應(由於系統預讀 + pagecache + IO排程演算法等因素, 也很難對應)

2  是系統級,沒辦法精確到程序,比如只能告訴你現在磁碟很忙,但是沒辦法告訴你是誰在忙,在忙什麼?

2 程序級IO監控

  iotop 和 pidstat (僅rhel6u系列)

iotop    顧名思義, io版的top

pidstat 顧名思義, 統計程序(pid)的stat,程序的stat自然包括程序的IO狀況

這兩個命令,都可以按程序統計IO狀況,因此可以回答你以下二個問題

    1.  當前系統哪些程序在佔用IO,百分比是多少?
    2.  佔用IO的程序是在讀?還是在寫?讀寫量是多少?

pidstat 引數很多,僅給出幾個個人習慣

       pidstat -d  1                  #只顯示IO

       pidstat -u -r -d -t 1        # -d IO 資訊,

                                           # -r 缺頁及記憶體資訊
                                           # -u CPU使用率
                                           # -t 以執行緒為統計單位
                                           # 1  1秒統計一次

iotop, 很簡單,直接敲命令

  block_dump, iodump

iotop   和 pidstat 用著很爽,但兩者都依賴於/proc/pid/io檔案匯出的統計資訊, 這個對於老一些的核心是沒有的,比如rhel5u2

因此只好用以上2個窮人版命令來替代:

echo 1 > /proc/sys/vm/block_dump     # 開啟block_dump,此時會把io資訊輸入到dmesg中

                                                        # 原始碼: [email protected]_rw_blk.c:3213

watch -n 1 "dmesg -c | grep -oP \"\w+\(\d+\): (WRITE|READ)\" | sort | uniq -c"

                                                         # 不停的dmesg -c

echo 0 > /proc/sys/vm/block_dump      # 不用時關閉

也可以使用現成的指令碼 iodump, 具體參見 http://code.google.com/p/maatkit/source/browse/trunk/util/iodump?r=5389

  iotop.stp

systemtap指令碼,一看就知道是iotop命令的窮人複製版,需要安裝Systemtap, 預設每隔5秒輸出一次資訊

stap iotop.stp                                     #  examples/io/iotop.stp

總結

程序級IO監控 ,

  1.  可以回答系統級IO監控不能回答的2個問題
  2.  距離業務層相對較近(例如,可以統計程序的讀寫量)

但是也沒有辦法跟業務層的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

複製程式碼
#! /usr/bin/env python
# -*- coding: gbk -*-
import os
import re
import timeit 
from ctypes import CDLL, create_string_buffer, c_ulong, c_longlong
from optparse import OptionParser

usage = '''%prog -s strace.log -f fileno.map '''

_glibc = None
_glibc_pread  = None
_c_char_buf   = None

_open_file = []

def getlines(filename):
    _lines = []
    with open(filename,'r') as _f:
        for line in _f:
            if line.strip() != "":
                _lines.append(line.strip())
    return _lines
    


def parsecmdline():
    parser = OptionParser(usage)
    parser.add_option("-s", "--strace", dest="strace_filename",
                      help="strace file", metavar="FILE")
                      
    parser.add_option("-f", "--fileno", dest="fileno_filename",
                      help="fileno file",  metavar="FILE")
                 
    (options, args) = parser.parse_args()
    if options.strace_filename is None: parser.error("strace is not specified.")
    if not os.path.exists(options.strace_filename): parser.error("strace file does not exist.")
    
    if options.fileno_filename is None: parser.error("fileno is not specified.")
    if not os.path.exists(options.strace_filename): parser.error("fileno file does not exist.")
    
    return options.strace_filename, options.fileno_filename
    
# [type, ...]
#   [pread, fno, count, offset]
# pread(15, "", 4348, 140156928)
def parse_strace(filename):
    lines = getlines(filename)
    
    action = []
    _regex_str = r'(pread|pread64)[^\d]*(\d+),\s*[^,]*,\s*([\dkKmM*+\-. ]*),\s*([\dkKmM*+\-. ]*)'
    for i in lines:
        _match = re.match(_regex_str, i)
        if _match is None: continue         # 跳過無效行
        _type, _fn, _count, _off = _match.group(1), _match.group(2), _match.group(3), _match.group(4)
        _off   = _off.replace('k', " * 1024 ").replace('K', " * 1024 ").replace('m', " * 1048576 ").replace('M', " * 1048576 ")
        _count = _count.replace('k', " * 1024 ").replace('K', " * 1024 ").replace('m', " * 1048576 ").replace('M', " * 1048576 ")
        #print _off
        action.append([_type, _fn, str(int(eval(_count))), str(int(eval(_off))) ])
    
    return action
    
def parse_fileno(filename):
    lines = getlines(filename)
    fmap = {}
    for i in lines:
        if i.strip().startswith("#"): continue      # 註釋行
        _split = [j.strip() for j in i.split("=")]
        if len(_split) != 2: continue               # 無效行
        fno, fname = _split[0], _split[1]
        fmap[fno] = fname
    return fmap
    
def simulate_before(strace, fmap):
    global _open_file, _c_char_buf
    rfmap = {}
    for i in fmap.values():
        _f = open(i, "r+b")
        #print "open {0}:{1}".format(_f.fileno(), i)
        _open_file.append(_f)
        rfmap[i] = str(_f.fileno())       # 反向對映
    
    to_read = 4 * 1024                    # 預設4K buf
    for i in strace:
        i[1] = rfmap[fmap[i[1]]]     # fid -> fname -> fid 對映轉換
        to_read = max(to_read, int(i[2]))
    #print "read buffer len: %d Byte" % to_read
    _c_char_buf = create_string_buffer(to_read)
    
    
    
def simulate_after():
    global _open_file
    for _f in _open_file:
        _f.close()
    
def simulate(actions):

    #timeit.time.sleep(10)        # 休息2秒鐘, 以便IO間隔
    
    start = timeit.time.time()
    
    for act in actions:
        __simulate__(act)
        
    finish = timeit.time.time()
    
    return finish - start
    
def __simulate__(act):
    global _glibc, _glibc_pread, _c_char_buf
    
    if "pread" in act[0]:
        _fno   = int(act[1])
        _buf   = _c_char_buf
        _count = c_ulong(int(act[2]))
        _off   = c_longlong(int(act[3]))
       
        _glibc_pread(_fno, _buf, _count, _off)
        
        #print _glibc.time(None)
    else:
        pass
    pass

def loadlibc():
    global _glibc, _glibc_pread
    _glibc = CDLL("libc.so.6")
    _glibc_pread = _glibc.pread64
    
if __name__ == "__main__":
    
    _strace, _fileno = parsecmdline()  # 解析命令列引數
    
    loadlibc()                         # 載入動態庫
    
    _action = parse_strace(_strace)    # 解析 action 檔案
    
    _fmap   = parse_fileno(_fileno)    # 解析 檔名對映 檔案
    
    simulate_before(_action, _fmap)    # 預處理
    #print "total io operate: %d" % (len(_action))
    
    #for act in _action: print " ".join(act)
    print "%f" % simulate(_action) 
複製程式碼

相關推薦

LinuxIO監控分析

近期要在公司內部做個Linux IO方面的培訓, 整理下手頭的資料給大家分享下                                 各種IO監視工具在Linux IO 體系結構中的位置                               

Linux IO 監控深入分析

4.6 .cn 計時 說明 扇區 版本 play linux patch https://jaminzhang.github.io/os/Linux-IO-Monitoring-and-Deep-Analysis/ Linux IO 監控與深入分析 引言 接昨天電話面試

(轉)Linux通過rsyncinotify(異步文件系統事件監控機制)實現文件實時同步

-a 推送 root started init.d log tool mysql同步 .tar.gz Linux下通過rsync與inotify(異步文件系統事件監控機制)實現文件實時同步原文:http://www.summerspacestation.com/linux%

Linux監控分析工具nmon

一、概述 nmon是一種在AIX與各種Linux作業系統上廣泛使用的監控與分析工具,相對於其它一些系統資源監控工具來說,nmon所記錄的資訊是比較全面的,它能在系統執行過程中實時地捕捉系統資源的使用情況,並且能輸出結果到檔案中,然後通過nmon_analyzer工具產生資料檔案與圖形化結

Linux程式設計------檔案IO(三) 檔案共享和fcntl函式

檔案共享 一個程序打開了兩個檔案 檔案表條目(file-table-entry):    1.檔案狀態標誌(file-status-flags): 讀/寫/追加/同步/

Linux MySQL 安裝卸載

word stat client int etc dpkg init net 是否 參考博客:http://www.cnblogs.com/steven_oyj/archive/2010/05/24/1742808.html http://www.linuxidc.com/

linuxLAMP安裝配置

函數 rri osi ase live ins php expose share 安裝 一. Apache 安裝 yum install -y httpd啟動 /etc/init.d/httpd start備註:Apache啟動之後會提示錯誤: 正在啟動http

Linux的打包壓縮和tar命令!

tar.gz 安裝 gin font log 針對 更新 mil emp 本文介紹了linux下的打包壓縮程序tar、gzip、gunzip、bzip2、bunzip2、 compress、uncompress、zip、unzip、rar、unrar程序,以及如何使用它們對

Linux安裝Nginx配置

目錄 openssl oct pan yum安裝 usr 負載均衡 官方 err 一,安裝GCC yum安裝gcc-c ++ -y 二,安裝nginx的所需要的依賴庫 yum -y安裝zlib-devel openssl-devel pcre-devel

2017-2018-1 20179202《Linux內核原理分析》第八周作業

預測 rar 合並 數據 代碼分析 一個 設置 堆棧 linu 一 、可執行程序的裝載 1. 預處理、編譯、鏈接 gcc –e –o hello.cpp hello.c //預處理 gcc -x cpp-output -S -o hello.s hello.cpp //

20179223《Linux內核原理分析》第九周學習筆記

3.18 用戶 通過 linux內核 良好的 數據 context from inux 視頻學習 進程調度與進程調度的時機分析 不同類型的進程有不同的調度需求 第一種分類: ——I/O-bound:1.頻繁的進行I/O;2.通常會花費很多時間等待I/O操作的完成 ——CPU

2017-2018-1 20179202《Linux內核原理分析》第九周作業

發生 png inpu 方法 地址轉換 blog pic int 內核棧 進程的切換和系統的一般執行過程 1.知識總結 (1)進程調度的時機: 中斷處理過程直接調用schedule(),或者返回用戶態時根據need_resched標記調用schedule()。 內核線程是

20179203 《Linux內核原理分析》第十周作業

計時 使用 數據 一個個 類型 原則 聲明 標識 變量 第17章 設備與模塊 一、設備類型 1. Linux及Unix系統: 塊設備 字符設備 網絡設備 2.塊設備: 通常縮寫為blkdev,它是可尋址的,尋址以塊為單位,塊大小隨設備不同而不同;塊設備通常支持重定位操作,也

2017-2018-1 20179215《Linux內核原理分析》第十周作業

自動填充 可移植性 智能 x86 調試 推薦 狀態 討論 ani 第17章 設備與模塊 一、設備類型 ?除了以上3種典型的設備之外,其實Linux中還有一些其他的設備類型,其中見的較多的應該算是"偽設備"。所謂"偽設備",其實就是一些虛擬的設備,僅提供訪問內核功能而已,沒

Linuxtomcat6.0jdk安裝

可執行 tomcat6 環境變量 /var/ serve 兩個 所在 開機自啟動 完成後 Linux下tomcat6.0與jdk安裝 步驟如下: 1、 上傳apache-tomcat-6.0.37.tar.gz和jdk-6u13-linux-i586.bin至/usr/lo

Redis在Linux的安裝配置

conf eas etc 下載 127.0.0.1 基於內存 最新 ansi lin Redis是一個開源的使用ANSI C語言編寫、支持網絡、可基於內存亦可持久化的日誌型、Key-Value數據庫,並提供多種語言的API。 Redis是 NoSQL技術陣營中的一員。 說到

2017-2018-1 20179215《Linux內核原理分析》第十二周作業

繼續 解壓 判斷語句 cti 數據信息 接下來 分組 後臺數據庫 就是 Sql註入基礎原理介紹 分組:和20179205王雅哲共同完成實驗 一、實驗說明 1.1 sql註入 ?SQL註入攻擊通過構建特殊的輸入作為參數傳入Web應用程序,而這些輸入大都是SQL語法裏的一些組合

20179203 《Linux內核原理分析》第十二周作業

系統管理 ash 數據讀取 用戶控制 tar 初始設置 可執行 uid time Return-to-libc 攻擊實驗 一、實驗描述 緩沖區溢出的常用攻擊方法是用 shellcode 的地址來覆蓋漏洞程序的返回地址,使得漏洞程序去執行存放在棧中 shellcode。為了

linuxFTP安裝配置

linux ftp pure-ftpdcd /usr/local/src/wget http://download.pureftpd.org/pub/pure-ftpd/releases/pure-ftpd-1.0.42.tar.bz2 (下載pureftp源碼包)tar jxvf pure-f

linuxrsync安裝配置

linux rsync rsync (同步數據,支持增量備份)使用方法:rsync -av 192.168.31.182:/tmp/1.txt /tmp/ (將服務器192.168.31.182中tmp文件夾下的1.txt文件拷貝到本機的tmp目錄下)rsync -av /tmp/1.tx