做人呢,最紧要就系开心啦

Linux内核之IO4:块I/O流程与I/O调度器

737次阅读
没有评论

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

正文完
 
admin
版权声明:本站原创文章,由 admin 2022-01-04发表,共计2817字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)