场景如下

    1. package main
    2. import "fmt"
    3. /*
    4. #define JUMP_PARENT 0x00
    5. #define JUMP_CHILD 0xA0
    6. #define _GNU_SOURCE
    7. #include <stdio.h>
    8. #include <stdlib.h>
    9. #include <unistd.h>
    10. #include <sched.h>
    11. #include <setjmp.h>
    12. char child_stack[4096] __attribute__ ((aligned(16)));
    13. int child_func(void *arg) {
    14. jmp_buf* env = (jmp_buf*)arg;
    15. longjmp(*env, JUMP_CHILD);
    16. }
    17. __attribute__((constructor)) void init(void) {
    18. printf("init...\n");
    19. jmp_buf env;
    20. switch(setjmp(env)) {
    21. case JUMP_PARENT:
    22. printf("JUMP_PARENT\n");
    23. int child_pid = clone(child_func, child_stack, CLONE_PARENT, env);
    24. printf("CHILD_PID: %d\n", child_pid);
    25. exit(0);
    26. case JUMP_CHILD:
    27. printf("JUMP_CHILD\n");
    28. return;
    29. }
    30. }
    31. */
    32. import "C"
    33. func main() {
    34. fmt.Println("main...")
    35. }

    使用Cgo在进入Go runtime之前先执行一段C代码。正常情况下child进程应该打印完JUMP_CHILD后开始执行main函数。
    但是在执行完init函数后,无法回到main函数,child进程会阻塞在一个地方。

    1. [root@localhost cgo-practive]# go build .
    2. [root@localhost cgo-practive]# ls
    3. cgo-practive main.go
    4. [root@localhost cgo-practive]# ./cgo-practive
    5. init...
    6. JUMP_PARENT
    7. CHILD_PID: 14348
    8. [root@localhost cgo-practive]# JUMP_CHILD

    使用GDB查看stack:

    1. (gdb) list
    2. 28 exit(0);
    3. 29 case JUMP_CHILD:
    4. 30 printf("JUMP_CHILD\n");
    5. 31 return;
    6. 32 }
    7. 33 }
    8. 34 */
    9. 35 import "C"
    10. 36
    11. 37 func main() {
    12. (gdb) info stack
    13. #0 0x00007efd6f9684ed in __lll_lock_wait () from /lib64/libpthread.so.0
    14. #1 0x00007efd6f966170 in pthread_cond_broadcast@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
    15. #2 0x00000000004862e6 in x_cgo_notify_runtime_init_done (dummy=<optimized out>) at gcc_libinit.c:69
    16. #3 0x0000000000451070 in runtime.asmcgocall () at /usr/local/go/src/runtime/asm_amd64.s:637
    17. #4 0x00007ffdec4b5c30 in ?? ()
    18. #5 0x000000000044efd1 in runtime.malg.func1 () at /usr/local/go/src/runtime/proc.go:3289
    19. #6 0x000000000044f886 in runtime.systemstack () at /usr/local/go/src/runtime/asm_amd64.s:351
    20. #7 0x000000000042c5b0 in ?? () at /usr/local/go/src/runtime/proc.go:1146
    21. #8 0x000000000044f719 in runtime.rt0_go () at /usr/local/go/src/runtime/asm_amd64.s:201
    22. #9 0x0000000000000000 in ?? ()

    查看Cgo的源码:

    1. // /usr/local/Cellar/go/1.11.5/libexec/src/runtime/cgo/gcc_libinit.c
    2. void
    3. x_cgo_notify_runtime_init_done(void* dummy) {
    4. pthread_mutex_lock(&runtime_init_mu);
    5. runtime_init_done = 1;
    6. pthread_cond_broadcast(&runtime_init_cond); // 阻塞在此处
    7. pthread_mutex_unlock(&runtime_init_mu);
    8. }