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);
}
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_DEAD
或 CPU_DEAD_FROZEN
时,migrate_timers
为 运行。通过调用 _cpu_down
函数在 _cpu_down
函数内部设置此状态:
cpu_notify_nofail(CPU_DEAD | mod, hcpu);
它在 __cpu_die(cpu)
之后调用,确保我们禁用的 CPU 不再工作,因此我们可以确定此代码 运行 在其他 CPU 上. migrate_timers
会将所有计时器重新分配给 CPU 其 运行 宁。
那么 CPU 接管计时器应该在哪里决定呢?可以说这是由调度程序完成的:
如果您在与您要禁用的 CPU 不同的 cpu_down
上调用 cpu_down
,那么这就是将接管的 CPU。
如果您在将要禁用的 CPU 上调用 cpu_down
,它将自行安排在 __cpu_die
中,其余代码将然后重新安排在其他 CPU.
我不知道,但请看一下 LWN 的 Scheduler 部分。
嘿,所以我正在 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);
}
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_DEAD
或 CPU_DEAD_FROZEN
时,migrate_timers
为 运行。通过调用 _cpu_down
函数在 _cpu_down
函数内部设置此状态:
cpu_notify_nofail(CPU_DEAD | mod, hcpu);
它在 __cpu_die(cpu)
之后调用,确保我们禁用的 CPU 不再工作,因此我们可以确定此代码 运行 在其他 CPU 上. migrate_timers
会将所有计时器重新分配给 CPU 其 运行 宁。
那么 CPU 接管计时器应该在哪里决定呢?可以说这是由调度程序完成的:
如果您在与您要禁用的 CPU 不同的
cpu_down
上调用cpu_down
,那么这就是将接管的 CPU。如果您在将要禁用的 CPU 上调用
cpu_down
,它将自行安排在__cpu_die
中,其余代码将然后重新安排在其他 CPU.
我不知道,但请看一下 LWN 的 Scheduler 部分。