我正在尝试创建一个用 Go 编写的静态对象来与 C 程序(比如内核模块或其他东西)交互。
我找到了关于从 Go 调用 C 函数的文档,但我还没有找到太多关于如何走另一条路的信息。我发现这是可能的,但很复杂。
这是我发现的:
这个事情谁有经验?简而言之,我正在尝试创建一个完全用 Go 编写的 PAM 模块。
原文由 beatgammit 发布,翻译遵循 CC BY-SA 4.0 许可协议
我正在尝试创建一个用 Go 编写的静态对象来与 C 程序(比如内核模块或其他东西)交互。
我找到了关于从 Go 调用 C 函数的文档,但我还没有找到太多关于如何走另一条路的信息。我发现这是可能的,但很复杂。
这是我发现的:
这个事情谁有经验?简而言之,我正在尝试创建一个完全用 Go 编写的 PAM 模块。
原文由 beatgammit 发布,翻译遵循 CC BY-SA 4.0 许可协议
如果你使用 gccgo,这不是一个令人困惑的命题。这在这里工作:
package main
func Add(a, b int) int {
return a + b
}
#include <stdio.h>
extern int go_add(int, int) __asm__ ("example.main.Add");
int main() {
int x = go_add(2, 3);
printf("Result: %d\n", x);
}
all: main
main: foo.o bar.c
gcc foo.o bar.c -o main
foo.o: foo.go
gccgo -c foo.go -o foo.o -fgo-prefix=example
clean:
rm -f main *.o
原文由 Alexander 发布,翻译遵循 CC BY-SA 3.0 许可协议
7 回答5.3k 阅读
6 回答6.9k 阅读✓ 已解决
4 回答2.3k 阅读
1 回答2k 阅读✓ 已解决
1 回答3.4k 阅读
1 回答3.3k 阅读
2 回答2.2k 阅读
您可以从 C 调用 Go 代码。不过,这是一个令人困惑的命题。
您链接到的博客文章中概述了该过程。但我可以看出这不是很有帮助。这是一个没有任何不必要位的简短片段。它应该让事情更清楚一点。
调用所有内容的顺序如下:
这里要记住的关键是,回调函数必须在 Go 端标记为
//export
注释,在 C 端标记为extern
。这意味着您希望使用的任何回调都必须在您的包中定义。为了允许您的包的用户提供自定义回调函数,我们使用与上面完全相同的方法,但我们提供用户的自定义处理程序(这只是一个常规的 Go 函数)作为传递到 C 的参数一边为
void*
。然后它被我们包中的回调处理程序接收并调用。让我们使用我目前正在使用的更高级的示例。在本例中,我们有一个执行相当繁重任务的 C 函数:它从 USB 设备读取文件列表。这可能需要一段时间,所以我们希望我们的应用程序能够收到进度通知。我们可以通过传入我们在程序中定义的函数指针来做到这一点。它只是在被调用时向用户显示一些进度信息。由于它有一个众所周知的签名,我们可以为它分配自己的类型:
此处理程序获取一些进度信息(当前收到的文件数和文件总数)以及一个接口{}值,该值可以保存用户需要它保存的任何内容。
现在我们需要编写 C 和 Go 管道以允许我们使用此处理程序。幸运的是,我希望从库中调用的 C 函数允许我们传入类型为
void*
的用户数据结构。这意味着它可以容纳我们想要容纳的任何东西,没有任何问题,我们将按原样将它带回围棋世界。为了使所有这些工作正常,我们不直接从 Go 调用库函数,而是为它创建一个 C 包装器,我们将其命名为goGetFiles()
。正是这个包装器实际上将我们的 Go 回调与用户数据对象一起提供给 C 库。注意
goGetFiles()
函数不接受任何回调函数指针作为参数。相反,我们的用户提供的回调被打包在一个自定义结构中,该结构包含该处理程序和用户自己的用户数据值。我们将其作为用户数据参数传递给goGetFiles()
。这就是我们的 C 绑定。用户的代码现在非常简单:
这一切看起来比实际要复杂得多。与我们之前的示例相比,调用顺序没有改变,但我们在链的末尾有两个额外的调用:
顺序如下: