场景如下
package main
import "fmt"
/*
#define JUMP_PARENT 0x00
#define JUMP_CHILD 0xA0
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sched.h>
#include <setjmp.h>
char child_stack[4096] __attribute__ ((aligned(16)));
int child_func(void *arg) {
jmp_buf* env = (jmp_buf*)arg;
longjmp(*env, JUMP_CHILD);
}
__attribute__((constructor)) void init(void) {
printf("init...\n");
jmp_buf env;
switch(setjmp(env)) {
case JUMP_PARENT:
printf("JUMP_PARENT\n");
int child_pid = clone(child_func, child_stack, CLONE_PARENT, env);
printf("CHILD_PID: %d\n", child_pid);
exit(0);
case JUMP_CHILD:
printf("JUMP_CHILD\n");
return;
}
}
*/
import "C"
func main() {
fmt.Println("main...")
}
使用Cgo在进入Go runtime之前先执行一段C代码。正常情况下child进程应该打印完JUMP_CHILD后开始执行main函数。
但是在执行完init函数后,无法回到main函数,child进程会阻塞在一个地方。
[root@localhost cgo-practive]# go build .
[root@localhost cgo-practive]# ls
cgo-practive main.go
[root@localhost cgo-practive]# ./cgo-practive
init...
JUMP_PARENT
CHILD_PID: 14348
[root@localhost cgo-practive]# JUMP_CHILD
使用GDB查看stack:
(gdb) list
28 exit(0);
29 case JUMP_CHILD:
30 printf("JUMP_CHILD\n");
31 return;
32 }
33 }
34 */
35 import "C"
36
37 func main() {
(gdb) info stack
#0 0x00007efd6f9684ed in __lll_lock_wait () from /lib64/libpthread.so.0
#1 0x00007efd6f966170 in pthread_cond_broadcast@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
#2 0x00000000004862e6 in x_cgo_notify_runtime_init_done (dummy=<optimized out>) at gcc_libinit.c:69
#3 0x0000000000451070 in runtime.asmcgocall () at /usr/local/go/src/runtime/asm_amd64.s:637
#4 0x00007ffdec4b5c30 in ?? ()
#5 0x000000000044efd1 in runtime.malg.func1 () at /usr/local/go/src/runtime/proc.go:3289
#6 0x000000000044f886 in runtime.systemstack () at /usr/local/go/src/runtime/asm_amd64.s:351
#7 0x000000000042c5b0 in ?? () at /usr/local/go/src/runtime/proc.go:1146
#8 0x000000000044f719 in runtime.rt0_go () at /usr/local/go/src/runtime/asm_amd64.s:201
#9 0x0000000000000000 in ?? ()
查看Cgo的源码:
// /usr/local/Cellar/go/1.11.5/libexec/src/runtime/cgo/gcc_libinit.c
void
x_cgo_notify_runtime_init_done(void* dummy) {
pthread_mutex_lock(&runtime_init_mu);
runtime_init_done = 1;
pthread_cond_broadcast(&runtime_init_cond); // 阻塞在此处
pthread_mutex_unlock(&runtime_init_mu);
}