我正在阅读2.6.11版linux内核的源代码,对arch/i386/kernel/process.c
中的内联汇编kernel_thread_helper
感到困惑。
首先,寄存器%ebx
存储一个函数的地址,寄存器%edx
存储一个指向该函数的指针。
extern void kernel_thread_helper(void);
__asm__(".section .text\n"
".align 4\n"
"kernel_thread_helper:\n\t"
"movl %edx,%eax\n\t"
"pushl %edx\n\t"
"call *%ebx\n\t"
"pushl %eax\n\t"
"call do_exit\n"
".previous");
我纳闷道:
- 这段代码为什么要执行
movl %edx,%eax
?因为pushl %edx
已经将%edx
的值压入堆栈,那么%ebx
指向的函数应该可以成功地从堆栈中检索到所需的参数。那么,是否不需要将%edx
复制到%eax
? - 我知道当
__attribute__((regparm(3)))
被添加到函数定义之前时,函数将按照%eax
、%edx
、%ecx
和堆栈的顺序检索参数。将%edx
中的值复制到%eax
的指令是否设计为与使用此__attribute__
的函数兼容?具有这样一个属性的函数应该从%eax
中检索所需的参数并继续运行!但这会导致一个问题:如果没有人将先前由pushl %edx
压入的指针弹出,则该指针可保留在堆栈中。
我是Linux内核的新手,可能误解了一些东西。我想知道这些汇编指令实际上是如何工作的,我的猜测有什么问题。
1条答案
按热度按时间pieyvz9o1#
早期Linux内核版本(v2.5.0)中的一条注解对此进行了解释(为了更准确起见,v2.1.131中添加了
movl
到eax
):因此,是的,正如您所怀疑的,将
movl
转换为eax
正是为了为使用-mregparam
编译的 both 函数或等效的__attribute__
* 和 * 不使用(i386 System V ABI调用约定,即堆栈上的所有参数)提供兼容性。但这就引起了一个问题:如果没有人将先前由
pushl %edx
压入的指针弹出,则该指针可保留在堆栈中。好吧,没错,但是如果我们在调用内核线程函数之后只做
pushl %eax; call do_exit
的话,谁会在乎弹出它呢?执行movl %eax, 0(%esp)
也可以,但是如果有足够的堆栈空间来运行内核线程,那么在它返回之后肯定有4个字节来压入寄存器。另外,pushl %eax
只有1个字节,而movl
只有3个字节。我不确定这两者的延迟或吞吐量,但我真的不认为这是这样一段代码中的问题。