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
      3
      int cpu = get_cpu(); // 禁用抢占并获取当前 CPU
      // ... 操作当前 CPU 的私有数据 ...
      put_cpu(); // 重新启用抢占
    • 或手动禁用抢占:
      1
      2
      3
      4
      preempt_disable();
      int cpu = smp_processor_id();
      // ... 操作 ...
      preempt_enable();

2. 中断上下文的使用

  • 中断上下文的安全性
    • 硬中断(Hard IRQ)软中断(SoftIRQ)中,抢占已被隐式禁用,可直接使用 smp_processor_id()
    • 线程化中断(Threaded IRQ)中,需手动禁用抢占,因为可能被调度到其他 CPU。

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
    3
    int cpu = get_cpu();  // 禁用抢占
    per_cpu_ptr(my_data, cpu)->count++;
    put_cpu(); // 启用抢占

总结

  • 核心原则:确保调用 smp_processor_id() 后,代码逻辑在同一 CPU 上持续执行。
  • 推荐实践:优先使用 get_cpu()/put_cpu() 或结合抢占控制,避免直接调用 smp_processor_id() 裸函数。