Linux 内核调度程序代码中 context_switch() 中的上一个任务的解释

问题描述 投票:0回答:1

我正在尝试了解 Linux 内核(特别是 x86)中上下文切换的实现,为此我有几个问题。

  1. 为什么

    switch_to()
    宏的定义和调用如下所示?

  2. 下面注释中位置 (1) 中

    prev
    的值是否与位置 (2) 中的值相同,或者作为
    switch_to()
    一部分的堆栈切换是否会改变它?如果
    prev
    next
    存储在寄存器
    rsi
    rdx
    中,而它们似乎没有保存在
    __switch_to_asm()
    中,我会假设
    prev
    在位置 (1) 和 (2) 中是相同的),但考虑到宏的定义,也许情况并非如此?

#define switch_to(prev, next, last)                 \
do {                                    \
    ((last) = __switch_to_asm((prev), (next)));         \
} while (0)

从 Linux 内核 v5.8.6 开始:

static __always_inline struct rq *
context_switch(struct rq *rq, struct task_struct *prev,
           struct task_struct *next, struct rq_flags *rf)
{
    prepare_task_switch(rq, prev, next);

    /*
     * For paravirt, this is coupled with an exit in switch_to to
     * combine the page table reload and the switch backend into
     * one hypercall.
     */
    arch_start_context_switch(prev);

    /*
     * kernel -> kernel   lazy + transfer active
     *   user -> kernel   lazy + mmgrab() active
     *
     * kernel ->   user   switch + mmdrop() active
     *   user ->   user   switch
     */
    if (!next->mm) {                                // to kernel
        enter_lazy_tlb(prev->active_mm, next);

        next->active_mm = prev->active_mm;
        if (prev->mm)                           // from user
            mmgrab(prev->active_mm);
        else
            prev->active_mm = NULL;
    } else {                                        // to user
        membarrier_switch_mm(rq, prev->active_mm, next->mm);
        /*
         * sys_membarrier() requires an smp_mb() between setting
         * rq->curr / membarrier_switch_mm() and returning to userspace.
         *
         * The below provides this either through switch_mm(), or in
         * case 'prev->active_mm == next->mm' through
         * finish_task_switch()'s mmdrop().
         */
        switch_mm_irqs_off(prev->active_mm, next->mm, next);

        if (!prev->mm) {                        // from kernel
            /* will mmdrop() in finish_task_switch(). */
            rq->prev_mm = prev->active_mm;
            prev->active_mm = NULL;
        }
    }

    rq->clock_update_flags &= ~(RQCF_ACT_SKIP|RQCF_REQ_SKIP);

    prepare_lock_switch(rq, next, rf);

// LOCATION (1) 

    /* Here we just switch the register state and the stack. */
    switch_to(prev, next, prev);

// LOCATION (2) 

    barrier();

    return finish_task_switch(prev);
}
linux linux-kernel scheduler context-switching
1个回答
1
投票

为什么

switch_to()
宏的定义和调用如下所示(下面引用)?

我只能假设此功能是作为宏实现的,因为它依赖于架构,并且可能需要的不仅仅是简单的函数调用(就像您在 x86 中看到的那样)。这是特定于 arch 的代码的常见模式。您可以在here查看宏的不同实现。

下面注释中位置 (1) 中

prev
的值与位置 (2) 中的值相同,还是作为
switch_to()
一部分的堆栈切换会更改它?

堆栈开关并不重要。

prev
的值既不会从堆栈中取出,也不会保存到堆栈中。
prev
的新值将只是
__switch_to_asm()
的返回值,即
__switch_to()
的返回值(因为前者对后者执行尾部调用)。由于
__switch_to()
按原样返回传递的
prev
参见代码),因此最终结果是
prev
值保持不变。

这是有道理的,你不希望通过

prev
切换上下文后
switch_to()
的值发生变化。如果您使用
prev=A next=B
进行安排,并且之前
B
prev=B next=C
切换掉,那么您希望
B
使用
prev=A
恢复,而不是 prev=B

© www.soinside.com 2019 - 2024. All rights reserved.