纯用户态进程perf stats中内核内存操作的成因与归因疑问
测试场景
- 将线程绑定至CPU 1,从预分配并初始化的2GB内存区域执行随机读取,内存访问循环期间无系统调用。
- 独立运行perf进程,执行测量指令:
mem_inst_retired.all_loads:k,mem_inst_retired.all_stores:k -I 200 -p <pid>
极简测试代码
void access_memory(char *memory) { // 绑定线程到CPU 1 cpu_set_t cpuset; CPU_ZERO(&cpuset); CPU_SET(1, &cpuset); pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset); std::mt19937 gen(std::random_device{}()); std::uniform_int_distribution<size_t> dist(0, 2GB - 500); char buffer[500]; while (!should_stop) { size_t offset = dist(gen); memcpy(buffer, memory + offset, 500); buffer[0]++; } }
观测图表

图表显示内核加载/存储操作量异常偏高,怀疑与perf采样行为相关。
技术问题
- 为何仅执行用户态内存读取的进程会被统计到内核内存操作?是否是PMU(性能监控单元)采样产生的中断导致内核执行的内存操作被归因到该进程?查阅Intel SDM文档未找到明确答案。
- perf是如何将内核态操作归因到特定进程的?对于系统调用,内核确实是“代表进程”工作,但调度、内存整理、负载均衡等内核任务属于系统维护操作,并非为进程执行,为何会关联到该进程?其归因边界如何界定?
额外观测
将采样间隔从200ms改为5s后,每间隔测量到的内核操作数从10⁵变为10⁷。
通过taskset -c 1运行测试程序后,执行以下perf命令:
sudo perf record -e mem_inst_retired.all_loads:k -p <PID> -- sleep 20
得到的采样结果如下:
# Total Lost Samples: 0 # # Samples: 15 of event 'mem_inst_retired.all_loads:k' # Event count (approx.): 30000045 # # Overhead Command Shared Object Symbol # ........ ......... ................. .................................. # 13.33% perf-ldst [kernel.kallsyms] [k] decay_load 13.33% perf-ldst [kernel.kallsyms] [k] read_tsc 13.33% perf-ldst [kernel.kallsyms] [k] update_process_times 13.33% perf-ldst [kernel.kallsyms] [k] update_vsyscall 6.67% perf-ldst [kernel.kallsyms] [k] __raw_spin_lock_irqsave 6.67% perf-ldst [kernel.kallsyms] [k] __update_load_avg_se 6.67% perf-ldst [kernel.kallsyms] [k] _raw_spin_lock_irqsave 6.67% perf-ldst [kernel.kallsyms] [k] irq_work_run_list 6.67% perf-ldst [kernel.kallsyms] [k] timekeeping_adjust.constprop.0 6.67% perf-ldst [kernel.kallsyms] [k] timekeeping_advance 6.67% perf-ldst [kernel.kallsyms] [k] xas_move_index
此处的核心疑问是:定时器/调度器中断及管理操作属于系统维护任务,无论哪个进程运行都会执行,并非像系统调用那样“代表进程”执行,为何会被归因到当前运行的用户进程?
问题解答
1. 内核内存操作被统计的原因
使用mem_inst_retired.all_loads:k这类带:k修饰符的事件时,perf会统计所有内核态下执行的内存操作,其归因逻辑基于当前CPU上正在运行的进程:当PMU触发采样中断时,内核会读取CPU的current进程指针(即当前占用CPU的用户进程),并将此次内核中的内存操作关联到该进程。
你的测试线程绑定在CPU1上,几乎独占该CPU,因此CPU1上发生的所有内核中断(包括perf采样自身触发的中断、定时器中断、调度器周期任务)带来的内核内存操作,都会被归因到这个用户进程。此外,perf采样本身会触发内核的中断处理流程,该流程包含大量内存读写操作(如保存/恢复寄存器、更新采样数据结构等),这些操作也会被计入该进程的内核内存操作统计。
2. perf的进程归因规则边界
perf的归因逻辑核心是基于CPU的上下文关联,而非判断任务是否“为进程服务”:
- 当CPU处于用户态执行时,所有操作直接归因到当前进程。
- 当CPU进入内核态时,分两种情况:
- 主动进入内核态:如系统调用、缺页异常,此时内核明确为当前进程服务,操作会归因到该进程。
- 被动进入内核态:如外部中断、定时器中断,此时内核执行系统维护任务,但由于中断发生时CPU正被某个用户进程占用,perf会将这些内核操作关联到这个“被中断”的进程。
这种设计源于硬件层面的限制:中断发生时CPU的上下文属于当前运行的进程,perf无法区分内核操作是“为进程服务”还是“系统自身任务”,只能基于当前的进程上下文进行归因。
你观察到采样间隔从200ms改为5s后内核操作数大幅增加,原因是采样间隔越长,单次采样需要统计的内核操作累积量越大——更长的间隔意味着内核在该CPU上执行的系统维护任务(如定时器触发的负载更新、时间同步等)更多,这些操作都会被关联到你的测试进程。
内容的提问来源于stack exchange,提问作者idle_cycles

