当前位置:   article > 正文

通过一个例子演示golang调用C语言动态链接库中的函数

通过一个例子演示golang调用C语言动态链接库中的函数

本例提供了cgo调用C函数的示例,也演示了如何将C函数打印内容保存到golang的变量中

目录和源码

  • 目录结构
admin@hpc-1:~/go/my_stdout$ tree
.
├── include
│   ├── mylibrary.c
│   └── mylibrary.h
├── lib
└── main.go

2 directories, 3 files
admin@hpc-1:~/go/my_stdout$ 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • include目录下放C的源码和头文件
admin@hpc-1:~/go/my_stdout$ cat include/mylibrary.c
#include <stdio.h>

void writeToStdout() {
    printf("Hello from C!\n");
}
admin@hpc-1:~/go/my_stdout$ 
admin@hpc-1:~/go/my_stdout$ cat include/mylibrary.h
#ifndef MYLIBRARY_H
#define MYLIBRARY_H

void writeToStdout();

#endif
admin@hpc-1:~/go/my_stdout$ 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • golang的源码
admin@hpc-1:~/go/my_stdout$ cat main.go 
package main

/*
#cgo CFLAGS: -I./include
#cgo LDFLAGS: -L${SRCDIR}/lib -lmyprint -Wl,-rpath=${SRCDIR}/lib 
#include "mylibrary.h"
#include <stdio.h>
*/
import "C"

import (
    "syscall"
    "bytes"
    "log"
    "fmt"
    "io"
    "os"
)

func main() {
    // 克隆 Stdout 到 origStdout.
    origStdout, err := syscall.Dup(syscall.Stdout)
    if err != nil {
        log.Fatal(err)
    }

    // 创建管道
    reader, writer, err := os.Pipe()
    if err != nil {
        log.Fatal(err)
    }

    // 此后stdout将会写到writer
    if err = syscall.Dup2(int(writer.Fd()), syscall.Stdout); err != nil {
        log.Fatal(err)
    }

    // 启动背景 goroutine 收集输出
    out := make(chan []byte)
    go func() {
        var b bytes.Buffer
        io.Copy(&b, reader)
        out <- b.Bytes()
    }()

    // 调用C函数打印
    C.writeToStdout()

    // 一些清理工作
    C.fflush(nil)
    writer.Close()
    syscall.Close(syscall.Stdout)

    // 导出output
    record := <-out

    // Restore original Stdout.
    syscall.Dup2(origStdout, syscall.Stdout)
    syscall.Close(origStdout)

    fmt.Println("Captured:", string(record))
}
admin@hpc-1:~/go/my_stdout$ 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64

编译和运行

  • 首先编译出C的lib,放到./lib目录下
admin@hpc-1:~/go/my_stdout$ gcc -shared -o ./lib/libmyprint.so include/mylibrary.c
admin@hpc-1:~/go/my_stdout$ 
admin@hpc-1:~/go/my_stdout$ ls -lt ./lib/
total 16
-rwxrwxr-x 1 centec centec 16208 2-р сар  27 07:29 libmyprint.so
admin@hpc-1:~/go/my_stdout$ 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 运行golang程序,Captured:后面就是获取的C函数打印的显示内容
admin@hpc-1:~/go/my_stdout$ go run main.go 
Captured: Hello from C!

admin@hpc-1:~/go/my_stdout$ 
  • 1
  • 2
  • 3
  • 4

一些说明

  • Go 语言提供了 cgo 工具,用于在 Go 代码中调用 C 代码或让 C 代码调用 Go 代码。cgo 允许在 Go 代码中使用 C 函数、类型和变量,并提供了一种在两种语言之间进行交互的机制。
    • import "C"上面紧挨的被注释的部分,就是和C有关的信息
    • #cgo CFLAGS: -I./include指定C源码和头文件所在目录
    • -L${SRCDIR}/lib指定C编程的lib文件所在目录
    • -lmyprint其中-l的后面,是lib文件名(‘libmyprint.so’)去掉开头的’lib’后最后的’.so’之后的部分
    • -rpath=${SRCDIR}/lib指定了程序运行时候,到哪里去找lib文件,也就是说运行该程序的地方,一定要有用到的.so文件
    • 接下来的两个#include就是标准的C语言预处理指令
  • syscall.Dup2 函数用于复制文件描述符(file descriptor)到指定的目标文件描述符。它使得目标文件描述符成为源文件描述符的副本,两个文件描述符指向同一个底层文件或资源
  • os.Pipe() 函数用于创建一个管道(Pipe),它提供了在同一程序内部的两个不同 goroutine 之间进行进程间通信(IPC)的机制
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/164115
推荐阅读
相关标签
  

闽ICP备14008679号