能夠經過以下命令查看與 IO 相關的系統信息。python
tune2fs -l /dev/sda7 ← 讀取superblock信息 # blockdev --getbsz /dev/sda7 ← 獲取block大小 tune2fs -l /dev/sda7 | grep "Block size" ← 同上 # dumpe2fs /dev/sda7 | grep "Block size" ← 同上 # stat /boot/ | grep "IO Block" ← 同上 # fdisk -l ← 硬盤的扇區大小(Sector Size
)ios
在 WiKi 中的定義:A 「block」, a contiguous number of bytes, is the minimum unit of memory that is read from and written to a disk by a disk driver。git
塊是文件系統的抽象,而非磁盤的屬性,通常是 Sector Size 的倍數;扇區大小則是磁盤的物理屬性,它是磁盤設備尋址的最小單元。另外,內核中要求 Block_Size = Sector_Size * (2的n次方),且 Block_Size <= 內存的 Page_Size (頁大小)。github
【文章福利】小編推薦本身的C/C++Linux羣:832218493!整理了一些我的以爲比較好的學習書籍、視頻資料共享在裏面,有須要的能夠自行添加哦!~算法
其實是經過 statvfs() 方法查詢磁盤數據,能夠經過以下命令查看。json
$ python -c 'import os; os.statvfs("/")'
其空間佔用大體以下。bash
+--------------------------+----------------+-------------------------------------------------------+ | | | | +--------------------------+----------------+-------------------------------------------------------+ |<-- f_bavail(non-root) -->|<-- reserved -->|<------------- f_bused=f_blocks-f_bfree -------------->| |<------------- f_bfree(root) ------------->| | |<----------------------------------------- f_blocks ---------------------------------------------->|
前者是非 root 用戶已經使用的佔非 root 用戶可用空間百分數;後者是保留給 root 用戶以及已經使用磁盤佔整個磁盤空間百分數。對於 extN 類的文件系統通常會保留 1%~5% 的磁盤空間給 root 使用,當 reserved 佔比較大時會致使二者的計算差較大。併發
主要是要獲取當前系統使用的什麼類型的磁盤 (SCSI、IDE、SSD等),甚至是製造商、機器型號、序列號等信息。ionic
$ dmesg | grep scsi
簡單列舉磁盤監控時常見的指標。ide
IOPS 每秒IO數 對磁盤來講,一次磁盤的連續讀或寫稱爲一次磁盤 IO,當傳輸小塊不連續數據時,該指標有重要參考意義。 Throughput 吞吐量 硬盤傳輸數據流的速度,單位通常爲 MB/s,在傳輸大塊不連續數據的數據,該指標有重要參考做用。 IO平均大小 實際上就是吞吐量除以 IOPS,用於判斷磁盤使用模式,通常大於 32K 爲順序讀取爲主,不然隨機讀取爲主。 Utilization 磁盤活動時間百分比 磁盤處於活動狀態 (數據傳輸、尋道等) 的時間百分比,也即磁盤利用率,通常該值越高對應的磁盤資源爭用越高。 Service Time 服務時間 磁盤讀寫操做執行的時間,對於機械磁盤包括了尋道、旋轉、數據傳輸等,與磁盤性能相關性較高,另外,也受 CPU、內存影響。 Queue Length 等待隊列長度 待處理的 IO 請求的數目,注意,若是該磁盤爲磁盤陣列虛擬的邏輯驅動器,須要除以實際磁盤數,以獲取單盤的 IO 隊列。 Wait Time 等待時間 在隊列中排隊的時間。
除了能夠經過該命令查看磁盤信息以外,還能夠用來查看 CPU 信息,分別經過 -d 和 -c 參數控制;可直接經過 iostat -xdm 1 命令顯示磁盤隊列的長度等信息。
Device: rrqm/s wrqm/s r/s w/s rMB/s wMB/s avgrq-sz avgqu-sz await r_await w_await svctm %util sda 0.02 1.00 0.99 1.84 0.03 0.04 46.98 0.01 2.44 0.47 3.49 0.25
0.07
其中參數以下:
rrqm/s wrqm/s 讀寫請求每秒合併後發送給磁盤的請求。 r/s w/s 應用發送給系統的請求數目。 argrq-sz 提交給驅動層IO請求的平均大小(sectors),通常不小於4K,不大於max(readahead_kb, max_sectors_kb); 可用於判斷當前的 IO 模式,越大表明順序,越小表明隨機;計算公式以下: argrq-sz = (rsec + wsec) / (r + w) argqu-sz Average Queue Size 在驅動層的隊列排隊的平均長度。 await Average Wait 平均的等待時間,包括了在隊列中的等待時間,以及磁盤的處理時間。 svctm(ms) Service Time 請求發送給IO設備後的響應時間,也就是一次磁盤IO請求的服務時間,不過該指標官網說不許確,要取消。 對於單塊SATA盤,徹底隨機讀時,基本在7ms左右,既尋道+旋轉延遲時間。 %util 一秒內IO操做所佔的比例,計算公式是(r/s+w/s)*(svctm/1000),例如採集時間爲 1s 其中有 0.8s 在處 理 IO 請求,那麼 util 爲 80% ;對於一塊磁盤,若是沒有併發IO的概念,那麼這個公式是正確的,但 是對於RAID磁盤組或者SSD來講,這個計算公式就有問題了,就算這個值超過100%,也不表明存儲有瓶頸, 容易產生誤導
。
iostat 統計的是通用塊層通過合併 (rrqm/s, wrqm/s) 後,直接向設備提交的 IO 數據,能夠反映系統總體的 IO 情況,可是距離應用層比較遠,因爲系統預讀、Page Cache、IO調度算法等因素,很難跟代碼中的 write()、read() 對應。
簡言之,這是系統級,沒辦法精確到進程,好比只能告訴你如今磁盤很忙,可是沒辦法告訴你是那個進程在忙,在忙什麼?
/proc/diskstats 該命令會讀取 /proc/diskstats 文件,各個指標詳細的含義能夠參考內核文檔 iostats.txt,其中各個段的含義以下。 filed1 rd_ios 成功完成讀的總次數; filed2 rd_merges 合併寫完成次數,經過合併提升效率,例如兩次4K合併爲8K,這樣只有一次IO操做;合併操做是由IO Scheduler(也叫 Elevator)負責。 filed3 rd_sectors 成功讀過的扇區總次數; filed4 rd_ticks 全部讀操做所花費的毫秒數,每一個讀從__make_request()開始計時,到end_that_request_last()爲止,包括了在隊列中等待的時間; filed5 wr_ios 成功完成寫的總次數; filed6 wr_merges 合併寫的次數; filed7 wr_sectors 成功寫過的扇區總次數; filed8 wr_ticks 全部寫操做所花費的毫秒數; filed9 in_flight 如今正在進行的IO數目,在IO請求進入隊列時該值加1,在IO結束時該值減1,注意是在進出隊列時,而非交給磁盤時; filed10 io_ticks 輸入/輸出操做花費的毫秒數; filed11 time_in_queue 是一個權重值,當有上面的IO操做時,這個值就增長。
須要注意 io_ticks 與 rd/wr_ticks 的區別,後者是把每個 IO 所消耗的時間累加在一塊兒,由於硬盤設備一般能夠並行處理多個 IO,因此統計值每每會偏大;而前者表示該設備有 IO 請求在處理的時間,也就是非空閒,不考慮 IO 有多少,只考慮如今有沒有 IO 操做。在實際計算時,會在字段 in_flight 不爲零的時候 io_ticks 保持計時,爲 0 時中止計時。
另外,io_ticks 在統計時不考慮當前有幾個 IO,而 time_in_queue 是用當前的 IO 數量 (in_flight) 乘以時間,統計時間包括了在隊列中的時間以及磁盤處理 IO 的時間。
簡單介紹下常見的指標,包括了常常誤解的指標。
util
這裏重點說一下 iostat 中 util 的含義,該參數能夠理解爲磁盤在處理 IO 請求的總時間,若是是 100% 則代表磁盤一直在處理 IO 請求,這也就意味着 IO 在滿負載運行。
對於一塊磁盤,若是沒有併發 IO 的概念,因此這個公式是正確的,可是如今的磁盤或者對於RAID磁盤組以及SSD來講,這個計算公式就有問題了,就算這個值超過100%,也不表明存儲有瓶頸,容易產生誤導。
舉個簡化的例子:某硬盤處理單個 IO 須要 0.1 秒,也就是有能力達到 10 IOPS,那麼當 10 個 IO 請求依次順序提交的時候,須要 1 秒才能所有完成,在 1 秒的採樣週期裏 %util 達到 100%;而若是 10 個 IO 請求一次性提交的話,0.1 秒就所有完成,在 1 秒的採樣週期裏 %util 只有 10%。
可見,即便 %util 高達 100%,硬盤也仍然有可能還有餘力處理更多的 IO 請求,即沒有達到飽和狀態。不過遺憾的是如今 iostat 沒有提供相似的指標。
在 CentOS 中使用的是 github sysstat,以下是其計算方法。
rw_io_stat_loop() 循環讀取 |-read_diskstats_stat() 從/proc/diskstats讀取狀態 |-write_stats() 輸出採集的監控指標 |-write_ext_stat() |-compute_ext_disk_stats() 計算ext選項,如util |-write_plain_ext_stat()
關於該參數的代碼詳細介紹以下。
#define S_VALUE(m,n,p) (((double) ((n) - (m))) / (p) * HZ) void read_diskstats_stat(int curr) { struct io_stats sdev; ... ... if ((fp = fopen(DISKSTATS, "r")) == NULL) return; while (fgets(line, sizeof(line), fp) != NULL) { /* major minor name rio rmerge rsect ruse wio wmerge wsect wuse running use aveq */ i = sscanf(line, "%u %u %s %lu %lu %lu %lu %lu %lu %lu %u %u %u %u", &major, &minor, dev_name, &rd_ios, &rd_merges_or_rd_sec, &rd_sec_or_wr_ios, &rd_ticks_or_wr_sec, &wr_ios, &wr_merges, &wr_sec, &wr_ticks, &ios_pgr, &tot_ticks, &rq_ticks); if (i == 14) { /* Device or partition */ if (!dlist_idx && !DISPLAY_PARTITIONS(flags) && !is_device(dev_name, ACCEPT_VIRTUAL_DEVICES)) continue; sdev.rd_ios = rd_ios; sdev.rd_merges = rd_merges_or_rd_sec; sdev.rd_sectors = rd_sec_or_wr_ios; sdev.rd_ticks = (unsigned int) rd_ticks_or_wr_sec; sdev.wr_ios = wr_ios; sdev.wr_merges = wr_merges; sdev.wr_sectors = wr_sec; sdev.wr_ticks = wr_ticks; sdev.ios_pgr = ios_pgr; sdev.tot_ticks = tot_ticks; sdev.rq_ticks = rq_ticks; } ... ... save_stats(dev_name, curr, &sdev, iodev_nr, st_hdr_iodev); } fclose(fp); } void write_json_ext_stat(int tab, unsigned long long itv, int fctr, struct io_hdr_stats *shi, struct io_stats *ioi, struct io_stats *ioj, char *devname, struct ext_disk_stats *xds, double r_await, double w_await) { xprintf0(tab, "{\"disk_device\": \"%s\", \"rrqm\": %.2f, \"wrqm\": %.2f, " "\"r\": %.2f, \"w\": %.2f, \"rkB\": %.2f, \"wkB\": %.2f, " "\"avgrq-sz\": %.2f, \"avgqu-sz\": %.2f, " "\"await\": %.2f, \"r_await\": %.2f, \"w_await\": %.2f, " "\"svctm\": %.2f, \"util\": %.2f}", devname, S_VALUE(ioj->rd_merges, ioi->rd_merges, itv), S_VALUE(ioj->wr_merges, ioi->wr_merges, itv), S_VALUE(ioj->rd_ios, ioi->rd_ios, itv), S_VALUE(ioj->wr_ios, ioi->wr_ios, itv), S_VALUE(ioj->rd_sectors, ioi->rd_sectors, itv) / fctr, S_VALUE(ioj->wr_sectors, ioi->wr_sectors, itv) / fctr, xds->arqsz, S_VALUE(ioj->rq_ticks, ioi->rq_ticks, itv) / 1000.0, xds->await, r_await, w_await, xds->svctm, shi->used ? xds->util / 10.0 / (double) shi->used : xds->util / 10.0); /* shi->used should never be zero here */ } void compute_ext_disk_stats(struct stats_disk *sdc, struct stats_disk *sdp, unsigned long long itv, struct ext_disk_stats *xds) { double tput = ((double) (sdc->nr_ios - sdp->nr_ios)) * HZ / itv; xds->util = S_VALUE(sdp->tot_ticks, sdc->tot_ticks, itv); xds->svctm = tput ? xds->util / tput : 0.0; /* * Kernel gives ticks already in milliseconds for all platforms * => no need for further scaling. */ xds->await = (sdc->nr_ios - sdp->nr_ios) ? ((sdc->rd_ticks - sdp->rd_ticks) + (sdc->wr_ticks - sdp->wr_ticks)) / ((double) (sdc->nr_ios - sdp->nr_ios)) : 0.0; xds->arqsz = (sdc->nr_ios - sdp->nr_ios) ? ((sdc->rd_sect - sdp->rd_sect) + (sdc->wr_sect - sdp->wr_sect)) / ((double) (sdc->nr_ios - sdp->nr_ios)) : 0.0; }
實際上就是 /proc/diskstats 中的 filed10 消耗時間佔比。
在 Linux 中,每一個 IO 的平均耗時用 await 表示,包括了磁盤處理時間以及隊列排隊時間,因此該指標不能徹底表示設備的性能,包括 IO 調度器等,都會影響該參數值。通常來講,內核中的隊列時間幾乎能夠忽略不計,而 SSD 不一樣產品從 0.01ms 到 1.00 ms 不等,對於機械磁盤能夠參考 io 。
這個指標在 iostat 以及 sar 上都有註釋 Warning! Do not trust this field any more. This field will be removed in a future sysstat version.,該指標包括了隊列中排隊時間以及磁盤處理時間。
實際上,在 UNIX 中一般經過 avserv 表示硬盤設備的性能,它是指 IO 請求從 SCSI 層發出到 IO 完成以後返回 SCSI 層所消耗的時間,不包括在 SCSI 隊列中的等待時間,因此該指標體現了硬盤設備處理 IO 的速度,又被稱爲 disk service time,若是 avserv 很大,那麼確定是硬件出問題了。
從 top 中的解釋來講,就是 CPU 在 time waiting for I/O completion 中消耗的時間,而實際上,若是須要等待 IO 完成,實際 CPU 不會一直等待該進程,而是切換到另外的進程繼續執行。
因此在 Server Fault 中將該指標定義爲以下的含義:
iowait is time that the processor/processors are waiting (i.e. is in an idle state and does nothing), during which there in fact was outstanding disk I/O requests.
那麼對於多核,iowait 是隻在一個 CPU 上,仍是會消耗在全部 CPU ?若是有 4 個 CPUs,那麼最大是 20% 仍是 100% ?
能夠經過 dd if=/dev/sda of=/dev/null bs=1MB 命令簡單測試下,通常來講,爲了提升 cache 的命中率,會一直使用同一個 CPU ,不過部分系統會將其均分到不一樣的 CPU 上作均衡。另外,也能夠經過 taskset 1 dd if=/dev/sda of=/dev/null bs=1MB 命令將其綁定到單個 CPU 上。
按照二進制形式,從最低位到最高位表明物理 CPU 的 #0、#一、#二、…、#n 號核,例如:0x01 表明 CPU 的 0 號核,0x05 表明 CPU 的 0 號和 2 號核。
例如,將 9865 綁定到 #0、#1 上面,命令爲 taskset -p 0x03 9865;將進程 9864 綁定到 #一、#二、#5~#11 號核上面,從 1 開始計數,命令爲 taskset -cp 1,2,5-11 9865 。
能夠看出,若是是 top <1> 顯示各個 CPU 的指標,則是 100% 計算,而總的統計值則按照 25% 統計。
問題1:如何獲取真正的 serviice time(svctm)
?能夠經過 fio 等壓測工具,經過設置爲同步 IO,僅設置一個線程,以及 io_depth 也設置爲 1,壓測出來的就是真正的 service time(svctm)。
問題2:怎樣得到 IO 最大並行度,或者說如何得到真正的 util% 使用率?
最大並行度 = 壓測滿(r/s + w/s) * (真實svctm / 1000)
公式基本同樣,只是將 svctm 換成了上次計算的值。
問題3:如何判斷存在 IO 瓶頸了?
實際上在如上算出真實的最大並行度,能夠直接參考 avgqu-sz 值,也就是隊列中的值,通常來講超過兩倍可能就會存在問題。例如一塊機械盤,串行 IO (每次1個IO),那麼 avgqu-sz 持續大於 2 既表明持續有兩倍讀寫能力的 IO 請求在等待;或者當 RAIDs、SSD 等並行,這裏假定並行度爲 5.63,那麼 avgqu-sz 持續大於10,才表明持續有兩倍讀寫能力的 IO 請求在等待。
iotop pidstat iodump 進程級
一個 Python 腳本,能夠查看官網 guichaz.free.fr/iotop,另外一個經過 C 實現的監控可參考 IOPP。
pidstat 用於統計進程的狀態,包括 IO 情況,能夠查看當前系統哪些進程在佔用 IO 。
----- 只顯示IO
pidstat -d 1
上述二者均是統計的 /proc/pid/io 中的信息;另可參考 io/iotop.stp,這是 iotop 的複製版。
iodump 是一個統計每個進程(線程)所消耗的磁盤 IO 工具,是一個 perl 腳本,其原理是打開有關 IO 的內核記錄消息開關,然後讀取消息而後分析輸出。
echo 1 >/proc/sys/vm/block_dump # 打開有關IO內核消息的開關 # while true; do sleep 1; dmesg -c ; done | perl iodump # 而後分析
上述輸出的單位爲塊 (block),每塊的大小取決於建立文件系統時指定的塊大小。
ioprofile 命令本質上等價於 lsof + strace,能夠查看當前進程。
blktrace
blktrace 是塊層 IO 路徑監控和分析工具,做者 Jens Axboe 是內核 IO 模塊的維護者,目前就任於 FusionIO,同時他仍是著名 IO 評測工具 fio 的做者,使用它能夠深刻了解 IO 通路。
yum install blktrace # 在CentOS中安裝
$ make # 解壓源碼後直接安裝
$ man -l doc/blktrace.8 # 查看幫助
$ grep 'CONFIG_BLK_DEV_IO_TRACE' /boot/config-`uname -r` 大部分實現代碼都在 blktrace.c,利用 tracepoint 的特性,註冊了一些 trace 關鍵點,能夠查看 Documentation/tracepoint.txt 文件;交互機制利用了 relayfs 特性,看看 Documentation/filesystems/relay.txt
其源碼能夠從 brick.kernel.dk 下載,詳細使用參考 blktrace User Guide 原理
該工具包括了內核空間和用戶空間兩部分實現,內核空間裏主要是給塊層 IO 路徑上的關鍵點添加 tracepoint,而後藉助於 relayfs 系統特性將收集到的數據寫到 buffer 去,再從用戶空間去收集。
目前,內核空間部分的代碼已經集成到主線代碼裏面去了,能夠看看內核代碼 block/blktrace.c 文件是否是存在,編譯的時候把對應的這個 trace 選項選擇上就能夠了。
。
此時撈取的信息還比較原始,能夠經過用戶空間的 blkparse、btt、seekwatcher 這樣的工具來分析收集到的數據。
注意,使用以前要確保 debugfs 已經掛載,默認會掛載在 /sys/kernel/debug 。
使用
典型的使用以下,其中 /dev/sdaa、/dev/sdc 做爲 LVM volume adb3/vol。
blktrace -d /dev/sda -o - | blkparse -i - -o blkparse.out # 簡單用法,Ctrl-C退出 # btrace /dev/sda # 同上 # blktrace /dev/sdaa /dev/sdc & # 離線處理。1. 後臺運行採集 % mkfs -t ext3 /dev/adb3/vol # 2. 作些IO操做 % kill -15 9713 # 3. 中止採集 % blkparse sdaa sdc sdo > events # 4. 解析後查看
在 blktrace 中,-d 表示監控哪一個設備,-o - 表示將監控輸出到標準輸出;在 blkparse 中,-i - 表示從標準輸入獲取信息,-o 表示將解析的內容記錄在 blkparse.out 。
以下是輸出的詳細信息。
其中 event 對應了事件表;後面一列表明瞭操做類型,包括了 R(read)、W(write)、B(barrier operation)、S(synchronous operation),其中 event 有以下類型:
詳解
仍以以下簡單命令爲例。
$ blktrace -d /dev/sda -o sda # 輸出 sda.blktrace.N 文件,N 爲物理 CPU 個數。 $ ls /sys/kernel/debug/block/sda # 查看debugfs中的文件 dropped msg trace0 trace1 trace2 trace3 $ blkparse -i sda.blktrace.0 # 解析成可讀內容 $ blkrawverify sda # 校驗,其中sda爲blktrace的-o選項
其中 blktrace 經過 ioctl() 執行 BLKTRACESETUP、BLKTRACESTART、BLKTRACESTOP、BLKTRACETEARDOWN 操做,此時會在 debugfs 目錄的 block/DEV 目錄下寫入數據。
FIO
FIO 是個很是強大的 IO 性能測試工具,其做者 Jens Axboe 是 Linux 內核 IO 部分的 maintainer,能夠絕不誇張的說,若是你把全部的 FIO 參數都搞明白了,基本上就把 Linux IO 協議棧的問題搞的差很少明白了。
一個 IO 壓測工具,源碼以及二進制文件能夠參考 github-axboe,或者直接從 freecode.com 上下載。另外,該工具同時提供了一個圖形界面 gfio 。
在 CentOS 中能夠經過以下方式安裝。
yum --enablerepo=epel install fio
源碼編譯
能夠直接從 github 上下載源碼,而後經過以下方式進行編譯。
----- 編譯,注意依賴libaio
$ make
----- 查看幫助
$ man -l fio.1
----- 經過命令行指定參數,進行簡單測試
$ fio --name=global --rw=randread --size=128m --name=job1 --name=job2
----- 也能夠經過配置文件進行測試
$ cat foobar.fio [global] rw=randread size=128m [job1] [job2] $ fio foobar.fio
能夠經過命令行啓動,不過此時參數較多,可使用配置文件。
源碼解析
其版本經過 FIO_VERSION 宏定義,並經過 fio_version_string 變量定義。
main() |-parse_options() | |-parse_cmd_line() 解析命令行,如-i顯示全部的ioengines | | |-add_job() file1: xxxxxx 打印job信息 | |-log_info() fio-2.10.0 |-fio_backend() | |-create_disk_util_thread() 用於實時顯示狀態 | | |-setup_disk_util() | | |-disk_thread_main() 經過pthread建立線程 | | |-print_thread_status() | | | |-run_threads() Starting N processes | | |-setup_files() Laying out IO file(s) | | |-pthread_create() 若是配置使用線程,調用thread_main | | |-fork() 或者調用建立進程,一樣爲thread_main | | | |-show_run_stats() | |-show_thread_status_normal() 用於顯示最終的狀態 | |-show_latencies() 顯示lat信息 | |-... ... CPU、IO depth ioengines 經過 fio_libaio_register() 相似的函數初始化。
其它
ionice 獲取或設置程序的 IO 調度與優先級。 ionice [-c class] [-n level] [-t] -p PID... ionice [-c class] [-n level] [-t] COMMAND [ARG] ----- 獲取進程ID爲8九、91的IO優先級 $ ionice -p 89 9