Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Go runtime/cgo 包详解

概述

runtime/cgo 包为 cgo 工具生成的代码提供运行时支持。它主要用于在 Go 和 C 之间安全地传递包含 Go 指针的值,而不违反 cgo 指针传递规则。

重要说明

  • 此包主要用于 cgo 生成的代码
  • 提供了 Handle 机制来安全传递 Go 值给 C
  • 解决了 C 代码需要引用 Go 值的场景
  • 大多数 Go 程序员不需要直接使用此包

cgo 指针传递规则

  • Go 代码可以传递不包含 Go 指针的值给 C
  • 包含 Go 指针的值不能直接传递给 C
  • Handle 提供了一种安全的方式来绕过这个限制

包导入

import "runtime/cgo"

基本使用

示例 1:使用 Handle 传递 Go 字符串给 C

package main

/*
#include <stdint.h>

extern void MyGoPrint(uintptr_t handle);
void myprint(uintptr_t handle);
*/
import "C"
import "runtime/cgo"

//export MyGoPrint
func MyGoPrint(handle C.uintptr_t) {
    h := cgo.Handle(handle)
    val := h.Value().(string)
    println(val)
    h.Delete()
}

func main() {
    val := "hello Go"
    C.myprint(C.uintptr_t(cgo.NewHandle(val)))
}

C 代码部分

#include <stdint.h>

// Go 函数声明
extern void MyGoPrint(uintptr_t handle);

// C 函数实现
void myprint(uintptr_t handle) {
    MyGoPrint(handle);
}

运行结果

hello Go

示例 2:使用 Handle 传递任意 Go 值

package main

/*
#include <stdint.h>

extern void ProcessData(uintptr_t handle);
*/
import "C"
import (
    "fmt"
    "runtime/cgo"
)

type Data struct {
    Name string
    Age  int
}

//export ProcessData
func ProcessData(handle C.uintptr_t) {
    h := cgo.Handle(handle)
    data := h.Value().(*Data)
    fmt.Printf("Name: %s, Age: %d\n", data.Name, data.Age)
    h.Delete()
}

func main() {
    data := &Data{Name: "Alice", Age: 30}
    C.ProcessData(C.uintptr_t(cgo.NewHandle(data)))
}

运行结果

Name: Alice, Age: 30

类型详解

Handle

Handle 提供了一种方式来在 Go 和 C 之间传递包含 Go 指针的值,而不违反 cgo 指针传递规则。

type Handle uintptr

特性

  • Handle 是一个整数值,可以表示任何 Go 值
  • Handle 可以传递给 C,然后再传回 Go
  • Go 代码可以使用 Handle 检索原始 Go 值
  • Handle 的底层类型保证能容纳任何指针的位模式
  • Handle 的零值无效,可用作 C API 中的哨兵值

重要说明

  • Handle 使用资源,程序必须在不需时显式调用 Delete
  • 假设 C 代码可能会持有 handle,因此必须显式删除
  • 无效的 Handle 会导致 Value() 和 Delete() panic

NewHandle

func NewHandle(v interface{}) Handle

说明:为给定值返回一个 handle。该 handle 在程序调用 Delete 之前一直有效。

使用示例

package main

import (
    "fmt"
    "runtime/cgo"
)

func main() {
    // 创建 handle
    val := "test value"
    h := cgo.NewHandle(val)
    
    fmt.Printf("Handle created: %v\n", h)
    
    // 获取值
    retrieved := h.Value()
    fmt.Printf("Retrieved: %v\n", retrieved)
    
    // 删除 handle
    h.Delete()
    fmt.Println("Handle deleted")
}

运行结果

Handle created: 1
Retrieved: test value
Handle deleted

典型用法

package main

/*
#include <stdint.h>

extern void Callback(uintptr_t handle);
void doWork(uintptr_t handle);
*/
import "C"
import (
    "fmt"
    "runtime/cgo"
    "unsafe"
)

//export Callback
func Callback(handle C.uintptr_t) {
    h := cgo.Handle(handle)
    data := h.Value().(map[string]int)
    fmt.Printf("Callback received: %v\n", data)
    h.Delete()
}

func doWork(data map[string]int) {
    h := cgo.NewHandle(data)
    defer h.Delete()
    
    // 传递给 C 代码
    C.doWork(C.uintptr_t(h))
}

func main() {
    data := map[string]int{"a": 1, "b": 2}
    doWork(data)
}

Value

func (h Handle) Value() interface{}

说明:返回有效 handle 关联的 Go 值。如果 handle 无效会 panic。

使用示例

package main

import (
    "fmt"
    "runtime/cgo"
)

func main() {
    // 创建不同类型的 handle
    handles := []cgo.Handle{
        cgo.NewHandle("string"),
        cgo.NewHandle(42),
        cgo.NewHandle([]int{1, 2, 3}),
        cgo.NewHandle(map[string]int{"a": 1}),
    }
    
    for _, h := range handles {
        val := h.Value()
        fmt.Printf("Type: %T, Value: %v\n", val, val)
        h.Delete()
    }
}

运行结果

Type: string, Value: string
Type: int, Value: 42
Type: []int, Value: [1 2 3]
Type: map[string]int, Value: map[a:1]

错误处理

package main

import (
    "fmt"
    "runtime/cgo"
)

func safeValue(h cgo.Handle) (interface{}, error) {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered from panic:", r)
        }
    }()
    
    // 删除后的 handle 会 panic
    return h.Value(), nil
}

func main() {
    h := cgo.NewHandle("test")
    h.Delete()
    
    _, err := safeValue(h)
    if err != nil {
        fmt.Println("Error:", err)
    }
}

运行结果

Recovered from panic: invalid handle

Delete

func (h Handle) Delete()

说明:使 handle 无效。应该在程序不再需要将 handle 传递给 C 且 C 代码不再持有 handle 值时调用。如果 handle 无效会 panic。

使用示例

package main

import (
    "fmt"
    "runtime/cgo"
)

func main() {
    h := cgo.NewHandle("test")
    
    // 使用 handle
    val := h.Value()
    fmt.Println("Value:", val)
    
    // 删除 handle
    h.Delete()
    fmt.Println("Handle deleted")
    
    // 再次删除会 panic
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Panic:", r)
        }
    }()
    
    h.Delete() // panic
}

运行结果

Value: test
Handle deleted
Panic: invalid handle

资源管理最佳实践

package main

/*
#include <stdint.h>

extern void Process(uintptr_t handle);
*/
import "C"
import (
    "runtime/cgo"
)

//export Process
func Process(handle C.uintptr_t) {
    h := cgo.Handle(handle)
    data := h.Value().([]byte)
    // 处理数据...
    _ = data
    h.Delete()
}

func processData(data []byte) {
    h := cgo.NewHandle(data)
    defer h.Delete() // 确保删除
    
    C.Process(C.uintptr_t(h))
}

func main() {
    data := []byte("hello")
    processData(data)
}

Incomplete

type Incomplete struct{}

说明Incomplete 专门用于不完整 C 类型的语义。

使用示例

package main

/*
struct IncompleteType;  // 不完整的 C 类型
*/
import "C"
import "runtime/cgo"

// Incomplete 用于表示不完整的 C 结构体类型
var _ cgo.Incomplete

func main() {
    // 通常不直接使用
    // 主要用于 cgo 生成的代码中
}

典型示例

示例 1:C 回调中使用 Handle

package main

/*
#include <stdint.h>

typedef void (*CallbackFunc)(uintptr_t handle, int result);

extern void GoCallback(uintptr_t handle, int result);

static inline void registerCallback(CallbackFunc cb, uintptr_t handle) {
    cb(handle, 42);
}
*/
import "C"
import (
    "fmt"
    "runtime/cgo"
)

//export GoCallback
func GoCallback(handle C.uintptr_t, result C.int) {
    h := cgo.Handle(handle)
    callback := h.Value().(func(int))
    callback(int(result))
    h.Delete()
}

func registerCallback(callback func(int)) {
    h := cgo.NewHandle(callback)
    C.registerCallback(C.CallbackFunc(C.GoCallback), C.uintptr_t(h))
}

func main() {
    registerCallback(func(result int) {
        fmt.Printf("Callback called with result: %d\n", result)
    })
}

运行结果

Callback called with result: 42

示例 2:传递结构体指针

package main

/*
#include <stdint.h>

extern void ProcessStruct(uintptr_t handle);
*/
import "C"
import (
    "fmt"
    "runtime/cgo"
)

type Config struct {
    Name    string
    Timeout int
    Debug   bool
}

//export ProcessStruct
func ProcessStruct(handle C.uintptr_t) {
    h := cgo.Handle(handle)
    config := h.Value().(*Config)
    
    fmt.Printf("Config: %+v\n", config)
    fmt.Printf("Name: %s, Timeout: %d, Debug: %v\n", 
        config.Name, config.Timeout, config.Debug)
    
    h.Delete()
}

func main() {
    config := &Config{
        Name:    "myapp",
        Timeout: 30,
        Debug:   true,
    }
    
    C.ProcessStruct(C.uintptr_t(cgo.NewHandle(config)))
}

运行结果

Config: &{myapp 30 true}
Name: myapp, Timeout: 30, Debug: true

示例 3:传递切片

package main

/*
#include <stdint.h>

extern void ProcessSlice(uintptr_t handle);
*/
import "C"
import (
    "fmt"
    "runtime/cgo"
)

//export ProcessSlice
func ProcessSlice(handle C.uintptr_t) {
    h := cgo.Handle(handle)
    data := h.Value().([]int)
    
    sum := 0
    for _, v := range data {
        sum += v
    }
    
    fmt.Printf("Sum: %d\n", sum)
    h.Delete()
}

func main() {
    data := []int{1, 2, 3, 4, 5}
    C.ProcessSlice(C.uintptr_t(cgo.NewHandle(data)))
}

运行结果

Sum: 15

示例 4:传递通道

package main

/*
#include <stdint.h>

extern void SendToChannel(uintptr_t handle, int value);
*/
import "C"
import (
    "fmt"
    "runtime/cgo"
)

//export SendToChannel
func SendToChannel(handle C.uintptr_t, value C.int) {
    h := cgo.Handle(handle)
    ch := h.Value().(chan int)
    ch <- int(value)
    h.Delete()
}

func main() {
    ch := make(chan int)
    
    go func() {
        C.SendToChannel(C.uintptr_t(cgo.NewHandle(ch)), 100)
    }()
    
    result := <-ch
    fmt.Printf("Received: %d\n", result)
}

运行结果

Received: 100

示例 5:传递函数

package main

/*
#include <stdint.h>

extern void ExecuteCallback(uintptr_t handle);
*/
import "C"
import (
    "fmt"
    "runtime/cgo"
)

//export ExecuteCallback
func ExecuteCallback(handle C.uintptr_t) {
    h := cgo.Handle(handle)
    fn := h.Value().(func() string)
    result := fn()
    fmt.Println("Function result:", result)
    h.Delete()
}

func main() {
    callback := func() string {
        return "Hello from Go function!"
    }
    
    C.ExecuteCallback(C.uintptr_t(cgo.NewHandle(callback)))
}

运行结果

Function result: Hello from Go function!

示例 6:多个 Handle 管理

package main

/*
#include <stdint.h>

extern void ProcessMultiple(uintptr_t h1, uintptr_t h2);
*/
import "C"
import (
    "fmt"
    "runtime/cgo"
)

//export ProcessMultiple
func ProcessMultiple(h1, h2 C.uintptr_t) {
    handle1 := cgo.Handle(h1)
    handle2 := cgo.Handle(h2)
    
    str := handle1.Value().(string)
    num := handle2.Value().(int)
    
    fmt.Printf("String: %s, Number: %d\n", str, num)
    
    handle1.Delete()
    handle2.Delete()
}

func main() {
    h1 := cgo.NewHandle("test")
    h2 := cgo.NewHandle(42)
    
    C.ProcessMultiple(C.uintptr_t(h1), C.uintptr_t(h2))
}

运行结果

String: test, Number: 42

示例 7:Handle 与 unsafe.Pointer 配合

package main

/*
extern void ProcessContext(void *context);
*/
import "C"
import (
    "fmt"
    "runtime/cgo"
    "unsafe"
)

//export ProcessContext
func ProcessContext(context unsafe.Pointer) {
    h := *(*cgo.Handle)(context)
    data := h.Value().(map[string]string)
    
    for k, v := range data {
        fmt.Printf("%s: %s\n", k, v)
    }
    
    h.Delete()
}

func main() {
    data := map[string]string{
        "name":  "Alice",
        "email": "alice@example.com",
    }
    
    h := cgo.NewHandle(data)
    C.ProcessContext(unsafe.Pointer(&h))
}

运行结果

name: Alice
email: alice@example.com

示例 8:错误处理和验证

package main

import (
    "fmt"
    "runtime/cgo"
)

type SafeHandle struct {
    handle cgo.Handle
    valid  bool
}

func NewSafeHandle(v interface{}) *SafeHandle {
    return &SafeHandle{
        handle: cgo.NewHandle(v),
        valid:  true,
    }
}

func (sh *SafeHandle) Value() (interface{}, error) {
    if !sh.valid {
        return nil, fmt.Errorf("handle is invalid")
    }
    return sh.handle.Value(), nil
}

func (sh *SafeHandle) Delete() {
    if sh.valid {
        sh.handle.Delete()
        sh.valid = false
    }
}

func main() {
    sh := NewSafeHandle("test value")
    
    val, err := sh.Value()
    if err != nil {
        fmt.Println("Error:", err)
    } else {
        fmt.Println("Value:", val)
    }
    
    sh.Delete()
    fmt.Println("Handle deleted")
    
    // 再次获取值会返回错误
    _, err = sh.Value()
    if err != nil {
        fmt.Println("Error:", err)
    }
}

运行结果

Value: test value
Handle deleted
Error: handle is invalid

最佳实践

1. 始终删除 Handle

// ✅ 推荐:使用 defer
func process(data interface{}) {
    h := cgo.NewHandle(data)
    defer h.Delete()
    
    C.someFunction(C.uintptr_t(h))
}

// ❌ 不推荐:可能忘记删除
func process(data interface{}) {
    h := cgo.NewHandle(data)
    C.someFunction(C.uintptr_t(h))
    // 忘记删除会导致资源泄漏
}

2. 避免重复删除

// ✅ 推荐:确保只删除一次
func process(data interface{}) {
    h := cgo.NewHandle(data)
    defer h.Delete()
    
    C.someFunction(C.uintptr_t(h))
}

// ❌ 不推荐:可能删除多次
func process(data interface{}) {
    h := cgo.NewHandle(data)
    h.Delete()
    // ... 可能再次删除
}

3. 验证 Handle 有效性

// ✅ 推荐:添加验证
func safeValue(h cgo.Handle) (interface{}, error) {
    defer func() {
        if r := recover(); r != nil {
            // 处理 panic
        }
    }()
    
    return h.Value(), nil
}

4. 不要在 C 代码中保留 Handle 副本

// ✅ 推荐:C 代码不保留副本
/*
void process(uintptr_t handle) {
    use(handle);  // 立即使用
}
*/

// ❌ 不推荐:C 代码保留副本
/*
uintptr_t global_handle;  // 危险!

void process(uintptr_t handle) {
    global_handle = handle;  // 保留副本
}
*/

与其他包配合

runtime.Pinner

package main

/*
#include <stdint.h>

extern void Process(uintptr_t handle);
*/
import "C"
import (
    "runtime"
    "runtime/cgo"
)

//export Process
func Process(handle C.uintptr_t) {
    h := cgo.Handle(handle)
    data := h.Value().(*[]byte)
    // 使用数据...
    _ = data
}

func main() {
    data := make([]byte, 100)
    
    // 固定内存
    var pinner runtime.Pinner
    pinner.Pin(&data)
    
    h := cgo.NewHandle(&data)
    defer h.Delete()
    defer pinner.Unpin()
    
    C.Process(C.uintptr_t(h))
}

unsafe 包

package main

/*
extern void Process(void *ptr);
*/
import "C"
import (
    "runtime/cgo"
    "unsafe"
)

//export Process
func Process(ptr unsafe.Pointer) {
    h := *(*cgo.Handle)(ptr)
    val := h.Value().(string)
    println(val)
    h.Delete()
}

func main() {
    val := "test"
    h := cgo.NewHandle(val)
    C.Process(unsafe.Pointer(&h))
}

快速参考

类型

类型说明
Handle用于在 Go 和 C 之间安全传递 Go 值
Incomplete用于不完整 C 类型的语义

Handle 方法

方法参数返回值说明
NewHandlev interface{}Handle创建 handle
Value-interface{}获取关联的 Go 值
Delete--删除 handle

注意事项

1. Handle 资源管理

  • Handle 使用资源,必须显式删除
  • 假设 C 代码可能持有 handle,因此必须显式删除
  • 未删除的 handle 会导致资源泄漏

2. Handle 有效性

  • Handle 的零值无效
  • 删除后的 handle 无效
  • 对无效 handle 调用 Value() 或 Delete() 会 panic

3. C 代码限制

  • C 代码不应保留 handle 的副本
  • 除非内存被显式固定(使用 runtime.Pinner)
  • C 代码必须在使用后立即将 handle 传回 Go

4. 类型安全

  • Handle 可以持有任意 Go 值
  • 检索时需要类型断言
  • 错误的类型断言会 panic

5. 性能考虑

  • Handle 操作有少量开销
  • 频繁创建和删除 handle 可能影响性能
  • 尽可能复用 handle

6. 并发安全

  • Handle 本身不是并发安全的
  • 多个 goroutine 不应同时操作同一个 handle
  • 每个 goroutine 应使用自己的 handle

总结

runtime/cgo 包提供了在 Go 和 C 之间安全传递 Go 值的机制。

核心要点

  1. Handle 用于安全传递包含 Go 指针的值给 C
  2. 必须显式调用 Delete() 删除 handle
  3. Handle 可以传递任意 Go 值(字符串、切片、映射、通道、函数等)
  4. C 代码不应保留 handle 副本
  5. 无效的 handle 会导致 panic

主要用途

  • cgo 生成的代码
  • 需要在 Go 和 C 之间传递复杂 Go 值的场景
  • C 回调需要访问 Go 数据的场景
  • 实现 Go 和 C 之间的双向通信

重要提醒

  • 这是低级包,大多数 Go 程序员不需要直接使用
  • 使用 cgo 命令生成的代码会自动处理 handle
  • 手动使用时必须小心管理资源