天天看點

Linux核心之IO4:塊I/O流程與I/O排程器

1一個塊IO的一生

從page cache到bio到request

當APP打開一個檔案,核心為檔案維護一個pagecache(磁盤的一個副本);

讀寫檔案時如果page cache命中,隻會讀寫記憶體不操作磁盤;沒有命中,才操作磁盤。

在核心用file結構體表示,可見其包含一個inode結構體,一個位址空間;

Linux核心之IO4:塊I/O流程與I/O排程器

相關的幾個結構體在核心對應關系如下:

Linux核心之IO4:塊I/O流程與I/O排程器

可見,當多個程序同時打開同一個檔案時,不同的file結構體對應同一個inode和同一個位址空間,位址空間是由一顆radix

tree維護(即pagecache),讀寫檔案時,檢視對應記憶體頁在page

cache中是否命中,若命中直接從記憶體空間讀寫;若不命中,申請一個記憶體頁,從磁盤讀入資料,挂到page

cache的radix tree中。

另外,page cache與磁盤的同步由位址空間操作函數readpage/writepage完成

Linux核心之IO4:塊I/O流程與I/O排程器

對磁盤通路,有兩種方法:

a.裸磁盤直接通路;

b.通過檔案系統通路;

它們在核心page cached對應關系如下:

Linux核心之IO4:塊I/O流程與I/O排程器

一個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,但是會立即寫入同步磁盤;

Linux核心之IO4:塊I/O流程與I/O排程器

App通過page cache通路檔案和直接操作裸磁盤的模型,與CPU通過cache通路記憶體和DMA直接通路記憶體的模型非常類似;

Linux核心之IO4:塊I/O流程與I/O排程器

這裡page cache是記憶體,file是磁盤分區資料塊;

當有一個程序啟用O_DIRECT模式,其他程序最好也用O_DIRECT;

3 BIO 流程blktrace

對于一個pagecache位址空間,指向的是page頁,檔案系統ext4讀取inode,将page轉化為磁盤資料塊,變成BIO請求;

Linux核心之IO4:塊I/O流程與I/O排程器

BIO最終要轉化成request,然後request被塊裝置驅動程式調用完成;

Linux核心之IO4:塊I/O流程與I/O排程器

Bio經過三個隊列變成request,三進三出

Linux核心之IO4:塊I/O流程與I/O排程器

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 –
           
Linux核心之IO4:塊I/O流程與I/O排程器
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
           

繼續閱讀