[英]What's the related syscall for GUI programing on 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似乎是不必要的。
请帮我。
根据clone的联机帮助页,仅在设置CLONE_PARENT_SETTID
, CLONE_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与此手册页( 参考 )中的ptid
和ctid
相对应。
实际上,从os_linux.go: Source调用runtime.clone()时未设置此标志。
他们不需要tid的原因可能是因为它不是pthread之类的库,用户可以使用tid完成一些复杂的事情。
简而言之,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
线程中调用的某些函数的指针。 ( m
和g
结构如下: 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.