本例提供了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$
- 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$
- 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$
编译和运行
- 首先编译出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$
- 运行golang程序,
Captured:
后面就是获取的C函数打印的显示内容
admin@hpc-1:~/go/my_stdout$ go run main.go
Captured: Hello from C!
admin@hpc-1:~/go/my_stdout$
一些说明
- 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)的机制