You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何提升sysfs及虚拟文件系统的文件读取速度?

问题描述

使用sysfs采集CPU信息时,单个pread()调用开销可控,但循环读取多个CPU的sysfs文件(如每个CPU的cpuinfo_cur_freq)时,系统调用的开销会快速累积。示例代码如下:

int cpu0_freq_open() {
  int fd = open("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_cur_freq", O_RDONLY | O_NONBLOCK);
}

int cpu0_freq_read(int fd) {
  char buffer[64];
  if ( pread(fd, &buffer, sizeof(buffer), 0) == -1 ) {
    printf("Reading failed");
    return 1;
  }
  int freq = atoi(buffer);
  // function call to store the event value at this timestamp
}

int main() {
  int fd_cpu0 = cpu0_freq_open(); // we open for all cpu#s

  // do some action here
  // make a lot of cpu0_freq_read(fd_cpu0) calls for cpu0 and all other cpus

  cpu0_freq_close();
}

原本考虑的内存映射方案因sysfs虚拟文件系统特性无法适用,需要找到提升sysfs文件读取速度的方法,以及针对虚拟文件系统的整体优化方案。

优化方案

一、针对sysfs CPU信息读取的专项优化

1. 批量读取+结果缓存

避免单次读取单个CPU的单个文件,改为周期性批量读取所有目标CPU的相关文件,将结果缓存到内存中。后续业务逻辑直接从缓存获取数据,大幅减少系统调用次数:

  • 预先打开所有CPU的cpuinfo_cur_freq文件描述符
  • 每隔固定时间(如10ms)批量调用pread()读取所有CPU的频率数据并更新缓存
  • 业务代码直接读取缓存值,无需发起新的系统调用

2. 长期复用文件描述符

确保所有需要读取的sysfs文件描述符长期保持打开状态(示例代码已实现此点),避免每次读取时执行open()/close()系统调用——这两个操作的开销远高于pread(),高频场景下会显著拖慢性能。

3. 用汇总接口替代单个文件读取

部分场景下可借助proc或sysfs的汇总接口减少读取次数:

  • /proc/stat可获取CPU整体运行状态,/proc/cpuinfo能读取静态CPU属性(注意:其中的cpu MHz为初始频率,动态频率仍需依赖sysfs的cpufreq目录)
  • 若系统支持cpufreq的policy级目录(如/sys/devices/system/cpu/cpufreq/policy0),可通过该目录下的文件批量获取同policy组内CPU的频率信息

4. 异步IO批量处理请求

使用io_uring机制,将多个sysfs文件的读取请求一次性提交到内核批量处理。io_uring通过减少用户态与内核态的切换次数,能显著降低高频系统调用的总开销,适合大量小文件的读取场景。

二、虚拟文件系统(VFS)整体优化方案

1. 减少路径解析开销

用openat()替代open(),预先打开父目录的文件描述符,后续通过相对路径打开子文件:

  • 先打开父目录fd:int cpu_dir_fd = open("/sys/devices/system/cpu/", O_DIRECTORY | O_RDONLY);
  • 再通过openat(cpu_dir_fd, "cpu0/cpufreq/cpuinfo_cur_freq", O_RDONLY | O_NONBLOCK)打开目标文件
    这种方式利用内核已缓存的目录项(dentry),避免重复解析完整路径,提升文件打开速度。

2. 提升VFS缓存命中率

  • 保持访问路径的一致性,让内核缓存常用的目录项和inode,减少元数据查询开销
  • 避免频繁访问大量不同路径的文件,防止缓存被频繁替换导致命中率下降

3. 选择高效的系统调用

  • 批量读取目录内容时,使用getdents()替代多次readdir(),一次性获取多个目录项,减少系统调用次数
  • 读取小文件时,优先使用read()或pread()(无需额外lseek()操作),避免复杂IO接口带来的额外开销

4. 减少不必要的数据拷贝

sysfs文件内容通常很小,内核会通过copy_to_user()将数据拷贝到用户态。若批量读取多个文件,可尝试将结果写入连续的用户态缓冲区,减少多次拷贝的开销——此优化仅在极端高频场景下考虑,需权衡实现复杂度。

内容的提问来源于stack exchange,提问作者Kron

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 11:36:00