为什么Linux在执行上下文切换时会保存%ebp?

时间:2015-02-26 14:39:11

标签: linux assembly linux-kernel x86

在进行上下文切换时,x86 Linux(非常巧妙地)避免了保存和恢复EAX,EBX,ECX,EDX,ESI和EDI。当然,在切换到内核模式时,userland值会保存在内核堆栈中。但是 in 内核代码中的值不会被保存 - 而是使用GCC指令,它们告诉编译器不要在发生切换时保留这些寄存器中所需的任何值。

当然,ESP必须得到保存和恢复。但这是我不明白的:在ESP切换之前,EBP被推到内核堆栈上。我认为EBP被用作帧指针,但在我的内核调试器中,这些值肯定不像它:

(gdb) print $esp
$22 = (void *) 0xc0025ec0
(gdb) print $ebp
$23 = (void *) 0xcf827f3c

差异是方式太大,EBP无法成为这里的帧指针。代码中的注释表示" EBP是为wchan访问明确保存/恢复的,但是我在搜索代码时无法弄清楚是怎么回事。谷歌也没有帮助。一些内核向导可以介入并在这里提供帮助吗?

2 个答案:

答案 0 :(得分:3)

  

差异太大,EBP无法成为这里的帧指针。

据推测,您已编译内核而未启用帧指针。请参阅相关的配置选项:

config SCHED_OMIT_FRAME_POINTER
        def_bool y
        prompt "Single-depth WCHAN output"
        depends on X86
        ---help---
          Calculate simpler /proc/<PID>/wchan values. If this option
          is disabled then wchan values will recurse back to the
          caller function. This provides more accurate wchan values,
          at the expense of slightly more scheduling overhead.

          If in doubt, say "Y".

函数get_wchan将对ebp值进行健全性检查,只有在它似乎是帧指针时才使用它。

我认为最好在两个地方都使用上面的配置标志,这样如果ebp不是帧指针,get_wchan就不会被不必要地保存,ebp也不会如果我们知道不会有帧指针,那就麻烦了。也就是说,保存/恢复{{1}}只会增加很少的开销,所以这并不是悲剧性的。

答案 1 :(得分:2)

我已经弄清楚了。 EBP 一个帧指针,但在我检查其值时,ESP已经切换到新进程的内核堆栈,但EBP尚未恢复(因此它仍具有值从前一个过程)。遗憾!!

存储帧指针的原因是其他人可以确定进程进入休眠状态的内核代码中的位置。除其他外,它由/proc/PID/wchan使用,它打印使进程休眠的内核函数的名称。

检查此代码的代码如下(为简洁起见,删除了详细信息)

unsigned long get_wchan(struct task_struct *p)
{
    unsigned long sp, bp, ip;
    sp = p->thread.sp;
    bp = *(unsigned long *) sp;
    do {
        ip = *(unsigned long *) (bp+4);
        if (!in_sched_functions(ip))
            return ip;
        bp = *(unsigned long *) bp;
    } while (count++ < 16);
    return 0;
}

由于在切换内核堆栈之前正好推送EBP,因此休眠进程的堆栈指针将指向保存的EBP(帧指针)值。该帧指针指向调用者保存的帧指针,该指针指向前一个调用者的指针,指向前一个调用者的...换句话说,保存的帧指针形成一个返回调用堆栈的链表。

帧指针立即保存在函数入口上,因此它上面的值(4个字节以上)是调用函数的返回地址。

get_wchan中的循环遍历“链表”(bp = *bp),检查每个保存帧指针上方的返回地址,直到它在ep_poll之类的函数中找到地址或futex_wait_queue_me

get_wchan只返回函数内的地址;要在/proc中显示,lookup_symbol_name用于将该地址转换为函数名称。