Go语言通过自带的CGO的工具来支持C语言函数调用,也可以用Go语言导出C动态库接口给其它语言使用。
Go的代码执行环境就是goroutine以及Go的runtime,而C的执行环境需要一个不使用分段的栈,并且执行C代码的goroutine需要暂时地脱离调度器的管理,Go与C的相互调用,其需要解决的核心问题都是提供一个C/Go的运行环境来执行相应的代码。要达到这些要求,运行时提供的支持就是切换栈,以及runtime.entersyscall。
cgo通过runtime.entersyscall
,而C函数执行完返回后会调用runtime.exitsyscall。让cgo的运行仿佛是在另一个pthread中执行的,执行完毕后将返回值转换成Go的值。runtime.entersyscall,让cgo产生的外部代码脱离goroutine调度系统
cgo调用时,会切换到m的g0栈,,这样就不必担忧分段栈方面的问题。
1
2
3
4
5
6
7
8
9
| void
runtime·cgocall(void (*fn)(void*), void *arg)
{
runtime·lockOSThread(); //锁定g到m,保证如果在cgo内又回调了Go代码,切换回来时还是在同一个栈中的
runtime·entersyscall(); //进入系统调用
runtime·asmcgocall(fn, arg); //切换到m的g0栈,在g0中执行_cgo_Cfunc_test
runtime·exitsyscall(); //将g从m中解锁
endcgo();
}
|
1
2
3
4
5
6
| //export GoF
func GoF(arg1, arg2 int, arg3 string) int64 {
}
// in c file
extern int64 GoF(int arg1, int arg2, GoString arg3);
|
要使用CGO特性,需要安装C/C++构建工具链,在macOS和Linux下是要安装GCC,在windows下是需要安装MinGW工具。
同时需要保证环境变量CGO_ENABLED
被设置为1,这表示CGO是被启用的状态。
在本地构建时CGO_ENABLED
默认是启用的,当交叉构建时CGO默认是禁止的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| package main
/*
#include <stdio.h>
void printint(int v) {
printf("printint: %d\n", v);
}
*/
// #cgo CFLAGS: -DPNG_DEBUG=1 -I./include
// #cgo LDFLAGS: -L/usr/local/lib -lpng
// #include <png.h>
import "C"
func main() {
v := 42
C.printint(C.int(v))
}
|
第2章 CGO编程 · Go语言高级编程
cgo - 《深入解析Go》 - 书栈网 · BookStack