1一個塊IO的一生
從page cache到bio到request
當APP打開一個檔案,核心為檔案維護一個pagecache(磁盤的一個副本);
讀寫檔案時如果page cache命中,隻會讀寫記憶體不操作磁盤;沒有命中,才操作磁盤。
在核心用file結構體表示,可見其包含一個inode結構體,一個位址空間;
相關的幾個結構體在核心對應關系如下:
可見,當多個程序同時打開同一個檔案時,不同的file結構體對應同一個inode和同一個位址空間,位址空間是由一顆radix
tree維護(即pagecache),讀寫檔案時,檢視對應記憶體頁在page
cache中是否命中,若命中直接從記憶體空間讀寫;若不命中,申請一個記憶體頁,從磁盤讀入資料,挂到page
cache的radix tree中。
另外,page cache與磁盤的同步由位址空間操作函數readpage/writepage完成
對磁盤通路,有兩種方法:
a.裸磁盤直接通路;
b.通過檔案系統通路;
它們在核心page cached對應關系如下:
一個address_space對應一個inode。
free指令統計的buffer/cached,隻是統計上的差別;
buffer=操作裸分區的位址空間+檔案系統的中繼資料位址空間;
cached=檔案系統的位址空間(page cached)緩存;
但是對同一個磁盤,裸磁盤和檔案系統兩種方式同時操作的時候,同一個資料塊會被映射到不同的address_space,會産生同步的問題;
在用dd的時候,最好不要操作檔案系統資料。
2 O_DIRECT和O_SYNC
直接操作裸磁盤分區用O_DIRECT,核心沒有任何cache,直接操作磁盤;使用者可以根據資料特點,在使用者空間做cache。O_DIRECT申請記憶體要用posix_memalign接口;
而O_SYNC依然通過page cache,但是會立即寫入同步磁盤;
App通過page cache通路檔案和直接操作裸磁盤的模型,與CPU通過cache通路記憶體和DMA直接通路記憶體的模型非常類似;
這裡page cache是記憶體,file是磁盤分區資料塊;
當有一個程序啟用O_DIRECT模式,其他程序最好也用O_DIRECT;
3 BIO 流程blktrace
對于一個pagecache位址空間,指向的是page頁,檔案系統ext4讀取inode,将page轉化為磁盤資料塊,變成BIO請求;
BIO最終要轉化成request,然後request被塊裝置驅動程式調用完成;
Bio經過三個隊列變成request,三進三出
step1:原地蓄勢
把bio轉化為request,把request放入程序本程序的plug隊列;蓄勢多個request後,再進行洩洪。
可能合并相鄰bio為一個request;
Bio數量>=request數量;
多個程序,洩洪request到電梯;
step2.電梯排序
程序本地的plug隊列的request進入到電梯隊列,進行再次的合并、排序,執行QoS的排隊,之後按照QoS的結果,分發給dispatch隊列。電梯内部的實作,可以有各種各樣的隊列。
比如兩個程序需要讀鄰近的資料塊,可以合并為一個request
電梯排程層可以做QoS,設定程序通路IO的優先級;
step3.分發執行dispatch
電梯分發的request,被裝置驅動的request_fn()挨個取出來,派發真正的硬體讀寫指令到硬碟。這個分發的隊列,一般就是我們在塊裝置驅動裡面見到的request_queue了。request_queue完成真正的硬體操作;
工具ftrace
do.sh
#!/bin/bash
debugfs=/sys/kernel/debug
echo nop > $debugfs/tracing/current_tracer
echo 0 > $debugfs/tracing/tracing_on
echo `pidof read` > $debugfs/tracing/set_ftrace_pid
echo function_graph > $debugfs/tracing/current_tracer
echo vfs_read > $debugfs/tracing/set_graph_function
echo 1 > $debugfs/tracing/tracing_on
執行./read讀檔案
檢視trace過程
sudo cat /sys/kernel/debug/tracing/trace > t.txt
用VIM檢視t.txt,用.funcgrahp.vim插件打開可以合并/展開對應函數調用
vim -S ~/.funcgrahp.vim
4 IO排程算法,CFQ和ionice
檢視目前系統的IO排程算法
(base) leon\@pc:/sys/block/sda/queue\$ cat scheduler
noop deadline [cfq]
修改排程算法:
sudo echo cfg > scheduler
CFQ排程算法:類似程序排程的CFS算法
ionice –help
ionice –c 2 –n 0 dd if=/dev/sda of=/dev/null & //設定nice值=0
ionice –c 2 –n 8 dd if=/dev/sda of=/dev/null & //設定nice值=8
iotop檢視IO讀寫速度,有明顯差異
改成deadline政策
echo deadline > scheduler
此時盡管優先級不同,但兩個程序IO速度相近了;
5 cgroup與IO
cd /sys/fs/cgroup/blkio
a.建立群組A,B
Mkdir A;makedir B
cat blkiolweight //預設是500
分别把兩個程序放到A和B執行
cgexec -g blkio:A dd if=/dev/sda of=/dev/null iflag=direct &
cgexec -g blkio:B dd if=/dev/sda of=/dev/null iflag=direct &
檢視io占用iotop
預設是相近
b.修改群權重
echo 50 > blkio.weight
兩個程序優先級一樣,但IO速度差别很大;
c.cgroup還可以控制閥門,限制最大讀速度:
8:0磁盤的主次裝置号
echo "8:0 1048576" > /sys/fs/cgroup/blkio/A/blkio.throttle.read_bps_device
帶cache讀寫,這裡的限速不起作用;direct方式才生效;
cgroup v2版本支援帶cache限速
6 IO性能調試:iotop, iostat
blktrace跟蹤硬碟的各個讀寫點
blktrace –d /dev/sda –o - |blkparse –I –
dd if=main.c of=t.txt oflag=sync
debugfs –R ‘ickeck xxxx’ /dev/sda1
debugfs –R ‘nckeck inode’ /dev/sda1
blkcat /dev/sda1 xxx