通过一个例子演示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$

  • 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)的机制
相关推荐
平头哥技术团队35 分钟前
Day 16 | 用 ul、ol、li 一个标签,把散资料收成有序的技能清单页
开发语言·前端·html·html5
统计学小王子44 分钟前
数学建模国赛倒计时 2 天 ——《统计模型精讲(广义线性模型 R 语言实例 + 分析)》
开发语言·数学建模·r语言
此冬歌咏1 小时前
自动化服务器运维监控系统(python+shell)
linux·运维·服务器·开发语言·python·自动化
Cx330❀1 小时前
【Linux网络】深入TCP协议:从滑动窗口到拥塞控制与性能优化全景解析
linux·开发语言·网络·tcp/ip·ai·性能优化·ai编程
“AI国潮设计-小江”1 小时前
【Python实战】SDXL精准控制“普宁英歌舞×星空蛋糕”IP落地,附核心Prompt与商用授权思路
开发语言·人工智能·python·prompt·aigc
k4m7v2pz1 小时前
从 Python 搬到 Rust:pyglet MIDI DAW 变成 egui 鬼畜采样器的迁移复盘
开发语言·python·rust
C++ 老炮儿的技术栈2 小时前
MFC CPtrArray的用法
开发语言·数据结构·c++·算法·mfc·c
佳児素花痴╮2 小时前
C++基础速通
开发语言·c++
是立不是利2 小时前
深入理解JavaScript事件委托
开发语言·前端·javascript
啊阿狸不会拉杆2 小时前
《计算机网络-自顶向下方法》6.4 交换局域网 读书笔记
开发语言·计算机网络·php