簡介
可以通過如下命令查看與 IO 相關的系統資訊,
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
)
在 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,
塊是檔案系統的抽象,而非磁盤的屬性,一般是 Sector Size 的倍數;扇區大小則是磁盤的物理屬性,它是磁盤設備尋址的最小單元,另外,內核中要求 Block_Size = Sector_Size * (2的n次方),且 Block_Size <= 記憶體的 Page_Size (頁大小),
【文章福利】小編推薦自己的C/C++Linux群:832218493!整理了一些個人覺得比較好的學習書籍、視頻資料共享在里面,有需要的可以自行添加哦!~


磁盤使用空間
實際上是通過 statvfs() 方法查詢磁盤資料,可以通過如下命令查看,
$ python -c 'import os; os.statvfs("/")'
其空間占用大致如下,
+--------------------------+----------------+-------------------------------------------------------+
| | | |
+--------------------------+----------------+-------------------------------------------------------+
|<-- 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等),甚至是制造商、機器型號、序列號等資訊,
$ dmesg | grep scsi
監控指標
簡單列舉磁盤監控時常見的指標,
IOPS 每秒IO數
對磁盤來說,一次磁盤的連續讀或寫稱為一次磁盤 IO,當傳輸小塊不連續資料時,該指標有重要參考意義,
Throughput 吞吐量
硬碟傳輸資料流的速度,單位一般為 MB/s,在傳輸大塊不連續資料的資料,該指標有重要參考作用,
IO平均大小
實際上就是吞吐量除以 IOPS,用于判斷磁盤使用模式,一般大于 32K 為順序讀取為主,否則隨機讀取為主,
Utilization 磁盤活動時間百分比
磁盤處于活動狀態 (資料傳輸、尋道等) 的時間百分比,也即磁盤利用率,一般該值越高對應的磁盤資源爭用越高,
Service Time 服務時間
磁盤讀寫操作執行的時間,對于機械磁盤包括了尋道、旋轉、資料傳輸等,與磁盤性能相關性較高,另外,也受 CPU、記憶體影響,
Queue Length 等待佇列長度
待處理的 IO 請求的數目,注意,如果該磁盤為磁盤陣列虛擬的邏輯驅動器,需要除以實際磁盤數,以獲取單盤的 IO 佇列,
Wait Time 等待時間
在佇列中排隊的時間,
iostat 系統級
除了可以通過該命令查看磁盤資訊之外,還可以用來查看 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 消耗時間占比,
await
在 Linux 中,每個 IO 的平均耗時用 await 表示,包括了磁盤處理時間以及佇列排隊時間,所以該指標不能完全表示設備的性能,包括 IO 調度器等,都會影響該引數值,一般來說,內核中的佇列時間幾乎可以忽略不計,而 SSD 不同產品從 0.01ms 到 1.00 ms 不等,對于機械磁盤可以參考 io ,
svctm
這個指標在 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 很大,那么肯定是硬體出問題了,
iowait
從 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、#1、#2、…、#n 號核,例如:0x01 代表 CPU 的 0 號核,0x05 代表 CPU 的 0 號和 2 號核,
例如,將 9865 系結到 #0、#1 上面,命令為 taskset -p 0x03 9865;將行程 9864 系結到 #1、#2、#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 業務級
ioprofile 命令本質上等價于 lsof + strace,可以查看當前行程,
blktrace
blktrace 是塊層 IO 路徑監控和分析工具,作者 Jens Axboe 是內核 IO 模塊的維護者,目前就職于 FusionIO,同時他還是著名 IO 評測工具 fio 的作者,使用它可以深入了解 IO 通路,
yum install blktrace # 在CentOS中安裝
```bash
$ make # 解壓原始碼后直接安裝
$ man -l doc/blktrace.8 # 查看幫助
```bash
$ 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為89、91的IO優先級
$ ionice -p 89 9
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/224884.html
標籤:其他
