CPU 延迟工作处理

CPU Handling with Delayed Work

嘿,所以我正在 kernel source code 中查看此功能。我试图弄清楚 Linux 如何处理无法在本地 CPU.

上安排任务的情况
/**
* queue_delayed_work - queue work on a workqueue after delay
* @wq: workqueue to use
* @dwork: delayable work to queue
* @delay: number of jiffies to wait before queueing
*
* Equivalent to queue_delayed_work_on() but tries to use the local CPU.
*/
static inline bool queue_delayed_work(struct workqueue_struct *wq,
                                      struct delayed_work *dwork,
                                      unsigned long delay)
{
        return queue_delayed_work_on(WORK_CPU_UNBOUND, wq, dwork, delay);
}

这里是 queue_delayed_work_on:

bool queue_delayed_work_on(int cpu, struct workqueue_struct *wq,
                           struct delayed_work *dwork, unsigned long delay)
{
        struct work_struct *work = &dwork->work;
        bool ret = false;
        unsigned long flags;

        /* read the comment in __queue_work() */
        local_irq_save(flags);

        if (!test_and_set_bit(WORK_STRUCT_PENDING_BIT, work_data_bits(work))) {
                __queue_delayed_work(cpu, wq, dwork, delay);
                ret = true;
        }

        local_irq_restore(flags);
        return ret;
}

假设您有 4 个 CPU 并且它无法在 CPU 1 上安排任务,它会选择哪个,它在源代码中的何处处理?我找了好久也没找到。即使您不明白它是如何工作的,我也非常感谢 link 魔法发生的地方。

如您所见,queue_delayed_work 会将 cpu 参数设置为 WORK_CPU_UNBOUND。该值定义为大于内核支持的实际 CPU 数。此值传递给 __queue_delayed_work,如果 delay 非零,将使用计时器(使用 add_timer 函数在指定时间后触发回调函数 delayed_work_timer_fn(此回调函数在工作队列初始化时定义。这个回调函数所做的就是调用 __queue_work,仍然将 WORK_CPU_UNBOUND 作为 cpu 参数传递。所以整个 "magic" 就在那里发生。

此函数将检查 cpu 参数是否设置为 WORK_CPU_UNBOUND 并选择 cpu 作为当前处理器:

if (req_cpu == WORK_CPU_UNBOUND)
    cpu = raw_smp_processor_id()

因此工作将在处理之前设置的定时器中断的处理器上执行。现在我没有研究定时器代码,而是研究 LDD3 书中的 IIRC,定时器中断将由它们注册的 CPU 处理(除非此 CPU 将同时被禁用,当然,在在这种情况下,计时器 IRQ 将移至其他 CPU) 但那本书有些旧了,这可能不再适用了。

内核代码中还有另一个提示可以证明我写的内容 - 请参阅 queue_work 函数的注释:"We queue the work to the CPU on which it was submitted, but if the CPU dies it can be processed by another CPU"。此函数还使用 WORK_CPU_UNBOUND 作为 cpu 参数。

计时器迁移详细信息

如前所述,如果某个处理器出现故障,它就无法再处理 IRQ,因此它也无法处理已注册的定时器。因此,当 CPU 离线时,内核会将所有挂起的计时器迁移到其他 CPU。此任务由 migrate_timers() 函数完成,该函数由 timer_cpu_notify 运行 完成,后者又是注册为 cpu_notifier.

的回调 当 cpu 状态更改为 CPU_DEADCPU_DEAD_FROZEN 时,

migrate_timers 为 运行。通过调用 _cpu_down 函数在 _cpu_down 函数内部设置此状态:

cpu_notify_nofail(CPU_DEAD | mod, hcpu);

它在 __cpu_die(cpu) 之后调用,确保我们禁用的 CPU 不再工作,因此我们可以确定此代码 运行 在其他 CPU 上. migrate_timers 会将所有计时器重新分配给 CPU 其 运行 宁。

那么 CPU 接管计时器应该在哪里决定呢?可以说这是由调度程序完成的:

  1. 如果您在与您要禁用的 CPU 不同的 cpu_down 上调用 cpu_down,那么这就是将接管的 CPU。

  2. 如果您在将要禁用的 CPU 上调用 cpu_down,它将自行安排在 __cpu_die 中,其余代码将然后重新安排在其他 CPU.

我不知道,但请看一下 LWN 的 Scheduler 部分。