iostat等命令看到的是系統級的統計,比如下例中我們看到/dev/sdb很忙,如果要追查是哪個進程導致的I/O繁忙,應該怎么辦?
# iostat -xd ... Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util sda 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 sdb 0.00 0.00 6781.67 0.00 3390.83 0.00 1.00 0.85 0.13 0.13 0.00 0.13 85.03 dm-0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 dm-1 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 dm-2 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 ...
進程的內核數據結構中包含了I/O數量的統計:
struct task_struct { ... struct task_io_accounting ioac; ... };
可以直接在 /proc/<pid>/io 中看到:
# cat /proc/3088/io rchar: 125119 //在read(),pread(),readv(),sendfile等系統調用中讀取的字節數 wchar: 632 //在write(),pwrite(),writev(),sendfile等系統調用中寫入的字節數 syscr: 111 //調用read(),pread(),readv(),sendfile等系統調用的次數 syscw: 79 //調用write(),pwrite(),writev(),sendfile等系統調用的次數 read_bytes: 425984 //進程讀取的物理I/O字節數,包括mmap pagein,在submit_bio()中統計的 write_bytes: 0 //進程寫出的物理I/O字節數,包括mmap pageout,在submit_bio()中統計的 cancelled_write_bytes: 0 //如果進程截短了cache中的文件,事實上就減少了原本要發生的寫I/O
我們關心的是實際發生的物理I/O,從上面的注釋可知,應該關注 read_bytes 和 write_bytes。請注意這都是歷史累計值,從進程開始執行之初就一直累加。如果要觀察動態變化情況,可以使用 pidstat 命令,它就是利用了/proc/<pid>/io 中的原始數據計算單位時間內的增量:
# pidstat -d 2 2 Linux 3.10.0-229.14.1.el7.x86_64 (bj71s060) 11/16/2016 _x86_64_ (2 CPU) 12:30:15 PM UID PID kB_rd/s kB_wr/s kB_ccwr/s Command 12:30:17 PM 0 14772 3362.25 0.00 0.00 dd 12:30:17 PM UID PID kB_rd/s kB_wr/s kB_ccwr/s Command 12:30:19 PM 0 14772 3371.25 0.00 0.00 dd
另外還有一個常用的命令 iotop 也可以觀察進程的動態I/O:
Actual DISK READ: 3.31 M/s | Actual DISK WRITE: 0.00 B/s TID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND 14772 be/4 root 3.31 M/s 0.00 B/s 0.00 % 61.99 % dd if=/de~lag=direct 1 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % systemd -~rialize 24 2 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.00 % [kthreadd] ...
pidstat 和 iotop 也有不足之處,它們無法具體到某個硬盤設備,如果系統中有很多硬盤設備,都在忙,而我們只想看某一個特定的硬盤的I/O來自哪些進程,這兩個命令就幫不上忙了。怎么辦呢?可以用上萬能工具SystemTap。比如:我們希望找出訪問/dev/sdb的進程,可以用下列腳本,它的原理是對submit_bio下探針:
#! /usr/bin/env stap global device_of_interest probe begin { device_of_interest = $1 printf ("device of interest: 0x%x\n", device_of_interest) } probe kernel.function("submit_bio") { dev = $bio->bi_bdev->bd_dev if (dev == device_of_interest) printf ("[%s](%d) dev:0x%x rw:%d size:%d\n", execname(), pid(), dev, $rw, $bio->bi_size) }
這個腳本需要在命令行參數中指定需要監控的硬盤設備號,得到這個設備號的方法如下:
# ll /dev/sdb brw-rw----. 1 root disk 8, 16 Oct 24 15:52 /dev/sdb Major number(12-bit): 8 i.e. 0x8 Minor number(20-bit): 16 i.e. 0x00010 合在一起得到設備號: 0x800010 注意是十六進制
執行腳本,我們看到:
# ./dev_task_io.stp 0x800010 device of interest: 0x800010 [dd](31202) dev:0x800010 rw:0 size:512 [dd](31202) dev:0x800010 rw:0 size:512 [dd](31202) dev:0x800010 rw:0 size:512 [dd](31202) dev:0x800010 rw:0 size:512 [dd](31202) dev:0x800010 rw:0 size:512 ...
結果很令人滿意,我們看到是進程號為31202的dd命令在對/dev/sdb進行讀操作。
相關內容:
Linux的設備管理是和文件系統緊密結合的,把設備和文件關聯起來,這樣系統調用可以直接用操作文件一樣的方法來操作設備。
各種設備都以文件的形式存放在/dev目錄下,稱為設備文件。
應用程序可以打開、關閉和讀寫這些設備文件,完成對設備的操作,就像操作普通的數據文件一樣。
為了管理這些設備,系統為設備編了號,每個設備號又分為主設備號和次設備號。
主設備號用來區分不同類型的設備,而次設備號用來區分同一類型內的多個設備(及其設備分區)。
查看主設備號: cat /proc/devices
查看當前設備的主次設備號: ls -l /dev
一個Linux系統,當前所有注冊設備的主設備號可以通過/proc接口查看:
[root@localhost lenky]# cat /proc/devices Character devices: 1 mem 4 /dev/vc/0 4 tty 4 ttyS 5 /dev/tty 5 /dev/console 5 /dev/ptmx 7 vcs 10 misc 13 input 14 sound 21 sg 29 fb 99 ppdev 116 alsa 128 ptm 136 pts 162 raw 180 usb 189 usb_device 202 cpu/msr 203 cpu/cpuid 251 hidraw 252 usbmon 253 bsg 254 rtc Block devices: 1 ramdisk 2 fd 259 blkext 7 loop 8 sd 9 md 11 sr 65 sd 66 sd 67 sd 68 sd 69 sd 70 sd 71 sd 128 sd 129 sd 130 sd 131 sd 132 sd 133 sd 134 sd 135 sd 253 device-mapper 254 mdp [root@localhost lenky]#
字符設備與塊設備的主設備號並不沖突,所有兩個都可以有主設備號為1的設備,如果要繼續查看次設備號,那么可以通過直接ls -l來查看,比如查看主設備號為8的設備的次設備號:
[root@localhost lenky]# ls -Rl /dev/* | grep " 8," brw-rw----. 1 root disk 8, 0 Jan 12 06:24 /dev/sda brw-rw----. 1 root disk 8, 1 Jan 12 06:24 /dev/sda1 brw-rw----. 1 root disk 8, 2 Jan 12 06:24 /dev/sda2 brw-rw----. 1 root disk 8, 16 Jan 12 06:25 /dev/sdb brw-rw----. 1 root disk 8, 17 Jan 12 06:25 /dev/sdb1 brw-rw----. 1 root disk 8, 32 Jan 12 06:29 /dev/sdc brw-rw----. 1 root disk 8, 33 Jan 12 06:29 /dev/sdc1 brw-rw----. 1 root disk 8, 34 Jan 12 06:29 /dev/sdc2 brw-rw----. 1 root disk 8, 35 Jan 12 06:29 /dev/sdc3 [root@localhost lenky]#
上面的0,1,2,16,17等都是次設備號,用於區分標記各個sd硬盤或分區。查看系統所有的塊設備:
[root@localhost lenky]# grep ^ /sys/class/block/*/dev /sys/class/block/dm-0/dev:253:0 /sys/class/block/dm-1/dev:253:1 /sys/class/block/dm-2/dev:253:2 /sys/class/block/fd0/dev:2:0 /sys/class/block/loop0/dev:7:0 /sys/class/block/loop1/dev:7:1 /sys/class/block/loop2/dev:7:2 /sys/class/block/loop3/dev:7:3 /sys/class/block/loop4/dev:7:4 /sys/class/block/loop5/dev:7:5 /sys/class/block/loop6/dev:7:6 /sys/class/block/loop7/dev:7:7 /sys/class/block/ram0/dev:1:0 /sys/class/block/ram10/dev:1:10 /sys/class/block/ram11/dev:1:11 /sys/class/block/ram12/dev:1:12 /sys/class/block/ram13/dev:1:13 /sys/class/block/ram14/dev:1:14 /sys/class/block/ram15/dev:1:15 /sys/class/block/ram1/dev:1:1 /sys/class/block/ram2/dev:1:2 /sys/class/block/ram3/dev:1:3 /sys/class/block/ram4/dev:1:4 /sys/class/block/ram5/dev:1:5 /sys/class/block/ram6/dev:1:6 /sys/class/block/ram7/dev:1:7 /sys/class/block/ram8/dev:1:8 /sys/class/block/ram9/dev:1:9 /sys/class/block/sda1/dev:8:1 /sys/class/block/sda2/dev:8:2 /sys/class/block/sda/dev:8:0 /sys/class/block/sdb1/dev:8:17 /sys/class/block/sdb/dev:8:16 /sys/class/block/sdc1/dev:8:33 /sys/class/block/sdc2/dev:8:34 /sys/class/block/sdc3/dev:8:35 /sys/class/block/sdc/dev:8:32 /sys/class/block/sr0/dev:11:0 [root@localhost lenky]#
關於每個主設備號:次設備號對應設備的功能在Linux幫助文檔里可以找到:http://lxr.linux.no/#linux+v2.6.38.8/Documentation/devices.txt
在內核2.6.9之后,Linux系統上出現了一種名為device-mapper的存儲映射機制,這種機制的作用簡單來說就是給用戶提供簡單方便而又豐富的存儲管理接口,在這種機制以及相關工具的幫助下,用戶能夠方便的自定義存儲資源管理策略。
通過一些映射規則,device-mapper機制能夠從原有的物理磁盤或邏輯磁盤中划分映射出新的邏輯磁盤,可以看到這是一個遞歸的映射機制,理論上可無限迭代。舉個例子,系統有物理磁盤A和B,從物理磁盤A中映射出新的邏輯磁盤C、D、E,從物理磁盤B中映射出新的邏輯磁盤F、G,又可以從物理磁盤A和邏輯磁盤F中映射出新的邏輯磁盤H,等等。關於這方面,請參考:http://www.ibm.com/developerworks/cn/linux/l-devmapper/、http://sources.redhat.com/dm/等資源,不管是原物理磁盤還是通過device-mappe機制映射出來的新邏輯磁盤,在Linux操作系統看來都一樣,一切皆文件,復雜邏輯被隔離在底部。
[root@localhost lenky]# ls -Rl /dev/* | grep " 8," brw-rw----. 1 root disk 8, 0 Jan 12 06:24 /dev/sda brw-rw----. 1 root disk 8, 1 Jan 12 06:24 /dev/sda1 brw-rw----. 1 root disk 8, 2 Jan 12 06:24 /dev/sda2
一般說的各個分區相加等於硬盤,有個隱含說明就是硬盤內各個分區的硬盤存儲空間容量相加等於硬盤的硬盤存儲空間容量。
一個硬盤有一個描述硬盤的信息,而一個分區有一描述分區的信息,將硬盤內各個分區的描述分區的信息拼接在一起也得不到關於硬盤的信息,所以給硬盤配上一個次設備號是有必要不多余的。
[root@localhost lenky]# ls -Rl /dev/* | grep " 8," brw-rw----. 1 root disk 8, 0 Jan 12 06:24 /dev/sda brw-rw----. 1 root disk 8, 1 Jan 12 06:24 /dev/sda1 brw-rw----. 1 root disk 8, 2 Jan 12 06:24 /dev/sda2 brw-rw----. 1 root disk 8, 16 Jan 12 06:25 /dev/sdb brw-rw----. 1 root disk 8, 17 Jan 12 06:25 /dev/sdb1 brw-rw----. 1 root disk 8, 32 Jan 12 06:29 /dev/sdc brw-rw----. 1 root disk 8, 33 Jan 12 06:29 /dev/sdc1 brw-rw----. 1 root disk 8, 34 Jan 12 06:29 /dev/sdc2 brw-rw----. 1 root disk 8, 35 Jan 12 06:29 /dev/sdc3
我們看到/dev/sd*設備名的設備類型(即指的是IDE硬盤),這里有三個不同的IDE硬盤,其主設備號以及其分區的主設備號都是8。