linux性能优化-IO调度优化

这篇具有很好参考价值的文章主要介绍了linux性能优化-IO调度优化。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Linux I/O调度器(Linux I/O Scheduler)Linux内核中的一个组成部分,用户可以通过调整这个调度器来优化系统性能,介于通用块层和块设备驱动程序之间。

I/O 调度算法

  • noop(No Operation) :通常用于内存存储的设备。
  • cfq(Completely Fair Scheduler ) :完全公平调度器,进程平均使用IO带宽。
  • deadline :针对延迟的调度器,每一个 I/O,都有一个最晚执行时间。
  • Anticipatory : 启发式调度,类似 Deadline 算法,但是引入预测机制提高性能。

设置io调度参数

1. 查看CentOS IO支持的调度算法

$ cat /sys/block/sda/queue/scheduler
noop [deadline] cfq

CentOS 7.x默认支持的是deadline算法,CentOS 6.x下默认支持的cfq算法,而一般我们会在SSD固态盘硬盘环境中使用noop算法

2. 查看系统block size

第一种
$ tune2fs -l /dev/sda4|grep "Block size"
Block size:               4096

第二种
$ stat /dev/sda3 /|grep "IO Block"
Size: 0         	Blocks: 0          IO Block: 4096   block special file
Size: 4096      	Blocks: 8          IO Block: 4096   directory

第三种
$ dumpe2fs /dev/sda1 |grep "Block size"
Block size:               4096

3. 设置磁盘IO调度算法

$ echo cfq> /sys/block/vdb/queue/scheduler

4. 设置磁盘io相关参数

# 磁盘队列长度,默认128,可提高到512个,控制磁盘允许分配的读写请求数量
# 此增大参数会增大系统内存占用,但能更加多的合并读写操作,降低io磁盘读写速率
$ cat /sys/block/vdb/queue/nr_requests  
128

# 读优化
# 应用场景为顺序读,采用预读技术可提高用户体验。默认128KB,增大此参数对读大文件非常有用,可以有效的减少读 seek 的次数
$ cat /sys/block/sda/queue/read_ahead_kb
4096 
注:"echo 4096 > /sys/block/sda/queue/read_ahead_kb"与"blockdedv -setra 8192 /dev/sda"等价,blockdedv命令定义可以预读多少个扇区,blockdev --getra /dev/sda 是/sys/block/sda/queue/read_ahead_kb的倍数关系,设置其中一个,另外一个自动就会发生变化.
"blockdev --getra /dev/sda":查看/dev/sda预读扇区数

# 控制发送到磁盘的最大I/O请求数,最小值等于系统block size,做大值小于等于max_hw_sectors_kb。
# 如果发送请求的大小(即max_sectors_kb大于block size),会导致磁盘性能下降,调整之前,需要测试磁盘block size大小
# max_hw_sectors_kb:单个数据传输中硬件(如磁盘)最大支持多少KB的数据;
# max_sectors_kb: 一次请求中block 层最大支持多少KB数据,<= max_hw_sectors_kb。
$ cat /sys/block/sda/queue/max_sectors_kb
256
$ cat /sys/block/sda/queue/max_hw_sectors_kb
256 


# 设置磁盘寻道逻辑,ssd磁盘设置为0,防止调度器使用寻道逻辑。
# 机械盘设置为1
$ cat /sys/block/sda/queue/rotational
1

# 磁盘调试配置,默认禁用,设置为0,关闭调试
$ cat /sys/block/sda/queue/nomerges
0

5. 脏数据回刷参数与调优(内存相关参数)

# 缓存释放策略,不涉及dirty page(脏页)内容,默认为0(即不释放缓存)
# dirty page:dirty是物理内存中页标志,如果该页被改写了,就称之为dirty page
# drop_caches=1:释放pagecache(页缓存)
# drop_caches=2:释放inode(文件句柄)和dentry(目录项)
# drop_caches=3:释放pagecache(页缓存)、inode(文件句柄)和dentry(目录项)
$ cat /proc/sys/vm/drop_caches
0

# 控制文件系统的文件系统写缓冲区的大小,单位为百分比,表示占用系统内存的百分比;
# 当写缓冲使用到系统内存多少的时候,开始向磁盘写出数据,增大会使用更多系统内存用于磁盘写缓冲,也可以极大提高系统的写性能;
# 默认值20
$ cat /proc/sys/vm/dirty_ratio
40
注:在持续、恒定的写入场合时,应该降低其数值。

# 控制文件系统的pdflush进程在何时刷新磁盘,单位为百分比,表示当脏页占比因超过vm.dirty_ratio而触发强制写回后所能允许的脏页大小占比。
# 默认值10
# vm.dirty_ratio:让进程自己进行一个强制写回操作
# vm.dirty_background_ratio:调用per-BDI flush在后台写入
$ cat /proc/sys/vm/dirty_background_ratio
10
注:在持续、恒定的写入场合时,应该降低其数值。

#控制内核的脏数据刷新进程pdflush的运行间隔,单位是1/100 秒,默认值500,即5秒。
$ cat /proc/sys/vm/dirty_writeback_centisecs
500
注:持续写入动作,建议降低此参数,可以尖峰的写操作削平成多次写操作;若系统短期地尖峰式的写操作,并且写入数据不大(几十M/次)且内存有比较多富裕,建议增大此数值


# 控制脏页回写时间间隔,单位是1/100 秒,默认值30000,即30秒
# 声明Linux内核写缓冲区里面的数据多“旧”了之后,pdflush进程就开始考虑写到磁盘中去;
# 此参数不能设置太小,否则会导致频繁写磁盘,建议设置为 1500。
$ cat /proc/sys/vm/dirty_expire_centisecs
3000

场景案例

注:配置均为参考值,可根据业务场景合理配置

场景一:尽可能不丢数据

针对数据非常重要的场景,在满足性能要求的情况下,要做到尽可能不丢失数据。

dirty_background_ratio = 5
dirty_ratio = 10
dirty_writeback_centisecs = 50
dirty_expire_centisecs = 100
  • 当脏数据达到可用内存的5%时唤醒回刷进程
  • 当脏数据达到可用内存的10%时,应用每一笔数据都必须同步等待
  • 每隔500ms唤醒一次回刷进程
  • 内存中脏数据存在时间超过1s则在下一次唤醒时回刷

特征:通过减少Cache,更加频繁唤醒回刷进程的方式,尽可能让数据回刷

整体资源消耗:CPU消耗升高,内存使用降低,IO调度频率增高,IO占比降低

场景二:追求更高性能

不需要考虑数据安全问题,要做到尽可能高的IO性能

dirty_background_ratio = 50
dirty_ratio = 80
dirty_writeback_centisecs = 2000
dirty_expire_centisecs = 12000
  • 当脏数据达到可用内存的50%时唤醒回刷进程
  • 当脏数据达到可用内存的80%时,应用每一笔数据都必须同步等待
  • 每隔20s唤醒一次回刷进程
  • 内存中脏数据存在时间超过120s则在下一次唤醒时回刷

特征:增大Cache延迟回刷唤醒时间来尽可能缓存更多数据,进而实现提高性能

整体资源消耗:CPU、磁盘IO消耗低,内存使用增高

场景三:突然的IO峰值拖慢整体性能

什么是IO峰值?突然间大量的数据写入,导致瞬间IO压力飙升,导致瞬间IO性能狂跌

dirty_background_ratio = 5
dirty_ratio = 80
dirty_writeback_centisecs = 500
dirty_expire_centisecs = 3000
  • 当脏数据达到可用内存的5%时唤醒回刷进程
  • 当脏数据达到可用内存的80%时,应用每一笔数据都必须同步等待
  • 每隔5s唤醒一次回刷进程
  • 内存中脏数据存在时间超过30s则在下一次唤醒时回刷

特征:增大Cache总容量,更加频繁唤醒回刷进程的方式,解决IO峰值的问题,此时能保证脏数据比例保持在一个比较低的水平,当突然出现峰值,也有足够的Cache来缓存数据

整体资源消耗:CPU、内存使用增高,IO调度频率增高

内核源码

kernel/sysctl.c文件

static struct ctl_table vm_table[] = {
	...
	{
		.procname	= "dirty_background_ratio",
		.data		= &dirty_background_ratio,
		.maxlen		= sizeof(dirty_background_ratio),
		.mode		= 0644,
		.proc_handler	= dirty_background_ratio_handler,
		.extra1		= &zero,
		.extra2		= &one_hundred,
	},
	{
		.procname	= "dirty_ratio",
		.data		= &vm_dirty_ratio,
		.maxlen		= sizeof(vm_dirty_ratio),
		.mode		= 0644,
		.proc_handler	= dirty_ratio_handler,
		.extra1		= &zero,
		.extra2		= &one_hundred,
	},
	{
		.procname	= "dirty_writeback_centisecs",
		.data		= &dirty_writeback_interval,
		.maxlen		= sizeof(dirty_writeback_interval),
		.mode		= 0644,
		.proc_handler	= dirty_writeback_centisecs_handler,
	},
}

修改/proc/sys/vm配置项的信息,实际上修改了对应的某个全局变量的值,如dirty_background_ratio对应的变量为&dirty_background_ratio。

每个全局变量都有默认值,追溯这些全局变量的定义

<mm/page-writeback.c>

int dirty_background_ratio = 10;
unsigned long dirty_background_bytes;
int vm_dirty_ratio = 20;
unsigned long vm_dirty_bytes;
unsigned int dirty_writeback_interval = 5 * 100; /* centiseconds */
unsigned int dirty_expire_interval = 30 * 100; /* centiseconds */

默认值:

配置项名

对应源码变量名

默认值

dirty_background_bytes

dirty_background_bytes

0

dirty_background_ratio

dirty_background_ratio

10

dirty_bytes

vm_dirty_bytes

0

dirty_ratio

vm_dirty_ratio

30

dirty_writeback_centisecs

dirty_writeback_interval

500

dirty_expire_centisecs

dirty_expire_interval

3000

回刷进程

通过ps aux,我们总能看到writeback的内核进程。

$ ps aux | grep "writeback" 
root 21 0.0 0.0 0 0 ? S< Mar19 0:00 [writeback]

实际上是一个工作队列对应的进程,在default_bdi_init()中创建。

/* bdi_wq serves all asynchronous writeback tasks */
 struct workqueue_struct *bdi_wq;
 
static int __init default_bdi_init(void)
{
	...
	bdi_wq = alloc_workqueue("writeback", WQ_MEM_RECLAIM | WQ_FREEZABLE |
			WQ_UNBOUND | WQ_SYSFS, 0);
	...
}

回刷进程的核心是函数wb_workfn(),通过函数wb_init()绑定。

static int wb_init(struct bdi_writeback *wb, struct backing_dev_info *bdi
		int blkcg_id, gfp_t gfp)
{
	...
	INIT_DELAYED_WORK(&wb->dwork, wb_workfn);
	...
}

唤醒回刷进程的操作如下:

static void wb_wakeup(struct bdi_writeback *wb)
{
	spin_lock_bh(&wb->work_lock);
	if (test_bit(WB_registered, &wb->state))
		mod_delayed_work(bdi_wq, &wb->dwork, 0);
	spin_unlock_bh(&wb->work_lock);
}

表示唤醒的回刷任务在工作队列writeback中执行,这样,就把工作队列和回刷工作绑定了

在wb_workfn()的最后,有如下代码:

void wb_workfn(struct work_struct *work)
{
	...
	/* 如果还有需要回收的内存,再次唤醒 */
	if (!list_empty(&wb->work_list))
		wb_wakeup(wb);
	/* 如果还有脏数据,延迟唤醒 */
	else if (wb_has_dirty_io(wb) && dirty_writeback_interval)
		wb_wakeup_delayed(wb);
}

static void wb_wakeup(struct bdi_writeback *wb)
{
	spin_lock_bh(&wb->work_lock);
	if (test_bit(WB_registered, &wb->state))
		mod_delayed_work(bdi_wq, &wb->dwork, 0);
	spin_unlock_bh(&wb->work_lock);
}

void wb_wakeup_delayed(struct bdi_writeback *wb)
{
	unsigned long timeout;

	/* 在这里使用dirty_writeback_interval,设置下次唤醒时间 */
	timeout = msecs_to_jiffies(dirty_writeback_interval * 10);
	spin_lock_bh(&wb->work_lock);
	if (test_bit(WB_registered, &wb->state))
		queue_delayed_work(bdi_wq, &wb->dwork, timeout);
	spin_unlock_bh(&wb->work_lock);
}

根据kernel/sysctl.c的内容,我们知道dirty_writeback_centisecs配置项对应的全局变量是dirty_writeback_interval。

可以看出dirty_writeback_interval在wb_wakeup_delayed()中起作用,在wb_workfn()的最后根据dirty_writeback_interval设置下一次唤醒时间。

我们还发现通过msecs_to_jiffies(XXX * 10)来换算单位,表示dirty_writeback_interval乘以10之后的计量单位才是毫秒msecs。怪不得说dirty_writeback_centisecs的单位是1/100秒。

脏数据量

脏数据量通过dirty_background_XXX和dirty_XXX表示,根据kernel/sysctl.c的内容,我们知道dirty_background_XXX配置项对应的全局变量是dirty_background_XXX,dirty_XXX对于的全局变量是vm_dirty_XXX。

我们把目光聚焦到函数domain_dirty_limits(),通过这个函数换算脏数据阈值。

static void domain_dirty_limits(struct dirty_throttle_control *dtc)
{
	...
	unsigned long bytes = vm_dirty_bytes;
	unsigned long bg_bytes = dirty_background_bytes;
	/* convert ratios to per-PAGE_SIZE for higher precision */
	unsigned long ratio = (vm_dirty_ratio * PAGE_SIZE) / 100;
	unsigned long bg_ratio = (dirty_background_ratio * PAGE_SIZE) / 100;
	...
	if (bytes)
		thresh = DIV_ROUND_UP(bytes, PAGE_SIZE);
	else
		thresh = (ratio * available_memory) / PAGE_SIZE;

	if (bg_bytes)
		bg_thresh = DIV_ROUND_UP(bg_bytes, PAGE_SIZE);
	else
		bg_thresh = (bg_ratio * available_memory) / PAGE_SIZE;

	if (bg_thresh >= thresh)
		bg_thresh = thresh / 2;

	dtc->thresh = thresh;
	dtc->bg_thresh = bg_thresh;
  • dirty_background_bytes/dirty_bytes的优先级高于dirty_background_ratio/dirty_ratio
  • dirty_background_bytes/ratio和dirty_bytes/ratio最终会统一换算成页做计量单位
  • dirty_background_bytes/dirty_bytes做进一除法,表示如果值为4097Bytes,换算后是2页
  • dirty_background_ratio/dirty_ratio相乘的基数是available_memory,表示可用内存
  • 如果dirty_background_XXX大于dirty_XXX,则取dirty_XXX的一半

内存计算方法:

static unsigned long global_dirtyable_memory(void)
{
	unsigned long x;
	
	x = global_zone_page_state(NR_FREE_PAGES);
	/*
	 * Pages reserved for the kernel should not be considered
	 * dirtyable, to prevent a situation where reclaim has to
	 * clean pages in order to balance the zones.
	 */
	 
	 x += global_node_page_state(NR_INACTIVE_FILE);
	 x += global_node_page_state(NR_ACTIVE_FILE); 
	 
	 if (!vm_highmem_is_dirtyable)
	 	x -= highmem_dirtyable_memory(x);
	 
	 return x + 1; /* Ensure that we never return 0 */
}

因此,

# cat /proc/meminfo
MemTotal:        3880184 kB
MemFree:          562764 kB
MemAvailable:    3263916 kB
Buffers:          372732 kB
Cached:          2330740 kB
SwapCached:            0 kB
Active:          2064100 kB
Inactive:         849240 kB
Active(anon):     216636 kB
Inactive(anon):      240 kB
Active(file):    1847464 kB
Inactive(file):   849000 kB
Unevictable:        7936 kB
Mlocked:            7936 kB
SwapTotal:             0 kB
SwapFree:              0 kB
Dirty:                 0 kB
Writeback:            12 kB
AnonPages:        217868 kB
Mapped:           140412 kB
Shmem:               584 kB
Slab:             319784 kB
SReclaimable:     293908 kB
SUnreclaim:        25876 kB
KernelStack:        3920 kB
PageTables:         6392 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:     1940092 kB
Committed_AS:    2266836 kB
VmallocTotal:   34359738367 kB
VmallocUsed:       13392 kB
VmallocChunk:   34359718524 kB
Percpu:              352 kB
HardwareCorrupted:     0 kB
AnonHugePages:     40960 kB
CmaTotal:              0 kB
CmaFree:               0 kB
HugePages_Total:       0
HugePages_Free:        0
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB
DirectMap4k:      100208 kB
DirectMap2M:     4093952 kB
DirectMap1G:     2097152 kB

可用内存 = 空闲页 - 内核预留页 + 活动文件页 + 非活动文件页 ( - 高端内存)
即:
$available = $memfree - $water_low_total;

$pagecache = $Active_file + $Inactive_file;
$pagecache -=min($pagecache/2, $water_low_total);
$available += $pagecache;

$available += $SReclaimable - min($SReclaimable/2, $water_low_total);

简化为:
可用内存 = 空闲页 +  可回收页
可回收页 = Active_file + Inactive_file + SReclaimable

脏数据达到阈值后是怎么触发回刷的?文章来源地址https://www.toymoban.com/news/detail-499602.html

static void balance_dirty_pages(struct bdi_writeback *wb,
				unsigned long pages_dirtied)
{
	unsigned long nr_reclaimable;   /* = file_dirty + unstable_nfs */
	...
	/*
	 * Unstable writes are a feature of certain networked
	 * filesystems (i.e. NFS) in which data may have been
	 * written to the server's write cache, but has not yet
	 * been flushed to permanent storage.
	 */
	nr_reclaimable = global_node_page_state(NR_FILE_DIRTY) +
					global_node_page_state(NR_UNSTABLE_NFS);
	...
	if (nr_reclaimable > gdtc->bg_thresh)
		wb_start_background_writeback(wb);
}

void wb_start_background_writeback(struct bdi_writeback *wb)
{
	wb_wakeup(wb);
}

总结

  • 可回收内存 = 文件脏页 + 文件系统不稳定页(NFS)
  • 可回收内存达到dirty_background_XXX计算的阈值,只是唤醒脏数据回刷工作后直接返回,并不会等待回收完成,最终回收工作还是看writeback进程

到了这里,关于linux性能优化-IO调度优化的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 《深入Linux内核架构》第2章 进程管理和调度 (2)

    目录 2.4 进程管理相关的系统调用 2.4.1 进程复制 2.4.2 内核线程 2.4.3 启动新程序 2.4.4 退出进程 本专栏文章将有70篇左右,欢迎+关注,订阅后续文章。 1. _do_fork函数         fork vfork clone都最终调用_do_fork                 clone:通过CLONE_XX标志精确控制父子进程共享哪

    2024年04月11日
    浏览(38)
  • 进程切换和是Linux2.6内核中进程调度的算法

    正文开始前给大家推荐个网站,前些天发现了一个巨牛的 人工智能 学习网站, 通俗易懂,风趣幽默 ,忍不住分享一下给大家。点击跳转到网站。 进程并发就需要做到进程切换,一个CPU一套寄存器但是需要运行的进程有很多,CPU内是内置的有时间片的,当时间片到之后,上

    2024年01月16日
    浏览(44)
  • Linux内核分析(五)--IO机制原理与系统总线

    一、引言 二、I/O设备 ------2.1、块设备 ------2.2、字符设备 ------2.3、设备控制器 ------------2.3.1、I/O寻址 ------------2.3.2、内存映射 I/O 三、系统总线 ------3.1、数据总线 ------3.2、地址总线 ------3.3、控制总线 ------3.4、单总线结构 ------3.5、多总线结构 ------3.6、添加cache的三总线结构

    2024年02月05日
    浏览(50)
  • 文件IO_文件读写(附Linux-5.15.10内核源码分析)

    目录 1.什么是文件偏移量? 1.1 文件偏移量介绍 1.2 文件偏移量重点 1.3 文件偏移量工作原理 2.文件偏移量设置 2.1 lseek函数 2.2 lseek内核源码分析 3.写文件 3.1 write函数 3.2 write内核源码分析 4.读文件 4.1 read函数 4.2 read内核源码分析 5.文件读写,文件偏移量设置示例代码 在介绍文

    2024年02月16日
    浏览(53)
  • 【linux 多线程并发】多任务调度器,调度策略时间片轮转,先进先出,多种实时任务的策略,内核级最高优先级调度策略

    ​ 专栏内容 : 参天引擎内核架构 本专栏一起来聊聊参天引擎内核架构,以及如何实现多机的数据库节点的多读多写,与传统主备,MPP的区别,技术难点的分析,数据元数据同步,多主节点的情况下对故障容灾的支持。 手写数据库toadb 本专栏主要介绍如何从零开发,开发的

    2024年02月03日
    浏览(53)
  • 频繁设置CGroup触发linux内核bug导致CGroup running task不调度

    1. 说明 1 本篇是实际工作中linux上碰到的一个问题,一个使用了CGroup的进程处于R状态但不执行,也不退出,还不能kill,经过深入挖掘才发现是Cgroup的内核bug 2发现该bug后,去年给RedHat提交过漏洞,但可惜并未通过,不知道为什么,这里就发我博客公开了 3 前面的2个帖子《极简

    2023年04月15日
    浏览(58)
  • Linux性能学习(3.2):IO_磁盘IO

    参考资料: 1. Linux I/O模型 2. 判断磁盘I/O是否饱和与%util指标的意义 3. 磁盘利用率和饱和度 4. 辩证看待 I/Ostat 在上一篇中,大致了解了文件系统的一些知识,了解了不同的文件系统以及VFS的概念,其实在存储介质上也是有这个情况,在嵌入式开发中,会根据不同的项目使用不

    2024年02月08日
    浏览(52)
  • Linux内核优化

    参考 减少缓存 适合再内存不充足的条件下去使用,比如只有32G内存无法进行升级硬件了。 文件缓存 是一项重要的性能改进,在大多数情况下,读缓存在绝大多数情况下是有益无害的(程序可以直接从 RAM 中读取数据)。写缓存比较复杂, Linux 内核将磁盘写入缓存,过段时间

    2024年02月08日
    浏览(66)
  • Linux系统运行时参数命令(性能监控、测试)(3)网络IO性能监控

    通常用带宽、吞吐量、延时、PPS(Packet Per Second)等指标衡量网络性能 带宽 ,表示链路的最大传输速率,单位通常为b/s(比特/秒) 吞吐量 ,表示单位时间内成功传输的数据量,单位通常为b/s(比特/秒)或者B/s(字节/秒)。吞吐量受带宽限制,而吞吐量/带宽,也就是 网络

    2024年02月13日
    浏览(41)
  • ​Linux开源存储漫谈(2)IO性能测试利器fio

    fio(Flexible I/O Tester)正是非常常用的文件系统和磁盘 I/O 性能基准测试工具。提供了大量的可定制化选项,可以用来测试,裸盘、一个单独的分区或者文件系统在各种场景下的 I/O 性能,包括了不同块大小、不同 I/O 引擎以及是否使用缓存等场景。   ubuntu安装fio非常简单 fio选

    2024年02月04日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包