Go语言之cgo

Go语言之cgo

简介

Go语言通过自带的CGO的工具来支持C语言函数调用,也可以用Go语言导出C动态库接口给其它语言使用。

原理

Go的代码执行环境就是goroutine以及Go的runtime,而C的执行环境需要一个不使用分段的栈,并且执行C代码的goroutine需要暂时地脱离调度器的管理,Go与C的相互调用,其需要解决的核心问题都是提供一个C/Go的运行环境来执行相应的代码。要达到这些要求,运行时提供的支持就是切换栈,以及runtime.entersyscall。

Go调用C

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();
}

C调用Go

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默认是禁止的。

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))
}

参考

  1. 第2章 CGO编程 · Go语言高级编程

  2. cgo - 《深入解析Go》 - 书栈网 · BookStack

updatedupdated2024-05-102024-05-10