当前位置: 首页 > news >正文

【Linux从入门到精通】第43篇:I/O调度算法与磁盘性能优化

目录

一、引言:当多个进程同时读写磁盘

二、Linux I/O栈:请求如何到达磁盘

三、三种经典调度器

3.1 CFQ:人人有份的公平模式

3.2 Deadline:拒绝长队等待

3.3 NOOP:简单到极致的“无序”调度

3.4 三种调度器对比

3.5 查看和修改调度器

3.6 一个具体的场景分析

四、ionice:精细控制I/O优先级

4.1 I/O优先级体系

4.2 基本用法

4.3 实战:不打扰用户的备份脚本

4.4 ionice的选择策略

五、综合实战:磁盘性能优化决策流程

六、本篇小结

动手练习

七、下篇预告


一、引言:当多个进程同时读写磁盘

想象一个场景:你正在用浏览器加载网页(需要读取缓存文件),同时后台的备份脚本在疯狂拷贝大量文件到备份目录。这两个操作的I/O请求会同时到达磁盘。

如果没有调度,它们会按到达顺序排队。但浏览器的操作是交互式的——你在等页面加载,每多等100毫秒都会觉得卡;而备份脚本是批处理任务,晚几秒完成无伤大雅。

I/O调度器就是要在这种资源竞争中找到平衡:让交互式操作快速响应,让批处理任务高效完成,换谁也不至于干等。

二、Linux I/O栈:请求如何到达磁盘

先从整体看清楚一个读/写请求的完整路径:

text

应用程序(write/read系统调用) ↓ VFS(虚拟文件系统层):统一接口,屏蔽不同文件系统的差异 ↓ Page Cache(页缓存层):先写入内存缓存,由内核异步刷盘 ↓ 文件系统(ext4/xfs等):负责将数据组织为文件系统格式 ↓ Block Layer(通用块层):组装成块I/O请求(bio),准备下发给磁盘 ↓ ┌──────────────────────────────────┐ │ I/O调度器(决定请求的排队顺序) │ ← 本文主角 └──────────────────────────────────┘ ↓ 设备驱动(与物理磁盘通信) ↓ 磁盘硬件(HDD/SSD)

队列在这个流程中的位置决定了它的角色:通用块层将上层请求打包好后,不是直接扔给驱动,而是先交给I/O调度器排队。调度器根据算法决定“谁先谁后”,然后将排好序的请求派发给磁盘驱动。

为什么VFS和文件系统不做这个调度?因为它们面对的是文件和目录,而I/O调度器面对的是物理扇区地址——只有到了这个层次,才知道两个请求是否访问相邻位置、是否可以合并、是否在同一个磁盘上。

三、三种经典调度器

3.1 CFQ:人人有份的公平模式

CFQ(Completely Fair Queuing,完全公平排队)曾是Linux默认的I/O调度器,它的设计思想是:为每个进程分配独立的I/O队列,按时间片轮流向每个队列派发请求

text

进程A队列:[A1 A2 A3] ─┐ 进程B队列:[B1 B2] ─┤ → 调度器按时间片轮流分发 → 磁盘 进程C队列:[C1 C2 C3] ─┘

这种设计的意义在于确保没有单个进程可以独占磁盘。即使有一个进程在疯狂读写大文件,其他进程的I/O请求也能得到公平的响应机会,不会被“饿死”。

CFQ还内置了I/O优先级支持——优先级高的进程分得更大的时间片,它的请求在队列中等得更短。后面要讲的ionice就是基于这个机制。

适用场景:HDD,尤其是多用户、多任务的桌面或服务器环境。

3.2 Deadline:拒绝长队等待

CFQ太“公平”了——每个进程一个队列轮流服务——但也因此带来了问题:如果一个请求在队列尾部等待太久,它可能要等很多轮才能被派发。

Deadline调度器用一个简单规则解决了这个问题:每个请求都有“截止时间”。读请求默认500ms超时,写请求默认5000ms超时。当某个请求等待时间接近截止时间,调度器会跳过常规的排序逻辑,优先处理这个请求。

这意味着Deadline调度的核心约束是延迟上限可控——一个请求最多等多久,是可以预期而不会无限排队。

适用场景:数据库服务器(对读延迟敏感)或任何要求I/O响应时间可预测的场景。

3.3 NOOP:简单到极致的“无序”调度

NOOP(No Operation)调度器只维护一个FIFO(先进先出)队列,几乎没有调度逻辑。它做的工作就是请求合并:如果两个相邻请求访问连续的磁盘扇区,就合并成一个请求批量处理。

NOOP不对请求做任何重新排序。原因并不是它“懒”,而是在某些硬件上,硬件自身已经做了排序,内核再做一遍排序反而浪费CPU。这些硬件包括:

  • SSD和NVMe:无机械寻道,访问任何位置速度相同,软件排序无意义

  • 高端存储阵列(SAN/NAS):控制器内部有自己的I/O调度

  • 虚拟化环境:宿主机Hypervisor层已经对I/O做了合并和排序

在这些场景下,NOOP作为内核层最简单的通道,把调度工作完全交给硬件,反而效率最高。

3.4 三种调度器对比

调度器核心策略队列设计适用硬件典型场景
CFQ公平分配I/O带宽每进程独立队列HDD多任务桌面、多用户服务器
Deadline保证延迟上限读/写两队+截止时间HDD数据库、延迟敏感应用
NOOP仅合并相邻请求单一FIFO队列SSD/NVMe所有SSD类场景、虚拟化

3.5 查看和修改调度器

bash

# 查看某块磁盘当前的调度器和可用调度器 cat /sys/block/sda/queue/scheduler # 输出:noop [deadline] cfq # [] 括起来的是当前生效的调度器 # 临时修改调度器 echo noop | sudo tee /sys/block/sda/queue/scheduler # 查看系统中所有磁盘的调度器 for disk in /sys/block/sd*; do echo "$(basename $disk): $(cat $disk/queue/scheduler)" done # 永久修改(添加到GRUB启动参数,以CentOS为例) # vim /etc/default/grub # 在 GRUB_CMDLINE_LINUX 中添加 elevator=noop # sudo grub2-mkconfig -o /boot/grub/grub.cfg

现代Linux的变迁:内核5.0+引入了全新的多队列(multi-queue)块层设计,对应的调度器也升级为mq-deadlinebfq(Budget Fair Queuing,CFQ的理念继承者)和kyber(面向低延迟的简单调度器)。但无论调度器怎么升级,核心设计思想——让不同的硬件发挥各自的优势——从未改变。

3.6 一个具体的场景分析

场景:你有一台HDD服务器,上面既跑着Nginx(需要快速读取静态文件响应Web访问),又有凌晨的rsync备份脚本(连续大量读盘)。用户反馈网站时不时卡一下,iostat显示await很高。

原因:rsync的大批量读请求抢占了CFQ调度器的大量时间片,导致Nginx的I/O请求在队列中排队等待,时延变大。

解决方向

  • 选项A:让rsync从Nginx不在用的时候读取(cron调时间,最简单)

  • 选项B:降低rsync的I/O优先级,让它把调度器时间片让给Nginx(此时CFQ的进程级队列起作用)

  • 选项C:换成Deadline调度器,利用读优先和截止时间机制,确保交互式读请求不被无限排队

选项B正是接下来要讲的ionice

四、ionice:精细控制I/O优先级

4.1 I/O优先级体系

ionice控制I/O调度器分配给进程的优先级,它依赖CFQ调度器才能生效(NOOP和Deadline没有每进程队列的概念)。

三个调度类

调度类数字含义使用场景
realtime1实时优先级,其他进程必须等待它完成通常不推荐用
best-effort2尽力而为(默认),可在0-7范围内调节最常用
idle3空闲类,仅在磁盘完全空闲时才执行I/O后台批处理任务

best-effort类的优先级值(0=最高优先级,7=最低优先级):

text

优先级 0 ───────────── 7(默认 4) 高优先级 低优先级 (抢得快) (让着别人)

4.2 基本用法

bash

# 查看进程的I/O优先级 ionice -p PID # 以最低优先级执行备份脚本(不干扰核心服务) ionice -c 2 -n 7 rsync -av /data/ /backup/ # 以idle类执行日志归档(只有磁盘完全空闲时才干活) ionice -c 3 tar -czf /archive/logs_$(date +%Y%m%d).tar.gz /var/log/ # 调整已有进程的优先级 ionice -c 2 -n 7 -p $(pgrep -f "rsync")

4.3 实战:不打扰用户的备份脚本

bash

#!/bin/bash # backup.sh - 以最低I/O优先级执行备份,不干扰在线服务 LOG="/var/log/backup_$(date +%Y%m%d).log" echo "[$(date)] 备份开始(I/O优先级:最低)" >> "$LOG" # nice降低CPU优先级(第12篇讲过),+19是最低 # ionice降低I/O优先级,-n 7是最低 nice -n 19 ionice -c 2 -n 7 rsync -av --delete /data/ /backup/data/ >> "$LOG" 2>&1 echo "[$(date)] 备份完成" >> "$LOG"

这个脚本同时使用了nice(降低CPU优先级)和ionice(降低I/O优先级),确保备份操作不会影响白天用户的正常访问。

4.4 ionice的选择策略

任务类型调度类优先级
交互式应用(Nginx、MySQL)best-effort0-3
普通脚本best-effort4(默认)
定时备份best-effort6-7
日志归档idle-

五、综合实战:磁盘性能优化决策流程

当遇到磁盘I/O性能问题时,按以下步骤分析:

第一步:确认瓶颈在I/O

bash

vmstat 2 # wa列 > 10% = I/O瓶颈 # b列 > 0 = 进程在等待I/O

第二步:按硬件类型选择调度器

bash

# 查看当前磁盘类型 cat /sys/block/sda/queue/rotational # 0 = SSD, 1 = HDD # HDD → deadline 或 bfq # SSD → noop 或 none # 查看当前调度器 cat /sys/block/sda/queue/scheduler

第三步:降低非关键进程的I/O优先级

bash

# 识别高I/O进程 iotop -o # 按I/O使用率排序 # 对非关键进程降低优先级 ionice -c 2 -n 7 -p PID

第四步:在应用层面优化

bash

# 检查是否有大文件被反复重写 lsof +D /data/ | grep -E "^.*W" # 适当调大Page Cache容量(让更多数据在内存中完成操作) # 使用 tmpfs 存放临时文件 sudo mount -t tmpfs -o size=2G tmpfs /tmp

关于lsof +D+D表示递归搜索目录,输出访问该目录的所有进程和打开的文件描述符。加上grep W过滤出以“写”模式打开的文件。这个命令可以帮助你找到“谁在不停地往磁盘写数据”,但+D递归搜索本身也会消耗I/O,慎在高峰期使用。

六、本篇小结

I/O栈路径:应用 → VFS → 文件系统 → 通用块层 →I/O调度器→ 驱动 → 磁盘

三种调度器

调度器核心思想一句话
CFQ/bfq公平分配“每个人都得轮到”
Deadline/mq-deadline截止时间“读请求不能再等了”
NOOP/none无调度“硬件自己看着办”

ionice核心命令

场景命令
最低优先级备份ionice -c 2 -n 7 rsync ...
空闲才干活ionice -c 3 tar ...
查看已有进程优先级ionice -p PID

动手练习

bash

# 1. 查看所有磁盘的调度器 for disk in /sys/block/sd* /sys/block/nvme*; do [ -d "$disk" ] && echo "$(basename $disk): $(cat $disk/queue/scheduler)" done # 2. 判断你的磁盘是HDD还是SSD cat /sys/block/sda/queue/rotational # 0 = SSD, 1 = HDD # 3. 创建两个后台测试任务——一个高优先级I/O,一个低优先级 dd if=/dev/zero of=/tmp/high_io bs=1M count=500 & ionice -c 2 -n 7 dd if=/dev/zero of=/tmp/low_io bs=1M count=500 & # 用iostat观察磁盘活动和吞吐量 iostat -x 1 5 # 4. 安装iotop查看各进程的I/O使用情况 sudo apt install iotop -y sudo iotop -o # 只显示有I/O活动的进程 # 5. 清理测试文件 rm /tmp/high_io /tmp/low_io

七、下篇预告

I/O调度器解决了磁盘读写请求的排队问题,但数据最终要通过网络传输给用户。当并发连接数飙升时,你可能遇到过“TIME_WAIT状态满屏”的困扰。

下一篇我们将深入Linux网络协议栈与TCP参数调优,学习TCP三次握手/四次挥手的内核实现,理解TIME_WAIT的由来,以及如何通过调整net.ipv4.tcp_tw_reuse等内核参数来优化高并发场景下的网络性能。


延伸思考:SSD上没有机械寻道,为什么很多发行版在SSD上仍然默认使用mq-deadline而非none?因为多队列框架下,mq-deadline在实现请求合并写回优化方面仍然比完全的“无调度”有优势——将相邻扇区的写请求合并成批量操作、通过控制写积压来避免延迟飙升,即使在随机访问同样块的所有位置的SSD上,这些优化依然有意义。

http://www.jsqmd.com/news/737409/

相关文章:

  • 魔兽争霸III终极优化指南:WarcraftHelper完整使用教程
  • 2026年上海口碑好的股权纠纷律师事务所排名 - mypinpai
  • 从人口普查到App A/B测试:一文读懂整群抽样与系统抽样的实战选择
  • 绝区零一条龙:3步实现游戏全自动化的终极指南
  • Docker Engine安装
  • 告别镜像混乱!手把手教你调试MTK平台Camera的Flip与Mirror效果(含Vendor Tag与ADB秘籍)
  • 2026 年湖州装修公司推荐:为什么蓝鹊装饰值得重点了解?——从设计、施工、报价、材料、工艺到售后的深度解析 - GrowthUME
  • 从上帝视角到像素射线:用大白话图解LSS如何让自动驾驶汽车‘脑补’出3D世界
  • 2026年西安憬华木作口碑怎么样? - mypinpai
  • 避坑指南:CentOS 7最小化安装下部署Zabbix 6.4最容易踩的5个雷(附解决方案)
  • LinkSwift技术方案:八大网盘直链解析与高效下载实战指南
  • 【Linux从入门到精通】第44篇:Linux网络协议栈与TCP参数调优
  • 2026 年最佳 7 款网页爬虫工具 API
  • 题解:AcWing 4181 数的划分
  • AI驱动的SaaS店铺监控机器人:Creem自动化运营与实时警报实践
  • 终极指南:如何在Blender中高效创建和管理VRM虚拟角色
  • UnrealPakViewer:终极Pak文件分析工具,如何快速解密虚幻引擎资源黑盒
  • git 加速
  • 做烟囱维修加固用无脚手架工艺的公司有哪些? - mypinpai
  • ComfyUI-Manager:如何在无网络环境中部署AI节点管理神器?
  • 2026年AI营销GEO豆包推广公司怎么选择:5大专业服务商推荐与选择指南 - 深圳昊客网络
  • 绝区零自动化革命:如何用Python+AI实现游戏全流程智能化,每天节省45分钟
  • Docker 27原生日志驱动深度改造:支持GB/T 28181-2022审计格式输出,3小时完成等保日志对接(附开源工具包)
  • 2026年最新推荐一体化泵站源头厂家排行榜:聚焦优质预制/提升/智能泵站品牌 一体化雨水泵站/玻璃钢一体化泵站公司推荐 - 泵站报价15613348888
  • 《缺氧》U50高效开局:如何像速通玩家一样规划你的复制人基地(含四班倒日程与绿区开发技巧)
  • Claude AI代码交互界面:一体化Web开发环境部署与实战
  • 从Netflix推荐到反欺诈:手把手拆解Elasticsearch ANN算法的5个真实应用案例
  • 想玩转eBPF?在Ubuntu 22.04上编译带BTF支持的Linux内核,这个坑你得先跨过去
  • Blender贝塞尔曲线插件:从入门到精通的完整指南
  • 2026年无锡地区好用的抛光加工厂家推荐 - mypinpai