簡體   English   中英

為什么Golang克隆syscall abi與x86-64上的Linux內核克隆不同

[英]Why golang clone syscall abi is diffent from linux kernel clone on x86-64

在glibc / sysdeps / unix / sysv / linux / x86_64 / clone.S中的Linux內核克隆abi定義:

The kernel expects:
rax: system call number
rdi: flags
rsi: child_stack
rdx: TID field in parent
r10: TID field in child
r8: thread pointer

以及go1.11.5 / src / runtime / sys_linux_amd64.s上的golang克隆syscall:

// int32 clone(int32 flags, void *stk, M *mp, G *gp, void (*fn)(void));
TEXT runtime·clone(SB),NOSPLIT,$0
    MOVL    flags+0(FP), DI
    MOVQ    stk+8(FP), SI
    MOVQ    $0, DX
    MOVQ    $0, R10

    // Copy mp, gp, fn off parent stack for use by child.
    // Careful: Linux system call clobbers CX and R11.
    MOVQ    mp+16(FP), R8
    MOVQ    gp+24(FP), R9
    MOVQ    fn+32(FP), R12

    MOVL    $SYS_clone, AX
    SYSCALL

那么,為什么DX,R10,R8不遵守clone-syscall promise? 另一方面,R9和R12似乎是不必要的。

請幫我。

DX和R10為零的原因

根據clone聯機幫助頁,僅在設置CLONE_PARENT_SETTIDCLONE_CHILD_SETTID時才使用CLONE_PARENT_SETTID

CLONE_PARENT_SETTID(從Linux 2.5.49開始)將子線程ID存儲在父級內存中的ptid位置。 (在Linux 2.5.32-2.5.48中,有一個標志CLONE_SETTID做到了這一點。)存儲操作在clone()將控制權返回給用戶空間之前完成。

CLONE_CHILD_SETTID(從Linux 2.5.49開始)將子線程ID存儲在子代內存中的位置ctid。 在clone()將控制權返回給用戶空間之前,存儲操作完成。

DX和R10與此手冊頁( 參考 )中的ptidctid相對應。

實際上,從os_linux.go: Source調用runtime.clone()時未設置此標志。

他們不需要tid的原因可能是因為它不是pthread之類的庫,用戶可以使用tid完成一些復雜的事情。

R8,R9和R12用於什么

簡而言之,R8,R9和R12不會被系統調用使用,而是用於在其后構造堆棧。

請注意,R8和R9作為參數傳遞給系統調用,但未被克隆使用(請參見下面的原因),並且R12在系統調用之后被保留,在系統調用之后可以安全地使用這些寄存器。 參考

讓我們看看細節。

內部調用runtime.clone如下:

func newosproc(mp *m) {
    stk := unsafe.Pointer(mp.g0.stack.hi)
    ....
    ret := clone(cloneFlags, stk, unsafe.Pointer(mp), unsafe.Pointer(mp.g0), unsafe.Pointer(funcPC(mstart)))
    ....
}

閱讀Go的匯編程序快速指南 ,並發布OP代碼,您可以看到R8是mp指針,R9是mp.g0指針,R12是要在clone線程中調用的某些函數的指針。 mg結構如下: Source和this: Source )。

R8是clone的參數,表示tls(線程本地存儲),但除非設置了CLONE_SETTLS否則不使用它: Source

R9通常用作系統調用的第六個參數,但是clone不使用它,因為它僅使用5個參數( Source )。

R12是在系統調用后保留的寄存器。

最后,讓我們看看runtime.clone的源代碼 重要的是在SYSCALL 他們正在使用已創建的子線程中的R8和R9進行一些堆棧設置,並最終調用R12。

// int32 clone(int32 flags, void *stk, M *mp, G *gp, void (*fn)(void));
TEXT runtime·clone(SB),NOSPLIT,$0
    MOVL    flags+0(FP), DI
    MOVQ    stk+8(FP), SI
    MOVQ    $0, DX
    MOVQ    $0, R10

    // Copy mp, gp, fn off parent stack for use by child.
    // Careful: Linux system call clobbers CX and R11.
    MOVQ    mp+16(FP), R8
    MOVQ    gp+24(FP), R9
    MOVQ    fn+32(FP), R12

    MOVL    $SYS_clone, AX
    SYSCALL

    // In parent, return.
    CMPQ    AX, $0
    JEQ 3(PC)
    MOVL    AX, ret+40(FP)
    RET

    // In child, on new stack.
    MOVQ    SI, SP

    // If g or m are nil, skip Go-related setup.
    CMPQ    R8, $0    // m
    JEQ nog
    CMPQ    R9, $0    // g
    JEQ nog

    // Initialize m->procid to Linux tid
    MOVL    $SYS_gettid, AX
    SYSCALL
    MOVQ    AX, m_procid(R8)

    // Set FS to point at m->tls.
    LEAQ    m_tls(R8), DI
    CALL    runtime·settls(SB)

    // In child, set up new stack
    get_tls(CX)
    MOVQ    R8, g_m(R9)
    MOVQ    R9, g(CX)
    CALL    runtime·stackcheck(SB)

nog:
    // Call fn
    CALL    R12

//(omitted)

暫無
暫無

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

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