1. 程式人生 > >系統技術非業餘研究 » Iostat看不到裝置統計資訊的原因分析

系統技術非業餘研究 » Iostat看不到裝置統計資訊的原因分析

最近在把玩些高速的SSD和nvram裝置的時候,發現iostat無法統計到這些裝置的資訊,很是奇怪,於是分析和總結了一把,挺有意思的。

現象描述如下:

# uname -a
Linux dr4000 2.6.32-131.17.1.el6.x86_64 #1 SMP Wed Oct 5 17:19:54 CDT 2011 x86_64 x86_64 x86_64 GNU/Linux
# ls -al /dev/nvdisk0
brw-rw-r– 1 root root 252, 0 Mar 10 16:18 /dev/nvdisk0
# iostat -d
Linux 2.6.32-131.17.1.el6.x86_64 (dr4000) 03/10/2012 _x86_64_ (24 CPU)

Device: tps Blk_read/s Blk_wrtn/s Blk_read Blk_wrtn
sda 60.74 200.21 95.44 55171731 26299534

iostat很奇怪的看不到nvdisk0的IO統計資訊.

開始我們的分析之旅,先簡單的用strace看下iostat從那裡讀取這些統計資訊的:

# strace iostat -d 
execve("/usr/bin/iostat", ["iostat", "-d"], [/* 25 vars */]) = 0
...
open("/sys/devices/system/cpu", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 3
fcntl(3, F_GETFD)                       = 0x1 (flags FD_CLOEXEC)
getdents(3, /* 35 entries */, 32768)    = 1056
stat("/sys/devices/system/cpu/cpu0", {st_mode=S_IFDIR|0755, st_size=0, ...}) = 0
...
getdents(3, /* 0 entries */, 32768)     = 0
close(3)                                = 0
open("/proc/diskstats", O_RDONLY)       = 3
fstat(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ffa8ea55000
read(3, "   1       0 ram0 0 0 0 0 0 0 0 "..., 1024) = 1024
read(3, "881869 2312902 26299638 3731982 "..., 1024) = 464
read(3, "", 1024)                       = 0
close(3)                                = 0
munmap(0x7ffa8ea55000, 4096)            = 0
open("/etc/localtime", O_RDONLY)        = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=405, ...}) = 0
fstat(3, {st_mode=S_IFREG|0644, st_size=405, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ffa8ea55000
read(3, "TZif2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\3\0\0\0\0"..., 4096) = 405
lseek(3, -240, SEEK_CUR)                = 165
read(3, "TZif2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\0\0\0\3\0\0\0\0"..., 4096) = 240
close(3)                                = 0
munmap(0x7ffa8ea55000, 4096)            = 0
uname({sys="Linux", node="dr4000", ...}) = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 23), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ffa8ea55000
write(1, "Linux 2.6.32-131.17.1.el6.x86_64"..., 73Linux 2.6.32-131.17.1.el6.x86_64 (dr4000)     03/10/2012      _x86_64_        (24 CPU)
) = 73
write(1, "\n", 1
)                       = 1
rt_sigaction(SIGALRM, {0x404230, [ALRM], SA_RESTORER|SA_RESTART, 0x3ff7c32ac0}, {SIG_DFL, [], 0}, 8) = 0
alarm(0)                                = 0
munmap(0x7ffa8ea55000, 4096)            = 0
open("/proc/uptime", O_RDONLY)          = 3
fstat(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ffa8ea55000
read(3, "275660.93 6524235.15\n", 1024) = 21
close(3)                                = 0
munmap(0x7ffa8ea55000, 4096)            = 0
open("/proc/stat", O_RDONLY)            = 3
fstat(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ffa8ea55000
read(3, "cpu  1702243 167 1971793 6524235"..., 1024) = 1024
read(3, "0\ncpu20 26162 0 16915 27374407 1"..., 1024) = 1024
read(3, " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"..., 1024) = 1024
read(3, " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"..., 1024) = 797
read(3, "", 1024)                       = 0
close(3)                                = 0
munmap(0x7ffa8ea55000, 4096)            = 0
open("/proc/diskstats", O_RDONLY)       = 3
fstat(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ffa8ea55000
read(3, "   1       0 ram0 0 0 0 0 0 0 0 "..., 1024) = 1024
access("/sys/block/ram0", F_OK)         = 0
open("/etc/sysconfig/sysstat.ioconf", O_RDONLY) = 4
fstat(4, {st_mode=S_IFREG|0644, st_size=6253, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ffa8ea54000
read(4, "#\n# sysstat.ioconf\n#\n#  Copyrigh"..., 4096) = 4096
read(4, "8:7:\n136:48:8:\n137:48:9:\n138:48:"..., 4096) = 2157
read(4, "", 4096)                       = 0
close(4)                                = 0
munmap(0x7ffa8ea54000, 4096)            = 0
access("/sys/block/ram1", F_OK)         = 0
..
access("/sys/block/loop7", F_OK)        = 0
read(3, "881869 2312902 26299638 3731982 "..., 1024) = 464
access("/sys/block/sda", F_OK)          = 0
...
access("/sys/block/nvdisk0", F_OK)      = 0
read(3, "", 1024)                       = 0
close(3)                                = 0
munmap(0x7ffa8ea55000, 4096)            = 0
stat("/etc/localtime", {st_mode=S_IFREG|0644, st_size=405, ...}) = 0
write(1, "", 0)                         = 0
write(1, "Device:            tps", 22Device:            tps)  = 22
write(1, "   Blk_read/s   Blk_wrtn/s   Blk"..., 49   Blk_read/s   Blk_wrtn/s   Blk_read   Blk_wrtn
) = 49
write(1, "sda          ", 13sda          )           = 13
write(1, "    60.72       200.14        95"..., 58    60.72       200.14        95.41   55171731   26299638
) = 58
write(1, "\n", 1
)                       = 1
exit_group(0)                           = ?

很容易看出,主要IO相關資訊是從/proc/diskstats讀取的。 我們再來看下這個檔案的內容:

# cat /proc/diskstats 
   1       0 ram0 0 0 0 0 0 0 0 0 0 0 0
..
   1      15 ram15 0 0 0 0 0 0 0 0 0 0 0
   7       0 loop0 0 0 0 0 0 0 0 0 0 0 0
..
   7       7 loop7 0 0 0 0 0 0 0 0 0 0 0
   8       0 sda 11856307 92646 55171731 7784368 4881904 2312902 26299918 3731991 0 397111 11495597
...
   8       5 sda5 11832182 79153 54227969 7705146 4618019 600324 10473322 1854282 0 314124 9538788
 252       0 nvdisk0 0 0 0 0 0 0 0 0 0 0 0

順手開啟核心程式碼block/genhd.c可以看到和diskstats統計相關的程式碼:

        seq_printf(s, "%4d %4d %s %lu %lu %llu %u %lu %lu %llu %u %u %u %u\n",
                gp->major, n + gp->first_minor, disk_name(gp, n, buf),
                disk_stat_read(gp, ios[0]), disk_stat_read(gp, merges[0]),
                (unsigned long long)disk_stat_read(gp, sectors[0]),
                jiffies_to_msecs(disk_stat_read(gp, ticks[0])),
                disk_stat_read(gp, ios[1]), disk_stat_read(gp, merges[1]),
                (unsigned long long)disk_stat_read(gp, sectors[1]),
                jiffies_to_msecs(disk_stat_read(gp, ticks[1])),
                gp->in_flight,
                jiffies_to_msecs(disk_stat_read(gp, io_ticks)),
                jiffies_to_msecs(disk_stat_read(gp, time_in_queue)));

這些欄位的意義如下:
Field 1 — # of reads issued
Field 2 — # of reads merged, field 6 — # of writes merged
Field 3 — # of sectors read
Field 4 — # of milliseconds spent reading
Field 5 — # of writes completed
Field 7 — # of sectors written
Field 8 — # of milliseconds spent writing
Field 9 — # of I/Os currently in progress
Field 10 — # of milliseconds spent doing I/Os
Field 11 — weighted # of milliseconds spent doing I/Os
更具體的參看 這裡

上面的現象的直接原因是由於我們的裝置nvdisk0統計值全0引起的。

那為什麼這些值全部是空的呢,顯然是裝置沒有把這些資訊暴露出來。 我們看下核心更新這些統計值的部分。
注意:為了簡單我就抄了2.6.18相關程式碼,因為他的程式碼集中在 ll_rw_blk.c 檔案中,而2.6.32分散到其他檔案,不好交代事情。

$ grep -rin _stat_add .
./ll_rw_blk.c:2744:             __disk_stat_add(disk, time_in_queue,
./ll_rw_blk.c:2746:             __disk_stat_add(disk, io_ticks, (now - disk->stamp));
./ll_rw_blk.c:2762:             part_stat_add(part, time_in_queue,
./ll_rw_blk.c:2764:             part_stat_add(part, io_ticks, (now - ps->stamp));
./ll_rw_blk.c:3379:             all_stat_add(req->rq_disk, sectors[rw],
./ll_rw_blk.c:3402:             __all_stat_add(disk, ticks[rw], duration, req->sector);

呼叫點主要是blk_account_io_done和blk_account_io_completion,而最終的源頭是void end_request(struct request *req, int uptodate); 也就是說如果裝置驅動程式不呼叫 end_request之類的函式更新統計的話,那我們就得不到。

我們的nvdisk0是marvell造的nvram卡,目前不單賣,幸運的隨機廠家附送了驅動程式程式碼(謝謝老棟同學指出困惑),我們來看下:
mvwam-1.6.2/driver/lokidrv.c

#define NV_BDEV_SECTOR_SZ       512  

static int
nv_bdev_create(nvram_hba_t *nvhba)
{
...
        /*                                                                                                                                                              
         * Initialize block device queue                                                                                                                                
         */
        gd->queue = blk_alloc_queue(GFP_KERNEL);
...
        blk_queue_make_request(gd->queue, nvram_make_request);
...
        blk_queue_hardsect_size(gd->queue, NV_BDEV_SECTOR_SZ);
...
}
*                                                                                                                                                                      
 * Entry of block I/O                                                                                                                                                   
 *                                                                                                                                                                      
 * This function will construct prepare and submit block I/O to the queue                                                                                               
 *                                                                                                                                                                      
 * @q           - request queue from bio layer                                                                                                                          
 * @bio         - block io request                                                                                                                                      
 *                                                                                                                                                                      
 * @return 0 any way, extra info will be returned in nvram_endio                                                                                                        
 */
static int
nvram_make_request(struct request_queue *q, struct bio *bio)
{
...

        dma_req = kmem_cache_alloc(nv_dma_req_cache, GFP_KERNEL);
        if (dma_req == NULL)
                return -ENOMEM;

        memset(dma_req, 0, sizeof(*dma_req));
        dma_req->dma_next = NULL;
...
        dma_req->dma_done = nv_blk_req_done;
...
 bio_for_each_segment(bvec, bio, segno) {
                paddr = (page_to_pfn(bvec->bv_page) << PAGE_SHIFT)
                                            + bvec->bv_offset;
                valid_bytes = bvec->bv_len;

                ret = host_prd_try_merge(&cntxt, paddr, valid_bytes);
                if (ret != 0) {
                        /* max prd limit reached. need to split io */
                        ret = nv_split_io(nvhba, &dma_req, rw,
                                        &offset, &cntxt);
                        if (ret != 0) {
                                goto prep_err;
                        }
                        nr_splits++;
                        host_prd_try_merge(&cntxt, paddr, valid_bytes);
                }
        }

...
}

該驅動直接在nvram_make_request中DMA的幹活了,為了效能就不走標準的IO請求佇列,所以也就不會呼叫和佇列功能相關的end_request函式。

深層次的原因找到了!

其實還有其他的硬體廠商做的裝置為了效能,在這塊對使用者不是太友好,不知道能不能改進下,尊重使用者的使用習慣!

小結:有原始碼,一切瞭然!

祝大家玩得開心!

Post Footer automatically generated by wp-posturl plugin for wordpress.

相關推薦

系統技術業餘研究 » Iostat裝置統計資訊原因分析

最近在把玩些高速的SSD和nvram裝置的時候,發現iostat無法統計到這些裝置的資訊,很是奇怪,於是分析和總結了一把,挺有意思的。 現象描述如下: # uname -a Linux dr4000 2.6.32-131.17.1.el6.x86_64 #1 SMP Wed Oct 5 17:1

系統技術業餘研究 » oprofile抓到取樣資料問題和解決方法

最近有同學反映在某些新機器上做效能調優的時候, oprofile 有時抓不到資料,我之前也遇到這個情況,很是無語,今天特地驗證了下。 # 我們的作業系統和機器配置大概是這樣的: $sudo aspersa/summary # Aspersa System Summary Report ##

系統技術業餘研究 » gen_tcp接受連結時enfile的問題分析及解決

最近我們為了安全方面的原因,在RDS伺服器上做了個代理程式把普通的MYSQL TCP連線變成了SSL連結,在測試的時候,皓庭同學發現Tsung發起了幾千個TCP連結後Erlang做的SSL PROXY老是報告gen_tcp:accept返回{error, enfile}錯誤。針對這個問題,我展開了

系統技術業餘研究 » systemtap全域性變數自動列印的原因和解決方法

在執行stap的時候,經常會發現在指令碼結束執行的時候打出了很多無預期的東西,仔細一看都是些全域性變數的dump, 這個問題比較煩人. 我來演示下: $ cat > g.stp global xyz probe begin{ xyz=2010 exit(); } CT

系統技術業餘研究 » gen_tcp傳送程序被掛起起因分析及對策

最近有同學在gmail上問關於gen_tcp傳送程序被掛起的問題,問題描述的非常好,見底下: 第一個問題是關於port_command和gen_tcp:send的。從專案上線至今,我在tcp傳送的地方遇到過兩次問題,都跟port_command有關係。 起初程式的效能不好,我從各方面嘗試分析和優化

系統技術業餘研究 » gen_tcp傳送緩衝區以及水位線問題分析

前段時間有同學在線上問了個問題: 伺服器端我是這樣設的:gen_tcp:listen(8000, [{active, false}, {recbuf,1}, {buffer,1}]). 客戶端是這樣設的:gen_tcp:connect(“localhost”, 8000, [{active, f

系統技術業餘研究 » 計算機各系統元件的吞吐量和延遲 說話

這個圖挺好的,就是比較粗線條,有些東西不太完整/準確,比如現有的USB實際上是480M/12M和1.5M三種速率(暫不算3.0和無線的) 另:幫樓主補充一下這類資料的意義:如果對各種匯流排的速率和IOPS如果沒有概念和預估,寫程式時就只能Compile & Pray了。 Reply:O

系統技術業餘研究 » MYSQL資料庫網絡卡軟中斷平衡問題及解決方案

最近公司在MySQL的資料庫上由於採用了高速的如PCIe卡以及大記憶體,去年在壓力測試的時候突然發現數據庫的流量可以把一個千M網絡卡壓滿了。隨著資料庫的優化,現在流量可以達到150M,所以我們採用了雙網絡卡,在交換機上繫結,做LB的方式,提高系統的吞吐量。 但是在最近壓測試的一個數據庫中,mps

系統技術業餘研究 » 圖學TCP API以及狀態變遷

client的connect呼叫和server端之間的三根線應該對應著三次握手過程,這裡有一點問題。 client傳送的sync到達應該在server端accpet之前,所以橙色線應該指向accpet前面。 同樣,server端響應ack,sync是在accpet呼叫前的,因此粉色線也應該放在a

系統技術業餘研究 » TCP連結主動關閉發fin包奇怪行為分析

問題描述: 多隆同學在做網路框架的時候,發現一條tcp連結在close的時候,對端會收到econnrest,而不是正常的fin包. 通過抓包發現close系統呼叫的時候,我端發出rst報文, 而不是正常的fin。這個問題比較有意思,我們來演示下: $ erl Erlang R14B03 (e

系統技術業餘研究 » multitrace程式碼學習如何定製自己的dbg資訊

multitrace是ttb應用帶的一個例子,給了個例子讓使用者來格式化和定製自己的dbg資訊。 文件在這裡: The module multitrace.erl which can be found in the src directory of the Observer application

系統技術業餘研究 » systemtap函式呼叫棧資訊齊的原因和解決方法

有時候在看系統程式碼的時候,我們很難從原始碼中看出我們感興趣的函式是如何被呼叫的,因為呼叫路徑有可能太多。使用者空間的程式gdb設斷點是個好的方法,核心的就麻煩了。這時候systemtap可以幫忙, 比如: $uname -r 2.6.18-164.el5 $stap -V Syste

系統技術業餘研究

ItPub寫的文章“2017 年度 DB-Engines 資料庫冠軍得主:PostgreSQL 封王!”, 點選 這裡 進一步閱讀 升的最快的幾個資料庫,我簡單的無責任點評: PG資料庫是很老的資料庫,不過這幾年冉冉升起,因為是學院派的,有很好的學術和智力的支援,一直以來在資料庫的體系結構,程式碼

系統技術業餘研究 » MySQL資料庫架構的演化觀察

MySQL資料庫架構的演化觀察 December 14th, 2017 Categories: 資料庫 Tags: mysql

系統技術業餘研究 » inet_dist_connect_options

Erlang 17.5版本引入了inet_dist_{listen,connect}_options,對於結點間的互聯socket可以有更精細的控制,RPC的時候效能可以微調: raimo/inet_tcp_dist-priority-option/OTP-12476: Document ke

系統技術業餘研究 » 推薦工作機會

最後更新時間:2014/11/28 請賜簡歷至:[email protected], 感謝您對加入我們公司有興趣,我們希望能早日和您共事。 以下幾個職位1年內有效,歡迎內部轉崗:
 資深資料工程師 公司:阿里(核心系統資料庫組) 工作地點:杭州(西溪園區) 崗位描述: 分析雲服務產生的海

系統技術業餘研究 » 新的工作和研究方向

和大家更新下: 做了將近8年資料庫後,我的工作和研究方向將會延伸到虛擬化和計算相關的雲服務,希望能夠和大家一起進步,Happy New Year! 預祝大家玩得開心! Post Footer automatically generated by wp-posturl plugin for w

系統技術業餘研究 » 叢集引入inet_dist_{listen,connect}_options更精細引數微調

Erlang 17.5版本引入了inet_dist_{listen,connect}_options,對於結點間的互聯socket可以有更精細的控制,RPC的時候效能可以微調: raimo/inet_tcp_dist-priority-option/OTP-12476: Document ke

系統技術業餘研究 » 2017升的最快的幾個資料庫無責任點評

ItPub寫的文章“2017 年度 DB-Engines 資料庫冠軍得主:PostgreSQL 封王!”, 點選 這裡 進一步閱讀 升的最快的幾個資料庫,我簡單的無責任點評: PG資料庫是很老的資料庫,不過這幾年冉冉升起,因為是學院派的,有很好的學術和智力的支援,一直以來在資料庫的體系結構,程式碼

系統技術業餘研究 » Erlang 17.5引入+hpds命令列控制程序預設字典大小

Erlang 17.5釋出引入控制程序預設字典大小的命令列引數: Erlang/OTP 17.5 has been released Written by Henrik, 01 Apr 2015 Some highlights of the release are: ERTS: Added co