Android性能系列专题理论之十一:block IO问题分析思路

【关注我,后续持续新增专题博文,谢谢!!!】

上一篇我们讲了:

这一篇我们开始讲:

目录

[一、block I/O背景](#一、block I/O背景)

[二、Block I/O 原理详解](#二、Block I/O 原理详解)

[三、Block I/O问题分类与特征分析](#三、Block I/O问题分类与特征分析)

[四、Block I/O 问题分析SOP](#四、Block I/O 问题分析SOP)

[五、Block I/O 的常见原因](#五、Block I/O 的常见原因)


一、block I/O背景

uninterruptible sleep (D状态)与block I/O的关系

当进程因等待I/O操作而进入uninterruptible sleep(标记为D状态)时,表示该进程正在等待磁盘或网络等底层资源响应。在systrace中表现为长时间停留在D状态,通常与以下场景相关:

  • 慢速存储设备(如机械硬盘或高延迟网络存储)
  • I/O队列拥塞或设备过载
  • 文件系统锁竞争或元数据操作

systrace中识别block I/O问题

systrace的CPU频率视图或进程活动区域中:

  • 查找长时间处于D状态的进程(红色标记)
  • 检查对应时间点的mmc(存储设备)或block内核函数调用
  • 观察iowait数值突增的CPU核心

典型模式表现为:

复制代码
<idle>-0     [000] d.h.  1234.567890: sched_blocked_reason: pid=1234 iowait=1 caller=ext4_file_write_iter+0x123

Block I/O 在 systrace 中的表现

在 systrace 中,Block I/O 导致的卡顿通常表现为以下特征:

  • 高延迟的 I/O 操作block_rq_issueblock_rq_complete 之间的时间间隔过长,表明 I/O 请求从发起到完成的耗时较高。
  • 频繁的 I/O 等待wait_on_page_bit_commonio_schedule 等事件频繁出现,表示进程因 I/O 阻塞而进入等待状态。
  • 磁盘队列堆积blk_queue_busyblk_mq_run_hw_queue 显示磁盘请求队列堆积,可能导致后续 I/O 请求延迟。

二、Block I/O 原理详解

以下是 Linux 内核中 Block I/O 操作的核心流程,涉及页面缓存、锁机制与底层文件系统交互:


内存分配与缓存管理

调用 __page_cache_alloc 从内存管理子系统分配一个物理页面(page)。该页面初始状态为空,未包含任何有效数据。

通过 add_to_page_cache_lru 将新分配的页面插入页面缓存(page cache)的 LRU(最近最少使用)链表。此时页面被纳入内核的统一缓存管理体系。


页面锁定与状态标记

设置页面的 PG_locked 标志位,标记该页面正处于 I/O 操作中。此时任何尝试访问该页面的进程将被阻塞。

调用 __lock_page 函数显式锁定页面,确保在数据加载完成前不会被其他进程修改或释放。


文件系统与块层交互

文件系统通过 readpages 方法处理批量页面读取请求。该方法将逻辑文件偏移转换为物理磁盘块地址。

块层(block layer)的 submit_bio 提交构造好的 I/O 请求(bio 结构体)。请求包含目标设备、扇区地址和内存缓冲区(即之前分配的 page)。


I/O 完成处理

磁盘控制器完成数据读取后,通过中断或轮询机制通知内核。数据被 DMA 传输到之前锁定的 page 内存中。

在 I/O 结束处理(endio)中清除 PG_locked 标志位,唤醒等待该页面的进程。此时页面数据已有效,可被正常访问。


关键点说明

  • 页面缓存:作为磁盘数据的缓存层,减少实际 I/O 操作次数
  • 锁机制PG_locked 保证数据一致性,防止竞态条件
  • 异步 I/O:整个流程非阻塞,进程可在页面锁定后挂起或执行其他任务

该流程体现了 Linux 将内存管理、文件系统与块设备驱动分层协作的设计哲学。

三、Block I/O问题分类与特征分析

底层异常导致的I/O性能问题
  • 表现特征:平均单次I/O耗时显著增加,通常超过10ms。
  • 常见原因
    • 存储空间接近或达到容量上限,导致碎片化或写放大。
    • 存储器件(如eMMC/UFS)老化,物理性能下降。
    • 底层调度策略(如CPU/Block层调度)出现异常或配置不当。
  • 典型场景
    • 长时间高负载写入后,器件寿命衰减。
    • 文件系统未及时维护(如TRIM未触发)。
进程行为或系统资源导致的I/O问题
  • 表现特征:单次I/O耗时较短(约几毫秒),但频繁的短暂I/O累积成高延迟。
  • 常见原因
    • 进程密集加载文件(如启动时加载大量动态库)。
    • 低内存状态下缓存被回收,触发重复I/O(如inode/dentry缓存失效)。
    • 小文件随机读写占主导,导致寻址开销叠加。
  • 典型场景
    • 应用程序初始化阶段或资源热加载。
    • 内存压力下频繁触发kswapd回收页缓存。

诊断方法建议

  • 工具使用

    • iostat -x 1观察awaitsvctm差值,判断队列堆积或硬件延迟。
    • ftrace/blktrace追踪具体I/O路径的耗时分布。
    • vmstat 1检查内存回收频率与I/O关联性。
  • 优化方向

    • 底层异常需检查SMART健康状态或更换存储介质。
    • 进程级问题可通过预加载、调整缓存策略或优化文件访问模式缓解。

四、Block I/O 问题分析SOP

确定底层IO性能是否异常的排查方法

平均单次IO耗时大于10ms

IO自身性能存在问题,需重点关注存储模块。检查存储硬件状态(如磁盘健康度、SSD磨损)、文件系统碎片化、RAID配置或存储驱动兼容性。必要时使用工具(如iostatblktrace)深入分析存储延迟分布。

平均单次IO耗时小于10ms

IO自身无异常,按以下方向排查:

器件性能差异验证

使用AndroBench等基准测试工具对比目标设备与参考机的读写速度(顺序/随机)、IOPS。若差异显著,需检查存储芯片规格、控制器性能或固件版本。

内存占用分析

通过freeproc/meminfo确认系统内存使用率。低内存可能导致频繁换页,触发额外IO。排查内存泄漏进程(如topsmem),或优化应用内存管理策略。

调度问题定位

通过systrace捕捉固定唤醒源及runnable线程阻塞时长。若存在长时间就绪态线程,需检查CPU负载均衡、锁竞争或优先级配置。调整调度策略(如CFS参数)或优化线程模型。

进程IO负载检查

使用iotop/proc/pid/io统计各进程读写量。高IO负载进程需进一步分析其操作模式(如大量小文件写入)。考虑限制其IO带宽或优化数据访问逻辑。

文件访问优化

通过stracelsof确认高频访问的文件路径。对必须频繁读写的文件采用内存映射(mmap)或预加载;异步写入合并策略可减少IO次数。避免直接操作小文件,改用数据库或缓存中间层。

2.1:器件性能差异验证

确认存储器件性能差异的方法

使用AndroBench工具测试存储性能,获取关键指标(如顺序读写、随机读写速度),与对比机数据进行比对。

测试步骤

下载并安装AndroBench(适用于Android平台),确保测试期间无其他后台任务干扰。运行基准测试,记录以下数据:

  • 顺序读取/写入速度(MB/s)
  • 随机读取/写入速度(IOPS)
  • SQLite数据库操作性能(若涉及数据库场景)

数据比对

将测试结果与目标对比机的历史数据或行业标准(如同类存储芯片规格)对比。若差异超过10%-15%,需考虑硬件性能差异对Block I/O问题的影响。

其他辅助验证手段

检查硬件规格

通过系统命令或硬件信息工具(如adb shell cat /proc/scsi/scsi或第三方App)确认存储型号(如UFS 2.1/3.1、eMMC 5.1等),不同型号的底层性能存在显著差异。

监控实时I/O负载

使用adb shell dumpsys diskstatsiotop命令观察实时I/O负载情况,结合业务场景分析是否因硬件瓶颈导致延迟。

注意事项

  • 测试时需关闭省电模式,避免CPU降频影响结果。
  • 多次测试取平均值,减少偶然误差。
  • 若条件允许,交叉验证其他工具(如A1 SD Bench)以确保数据一致性。

2.2 :排查内存占用异常的方法

检查系统内存使用情况 使用 free -h 命令查看内存使用情况,重点关注 available 字段,表示当前可用内存。若 available 值极低(如小于总内存的10%),可能存在内存压力。

分析缓存占用比例 通过 free -h 输出的 buff/cache 字段查看缓存占用量。Linux 会利用空闲内存缓存文件数据,若该值较高但 available 充足,属于正常行为。

监控缓存回收情况 使用 vmstat 1 观察 si(swap in)和 so(swap out)列。若频繁出现非零值,说明系统正在回收缓存或触发交换,可能因内存不足。

定位低内存引发的 IO 问题

观察块设备 IO 延迟 使用 iostat -x 1 查看 await%util 指标。高 await(如 >50ms)伴随高 %util(如 >80%)表明存在 IO 瓶颈,可能与缓存失效导致的直接磁盘读写有关。

跟踪进程级 IO 请求 通过 iotop -o 查看实时 IO 写入/读取进程。若某些进程在低内存时突然出现高 IO,可能是因缓存被回收后被迫从磁盘加载数据。

检查内核日志 使用 dmesg | grep -i oomjournalctl -k --grep="oom" 查找 OOM(Out of Memory)事件记录。OOM Killer 触发后可能导致进程异常终止并重新加载数据。

优化建议

调整缓存回收策略 修改 /proc/sys/vm/swappiness(默认值60)为更低值(如10-30),减少交换倾向,优先释放文件缓存而非触发交换。

限制内存密集型进程 使用 cgroups 为特定进程组设置内存限制(memory.limit_in_bytes),避免单一进程耗尽内存导致全局缓存失效。

增加监控与告警 部署 Prometheus + Grafana 监控 node_memory_MemAvailablenode_disk_io_now 等指标,设置阈值告警(如可用内存 <1GB)。

2.3 :IO 完成与调度延迟分析

IO 完成与调度延迟分析

IO 请求完成后通过 softirq 回调 endio 处理函数,若该函数因CPU调度延迟未能及时执行,会导致 IO 流程未真正完成。此时尽管硬件 IO 已结束,系统仍会表现为 block IO。以下是关键现象和排查方法:


关键特征与表现

  1. block IO 被 kworker 唤醒

    • endio 处理因调度延迟挂起时,block IO 的完成信号依赖 kworker 线程触发。若 kworker 线程处于 runnable 状态但长时间未被调度,IO 完成流程会被阻塞。
  2. eMMC 器件的 cmdq 线程延迟

    • 使用软件模拟 cmdq 的 eMMC 设备中,exe_cq 线程负责处理命令队列。若该线程 runnable 时间长,表明调度器未能及时分配 CPU 资源,导致 IO 延迟。

排查方法

检查调度延迟工具

  • 使用 ftraceperf sched 分析调度延迟:

    复制代码
    perf sched latency --sort max

    关注 kworkerexe_cq 线程的 wait timesch delay

监控线程状态

  • 通过 pstop 查看目标线程状态:

    复制代码
    watch -n 1 "ps -eo pid,comm,state,pcpu | grep -E 'kworker|exe_cq'"

    若线程长期处于 R(runnable)状态但 CPU 占用低,可能存在调度问题。

内核调度参数调整

  • 提高目标线程的调度优先级(需 root 权限):

    复制代码
    chrt -f -p 99 <PID>

    将线程设置为实时调度策略(SCHED_FIFO),优先级 99。

中断与 softirq 分析

  • 检查 softirq 处理是否被延迟:

    复制代码
    watch -n 1 "cat /proc/softirqs"

    HI_SOFTIRQTASKLET_SOFTIRQ 计数持续增长,可能存在软中断堆积。


优化建议

  1. 隔离 CPU 核心

    将关键线程(如 exe_cq)绑定到专用 CPU 核心,避免其他任务竞争:

    复制代码
    taskset -pc <CORE_ID> <PID>
  2. 调整内核配置

    启用 CONFIG_PREEMPTCONFIG_PREEMPT_RT,减少调度延迟。针对 eMMC 驱动,禁用 cmdq 的软件模拟(若硬件支持)。

  3. 监控工具链

    结合 sarvmstatiostat 监控系统负载与 IO 状态,确认是否因全局 CPU 过载导致调度延迟。


典型场景示例

  • 案例 1kworker 线程因高负载系统无法及时调度,导致 endio 未执行。
    解决 :通过 cgroups 限制其他进程的 CPU 配额,确保 kworker 获得足够资源。

  • 案例 2exe_cq 线程因实时性不足导致 eMMC 性能下降。
    解决 :切换线程调度策略为 SCHED_FIFO,并绑定到独立 CPU 核心。

2.4 :排查高IO负载的方法

IO带宽与阻塞IO的关系

IO带宽可以类比为马路的通行能力,当大量车辆同时行驶时,容易造成交通拥堵。类似地,当多个进程同时发起大量IO读写操作时,IO带宽可能成为瓶颈,导致阻塞IO(Blocking IO)现象。

高IO负载导致阻塞IO的原因

多个进程同时进行高频率的IO操作时,IO设备的处理能力可能无法满足需求。磁盘、网络等设备的吞吐量有限,当请求超过其处理能力时,IO操作会被排队等待,导致进程阻塞。

检查系统整体IO负载

使用iostat命令查看磁盘IO情况,重点关注%util(磁盘利用率)和await(IO平均等待时间)。若%util持续接近100%或await显著升高,表明存在高IO负载。

示例命令:

复制代码
iostat -x 1 5

定位高IO进程

通过iotoppidstat工具查看各进程的IO读写情况。iotop直接显示实时读写速率,pidstat可统计历史数据。

示例命令:

复制代码
sudo iotop -o
pidstat -d 1

统计IO读写量

使用脚本或工具统计特定时间段内的IO扇区读写量(1 sector=512字节)。例如通过/proc/<pid>/io获取进程级IO数据:

复制代码
cat /proc/<pid>/io | grep read_bytes

分析具体IO操作

通过strace跟踪进程的系统调用,或使用blktrace工具深入分析磁盘IO请求的延迟和类型(读/写)。

示例命令:

复制代码
strace -p <pid> -e trace=read,write
blktrace -d /dev/sda -o - | blkparse -i -

优化建议

  • 对频繁读写的小文件,考虑使用内存缓存(如tmpfs)。
  • 调整内核IO调度策略(如deadlinekyber),适用于高并发场景。
  • 使用ionice限制低优先级进程的IO带宽。

通过上述方法可系统性定位并缓解高IO负载问题。

解决高IO负载的方法

优化IO调度策略

调整操作系统的IO调度算法,例如Linux中可以选择deadlinenoopcfq调度器。不同的调度器适用于不同场景,如deadline适合减少IO延迟。

使用异步IO(AIO)

异步IO允许进程在发起IO请求后继续执行其他任务,而不必等待IO完成。例如Linux的libaio库可以实现非阻塞的IO操作,避免进程被阻塞。

增加缓存层

在应用层或系统层增加缓存,减少直接IO操作。例如使用redis缓存热点数据,或利用操作系统的页缓存(Page Cache)机制。

分散IO负载

将IO操作分散到不同的物理设备或时间段。例如使用RAID技术将数据分布到多块磁盘,或错峰执行批量IO任务。

监控与诊断工具

iostat命令可以监控系统的IO负载情况,观察设备的吞吐量和利用率。iotop工具能查看各个进程的IO使用量,帮助定位高IO进程。

复制代码
iostat -x 1  # 查看设备IO详细统计
iotop -o     # 显示活跃的IO进程

2.5 :排查进程读写文件的方法

优化blockio最彻底的办法就是不发起IO操作。因此当发生blockio时,我们需要知道进程在读写什么文件,文件读写是不是必须的?

使用strace跟踪系统调用 strace -p <PID> -e trace=file可以实时跟踪指定进程的文件操作,其中-e trace=file过滤出文件相关的系统调用(如open/read/write)。输出会显示文件名、操作类型及返回状态。

通过lsof查看已打开文件 lsof -p <PID>列出进程当前打开的所有文件描述符,包括文件路径、读写模式(如W表示写,R表示读)。结合-n禁用域名解析可加速输出。

检查/proc文件系统 ls -l /proc/<PID>/fd/显示进程的文件描述符符号链接,指向实际文件路径。cat /proc/<PID>/io提供进程的读写统计(如rchar/wchar)。

评估IO操作的必要性

分析业务逻辑 确认读写操作是否属于关键路径:日志文件写入可能异步延迟,而数据库事务文件同步则必须立即完成。结合应用文档或代码审查判断。

检查文件访问模式 高频小文件随机读写(如临时文件)可通过合并IO或内存缓存优化;大文件顺序读写(如媒体流)需调整预读策略。

使用性能工具定位瓶颈 iotop -oPa显示实时磁盘IO占用排名,blktrace分析块层延迟。若某文件的IO延迟显著高于其他,需针对性优化。

优化策略示例

减少非必要IO 日志文件改为缓冲写入或异步刷盘,临时文件使用内存文件系统(如tmpfs)。

合并与批量操作 将多次小写合并为单次大写(如writev系统调用),数据库启用批量提交而非单行提交。

调整IO调度与缓存 CFQ调度器适合机械盘,NVMe盘改用none调度器。增大vm.dirty_ratio让更多脏页缓存在内存。

代码层优化 内存映射文件(mmap)替代read/write,避免重复解析同一配置文件(如启动时加载到内存)。

优化 Block I/O 的深度策略

减少或消除不必要的 I/O 操作

分析进程的 I/O 行为,识别正在读写的文件是否属于关键路径。若非必需,可移除相关操作或调整其执行时机(如延迟加载)。例如,启动阶段的非核心日志文件读取可能被推迟。

高频访问资源的常驻内存

对频繁读写的系统库或数据文件,根据其访问频率和大小评估内存常驻(pinning)的可行性。通过 mlock 或类似机制将文件锁定在物理内存,避免重复磁盘访问。需注意内存开销与收益的平衡。

异步与非阻塞 I/O 模型

替换同步阻塞调用为异步接口(如 io_uringepoll)。任务队列化后由后台线程处理,主线程仅检查完成状态。适用于必须保留的 I/O 操作,减少直接阻塞时间。

缓存与预加载机制

实现应用层缓存(如 LRU 缓存热点数据)或利用操作系统预读(readahead)。对可预测的访问模式,提前加载数据至内存,避免运行时阻塞。

文件系统与硬件层优化

选择低延迟文件系统(如 XFS 对高频小文件更优),或使用 SSD 替代 HDD。调整内核 I/O 调度器(如 kyber 针对 NVMe)可进一步降低延迟。

五、Block I/O 的常见原因

Block I/O 的常见原因(非存储模块独占)

1. 文件系统或 VFS 层问题

  • 文件系统锁竞争(如 inode_lock 争用)会导致多个进程阻塞在元数据操作上。
  • 文件系统日志(如 ext4 的 journal)写入频繁,增加 I/O 延迟。

2. 内存压力与页面回收

  • 内存不足时触发直接内存回收(direct reclaim),可能阻塞进程执行同步 I/O。
  • Swap 频繁读写(kswapdswapin 高负载)会导致额外磁盘 I/O。

3. 进程调度与 I/O 优先级

  • 低优先级进程(如后台服务)的 I/O 请求可能被高优先级进程抢占,导致延迟上升。
  • CFQ 或 BFQ 等调度算法的配置不合理可能导致 I/O 带宽分配不均。

4. 应用层 I/O 模式问题

  • 小文件随机读写(如数据库未优化)会加剧磁盘寻道开销。
  • 同步 I/O(O_SYNC)或频繁 fsync() 强制刷盘,增加阻塞概率。

5. 硬件与驱动层因素

  • 磁盘本身的吞吐量或 IOPS 不足(如低端 eMMC)。
  • 驱动或固件缺陷导致 I/O 处理异常(如 NVMe 驱动超时)。

排查与优化建议

  • systrace 标记 :结合 trace_io_* 事件追踪具体进程的 I/O 行为。
  • 内存调优 :调整 vm.swappiness 或禁用 Swap 以减少回收压力。
  • I/O 调度器 :针对场景选择 deadline(低延迟)或 bfq(公平性)。
  • 文件系统优化 :如 ext4 关闭 barrier(需权衡安全性)或调整日志模式。

通过综合分析 systrace 中的阻塞点和上下文,可以更精准定位 Block I/O 的根本原因,而非仅归因于存储硬件。

【关注我,后续持续新增专题博文,谢谢!!!】

下一篇讲解:

相关推荐
千里马学框架1 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台1 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone1 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc1 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
深圳老胡1 天前
STM32F407 控制 L6470 步进电机驱动 —— 控制过程简介
笔记·stm32·单片机·嵌入式硬件·代码规范
女神下凡1 天前
嵌入式设计的各种存储芯片的硬件/软件设计规范,非常全面。
arm开发·单片机·嵌入式硬件
ttyyttemo1 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077001 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼1 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
liangshanbo12151 天前
React 性能优化实战
性能优化·react