linux - 为什么 schedule() 在使用默认的 prepare_arch_switch() 时不会导致死锁

标签 linux schedule

在 Linux 2.6.11.12 中,在 shedule() 函数选择“下一个”任务运行之前,它会锁定运行队列

spin_lock_irq(&rq->lock);

而且,在调用context_switch() 执行上下文切换之前,它会调用prepare_arch_switch(),默认是空操作:

/*
 * Default context-switch locking:
 */
#ifndef prepare_arch_switch
# define prepare_arch_switch(rq, next)  do { } while (0)
# define finish_arch_switch(rq, next)   spin_unlock_irq(&(rq)->lock)
# define task_running(rq, p)        ((rq)->curr == (p))
#endif

也就是说,它将持有rq->lock直到switch_to()返回,然后,宏finish_arch_switch()实际上释放锁。

假设,有任务A、B、C。现在A调用schedule()并切换到B(现在,rq->lock是锁定)。 B 迟早会调用 schedule()。此时,B 被 A 锁定了,如何获取rq->lock

还有一些arch依赖的实现,比如:

/*
 * On IA-64, we don't want to hold the runqueue's lock during the low-level context-switch,
 * because that could cause a deadlock.  Here is an example by Erich Focht:
 *
 * Example:
 * CPU#0:
 * schedule()
 *    -> spin_lock_irq(&rq->lock)
 *    -> context_switch()
 *       -> wrap_mmu_context()
 *          -> read_lock(&tasklist_lock)
 *
 * CPU#1:
 * sys_wait4() or release_task() or forget_original_parent()
 *    -> write_lock(&tasklist_lock)
 *    -> do_notify_parent()
 *       -> wake_up_parent()
 *          -> try_to_wake_up()
 *             -> spin_lock_irq(&parent_rq->lock)
 *
 * If the parent's rq happens to be on CPU#0, we'll wait for the rq->lock
 * of that CPU which will not be released, because there we wait for the
 * tasklist_lock to become available.
 */
#define prepare_arch_switch(rq, next)       \
do {                                        \
    spin_lock(&(next)->switch_lock);        \
    spin_unlock(&(rq)->lock);               \
} while (0)
#define finish_arch_switch(rq, prev)    spin_unlock_irq(&(prev)->switch_lock)

在这种情况下,我非常确定这个版本会做正确的事情,因为它在调用 context_switch() 之前解锁了 rq->lock

但是默认实现会发生什么?它如何正确地做事?

最佳答案

我在linux 2.6.32.68的context_switch()中找到一条注释,说的是代码下的故事:

/*
 * Since the runqueue lock will be released by the next
 * task (which is an invalid locking op but in the case
 * of the scheduler it's an obvious special-case), so we
 * do an early lockdep release here:
 */

但是我们还没有切换到另一个锁定了的任务,下一个任务会解锁它,如果下一个任务是新创建的,函数ret_from_fork() 最终也会调用 finish_task_switch() 来解锁 rq->lock

关于linux - 为什么 schedule() 在使用默认的 prepare_arch_switch() 时不会导致死锁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33967687/

相关文章:

linux - 如何在 SED 中使用 AWK

c - waitpid/wait/waitid选哪个?

android - Android 中的 WorkManager 多次执行 do Work()

sql - 如何通过 T-SQL 在 SQL Server 2008 中创建计划作业?

azure - 完成所有依赖管道 yaml 后触发管道

java - 推迟定时器安排java

c - 如何在 C 代码中更改 linux 用户?

linux - Bash 脚本 - 如何只匹配 4 字节十六进制值中的 1 个字节

linux - 如何使用 gdb 调试 gstreamer?

excel - 使用调度(分配值)