Почему syscall abi golang clone отличается от клона ядра Linux на x86-64 - PullRequest
0 голосов
/ 26 февраля 2019

Определение abi клона ядра Linux в glibc / sysdeps / unix / sysv / linux / x86_64 / clone.S:

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

И системный вызов клона golang на go1.11.5 / src / runtime / sys_linux_amd64.s:

// 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 не выполняют обещание клон-системного вызова?С другой стороны, R9 и R12 кажутся ненужными.

Пожалуйста, помогите мне.

1 Ответ

0 голосов
/ 06 марта 2019

Причина, по которой DX и R10 равны нулю

В соответствии с справочной страницей клона , они используются только когда установлены CLONE_PARENT_SETTID, CLONE_CHILD_SETTID.

CLONE_PARENT_SETTID (начиная с Linux 2.5.49) Сохраните идентификатор дочернего потока в месте ptid в памяти родителя.(В Linux 2.5.32-2.5.48 был флаг CLONE_SETTID, который сделал это.) Операция сохранения завершается до того, как clone () возвращает управление в пространство пользователя.

CLONE_CHILD_SETTID (начиная с Linux 2.5.49) СохранитеID дочернего потока в месте ctid в памяти ребенка.Операция сохранения завершается до того, как clone () вернет управление в пространство пользователя.

DX и R10 соответствуют ptid и ctid на этой man-странице ( Reference ).

На самом деле этот флаг не устанавливается при вызове runtime.clone () из os_linux.go: Source .

Причина, по которой им не нужен 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 ed.(структура m и g выглядит следующим образом: Источник и это: Источник ).

R8 - аргумент клону, который указывает tls (поток локальныйхранилище), но он не используется, если не установлено CLONE_SETTLS: Источник

R9 обычно используется в качестве шестого аргумента системного вызова, но клон не использует его, потому что он использует только 5аргументы ( Source ).

R12 - это регистр, который сохраняется после системного вызова.

Итак, наконец, давайте посмотрим source 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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...