smp_processor_id
smp_processor_id
smp_processor_id使用注意事项
smp_processor_id()
是 Linux 内核中用于获取当前 CPU 编号的函数,常用于调试、统计或与 CPU 绑定的操作。然而,使用时需特别注意以下几点:
1. 抢占安全性
- 问题:
在可抢占内核(CONFIG_PREEMPT
启用)中,若未禁用抢占,调用smp_processor_id()
后可能发生调度,导致代码后续逻辑运行在其他 CPU 上,引发数据不一致或逻辑错误。 - 解决方案:
- 使用
get_cpu()
和put_cpu()
包裹代码:1
2
3int cpu = get_cpu(); // 禁用抢占并获取当前 CPU
// ... 操作当前 CPU 的私有数据 ...
put_cpu(); // 重新启用抢占 - 或手动禁用抢占:
1
2
3
4preempt_disable();
int cpu = smp_processor_id();
// ... 操作 ...
preempt_enable();
- 使用
2. 中断上下文的使用
- 中断上下文的安全性:
- 在硬中断(Hard IRQ)或软中断(SoftIRQ)中,抢占已被隐式禁用,可直接使用
smp_processor_id()
。 - 在线程化中断(Threaded IRQ)中,需手动禁用抢占,因为可能被调度到其他 CPU。
- 在硬中断(Hard IRQ)或软中断(SoftIRQ)中,抢占已被隐式禁用,可直接使用
3. 返回值有效性
- CPU 热插拔:
内核支持 CPU 热插拔时,返回的 CPU ID 可能对应一个已离线(Offline)的 CPU。需通过cpu_online(cpu)
检查其状态。 - 范围限制:
返回值应满足0 <= cpu < nr_cpu_ids
,不可假设系统支持的最大 CPU 数量。
4. 调试与锁机制
- Lockdep 警告:
若在未禁用抢占时使用smp_processor_id()
,调试工具(如 Lockdep)可能发出警告。应优先使用get_cpu()
确保正确性。 - DEBUG_PREEMPT 配置:
启用该调试选项时,错误使用可能导致内核警告或崩溃。
5. 单处理器(UP)系统行为
- 在单处理器系统中,
smp_processor_id()
恒返回 0,但代码需兼容 SMP 场景,避免假设返回值始终为 0。
6. 不要长期依赖返回值
- CPU 迁移:
进程可能被迁移到其他 CPU,尤其在未禁用抢占或绑定 CPU 的情况下。若需长期跟踪 CPU,应结合cpu_relax()
或绑定进程到 CPU。
7. 替代接口
- **
raw_smp_processor_id()
**:
低阶接口,不进行抢占或调试检查,仅在内核确定抢占已禁用时使用(如中断处理)。 this_cpu
操作:
访问当前 CPU 的 Per-CPU 变量时,优先使用this_cpu_read()
/this_cpu_write()
等接口,自动处理抢占和中断。
错误示例与修正
错误用法:
1
2
3// 未禁用抢占,可能被调度到其他 CPU
int cpu = smp_processor_id();
per_cpu_ptr(my_data, cpu)->count++;正确用法:
1
2
3int cpu = get_cpu(); // 禁用抢占
per_cpu_ptr(my_data, cpu)->count++;
put_cpu(); // 启用抢占
总结
- 核心原则:确保调用
smp_processor_id()
后,代码逻辑在同一 CPU 上持续执行。 - 推荐实践:优先使用
get_cpu()
/put_cpu()
或结合抢占控制,避免直接调用smp_processor_id()
裸函数。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Helloeuler!