簡體   English   中英

kernel_thread_helper如何使用內聯匯編將參數傳遞給kernel線程function?

[英]How does kernel_thread_helper pass parameters to kernel thread function using inline assembly?

我正在閱讀版本 2.6.11 的 linux kernel 的源代碼,並對arch/i386/kernel/process.c中的內聯匯編kernel_thread_helper感到困惑。

以下是匯編代碼。 最初,寄存器%ebx存儲 function 的地址,寄存器%edx存儲將傳遞給 function 的指針。

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指向的 function 應該會成功地從堆棧中檢索所需的參數。 那么是否不需要將%edx復制到%eax

  • 我知道當在 function 定義前面添加__attribute__((regparm(3)))時,function 將按%eax%edx%ecx和堆棧的順序檢索參數。 所以我想知道,將%edx中的值復制到%eax的指令是否旨在與使用此__attribute__的函數兼容? 具有這樣一個屬性的 Function 應該在運行時從%eax和 go 檢索所需的參數:但這會導致一個問題,如果沒有人將其彈出,先前由pushl %edx壓入的指針可能會保留在堆棧中。

我是 Linux Kernel 的新手,可能誤解了一些東西。 我想知道這些匯編指令實際上是如何工作的,以及我的猜測有什么問題。

較早的 Linux kernel 版本(v2.5.0)中的評論解釋了這一點( 在 v2.1.131 中添加movl into eax以真正准確):

int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
{
    long retval, d0;

    __asm__ __volatile__(
        "movl %%esp,%%esi\n\t"
        "int $0x80\n\t"     /* Linux/i386 system call */
        "cmpl %%esp,%%esi\n\t"  /* child or parent? */
        "je 1f\n\t"     /* parent - jump */
        /* Load the argument into eax, and push it.  That way, it does
         * not matter whether the called function is compiled with
         * -mregparm or not.  */
        "movl %4,%%eax\n\t"
        "pushl %%eax\n\t"       
        "call *%5\n\t"      /* call fn */
        "movl %3,%0\n\t"    /* exit */
        "int $0x80\n"
        "1:\t"
        :"=&a" (retval), "=&S" (d0)
        :"0" (__NR_clone), "i" (__NR_exit),
         "r" (arg), "r" (fn),
         "b" (flags | CLONE_VM)
        : "memory");
    return retval;
}

所以是的,正如您所懷疑的那樣, movl into eax正是為使用-mregparam或等效的__attribute__編譯的兩個函數提供兼容性,沒有(i386 System V ABI 調用約定,即堆棧上的所有參數)。

但這會導致一個問題:如果沒有人將其彈出,則pushl %edx先前推送的指針可能會保留在堆棧中。

好吧,沒錯,但是如果我們在調用 kernel 線程 function 之后唯一要做的事情是pushl %eax; call do_exit pushl %eax; call do_exit 執行movl %eax, 0(%esp)也可以,但是如果有足夠的堆棧空間來運行 kernel 線程,那么在它返回后肯定有 4 個字節來推送寄存器。 此外pushl %eax只有 1 個字節,而movl是 3 個字節,我不確定兩者的延遲或吞吐量,但我真的不認為這是這樣一段代碼的問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM