从 C 调用 Go 函数
我正在尝试创建一个用 Go 编写的静态对象来与 C 程序(例如,内核模块或其他东西)交互。
我找到了有关从 Go 调用 C 函数的文档,但我还没有找到太多关于如何使用其他方法的文档。我发现这是可能的,但很复杂。
这是我发现的:
有人有这方面的经验吗?简而言之,我正在尝试创建一个完全用 Go 编写的 PAM 模块。
I am trying to create a static object written in Go to interface with a C program (say, a kernel module or something).
I have found documentation on calling C functions from Go, but I haven't found much on how to go the other way. What I've found is that it's possible, but complicated.
Here is what I found:
Blog post about callbacks between C and Go
Does anyone have experience with this? In short, I'm trying to create a PAM module written entirely in Go.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
您可以从 C 调用 Go 代码。不过,这是一个令人困惑的命题。
您链接到的博客文章中概述了该过程。但我可以看出这并没有多大帮助。这是一个简短的片段,没有任何不必要的部分。它应该让事情变得更清楚一些。
所有内容的调用顺序如下:
这里要记住的关键是,回调函数必须在 Go 端使用
//export
注释进行标记,并且标记为extern
在 C 侧。这意味着您希望使用的任何回调都必须在包内定义。为了允许您的包的用户提供自定义回调函数,我们使用与上面完全相同的方法,但我们提供用户的自定义处理程序(这只是一个常规的 Go 函数)作为传递到 C 的参数侧面为
void*
。然后它被我们包中的回调处理程序接收并调用。让我们使用我目前正在使用的一个更高级的示例。在本例中,我们有一个 C 函数来执行一项相当繁重的任务:它从 USB 设备读取文件列表。这可能需要一段时间,因此我们希望我们的应用程序能够收到进度通知。我们可以通过传入我们在程序中定义的函数指针来做到这一点。它只是在被调用时向用户显示一些进度信息。由于它具有众所周知的签名,因此我们可以为其分配自己的类型:
该处理程序获取一些进度信息(当前接收的文件数和文件总数)以及一个 interface{} 值,该值可以保存用户需要的任何内容抓住。
现在我们需要编写 C 和 Go 管道来允许我们使用这个处理程序。幸运的是,我希望从库中调用的 C 函数允许我们传入
void*
类型的用户数据结构。这意味着它可以容纳我们想要它容纳的任何内容,无需提出任何问题,我们将按原样将其带回 Go 世界。为了使这一切顺利进行,我们不直接从 Go 调用库函数,而是为其创建一个 C 包装器,将其命名为goGetFiles()
。正是这个包装器实际上向 C 库提供了我们的 Go 回调以及用户数据对象。请注意,
goGetFiles()
函数不将任何回调函数指针作为参数。相反,我们的用户提供的回调被打包在一个自定义结构中,该结构保存该处理程序和用户自己的用户数据值。我们将其作为 userdata 参数传递给goGetFiles()
。这就是我们的 C 绑定。用户的代码现在非常简单:
这一切看起来比实际复杂得多。与之前的示例相比,调用顺序没有改变,但我们在链的末尾得到了两个额外的调用:
顺序如下:
You can call the Go code from C. It is a confusing proposition, though.
The process is outlined in the blog post you linked to. But I can see how that isn't very helpful. Here is a short snippet without any unnecessary bits. It should make things a little clearer.
The order in which everything is called is as follows:
The key to remember here is that a callback function must be marked with the
//export
comment on the Go side and asextern
on the C side. This means that any callback you wish to use, must be defined inside your package.In order to allow a user of your package to supply a custom callback function, we use the exact same approach as above, but we supply the user's custom handler (which is just a regular Go function) as a parameter that is passed onto the C side as
void*
. It is then received by the callbackhandler in our package and called.Let's use a more advanced example I am currently working with. In this case, we have a C function that performs a pretty heavy task: It reads a list of files from a USB device. This can take a while, so we want our app to be notified of its progress. We can do this by passing in a function pointer that we defined in our program. It simply displays some progress info to the user whenever it gets called. Since it has a well known signature, we can assign it its own type:
This handler takes some progress info (current number of files received and total number of files) along with an interface{} value which can hold anything the user needs it to hold.
Now we need to write the C and Go plumbing to allow us to use this handler. Luckily the C function I wish to call from the library allows us to pass in a userdata struct of type
void*
. This means it can hold whatever we want it to hold, no questions asked and we will get it back into the Go world as-is. To make all this work, we do not call the library function from Go directly, but we create a C wrapper for it which we will namegoGetFiles()
. It is this wrapper that actually supplies our Go callback to the C library, along with a userdata object.Note that the
goGetFiles()
function does not take any function pointers for callbacks as parameters. Instead, the callback that our user has supplied is packed in a custom struct that holds both that handler and the user's own userdata value. We pass this intogoGetFiles()
as the userdata parameter.That's it for our C bindings. The user's code is now very straight forward:
This all looks a lot more complicated than it is. The call order has not changed as opposed to our previous example, but we get two extra calls at the end of the chain:
The order is as follows:
如果您使用 gccgo,这并不是一个令人困惑的命题。这在这里工作:
foo.go
bar.c
Makefile
It is not a confusing proposition if you use gccgo. This works here:
foo.go
bar.c
Makefile
随着 Go 1.5 的发布,答案发生了变化
我前段时间提出的这个问题根据 1.5 添加的功能再次解决了这个问题
在现有 C 项目中使用 Go 代码
The answer has changed with the release of Go 1.5
This SO question that I asked some time ago addresses the issue again in light of the 1.5 added capabilities
Using Go code in an existing C project
就我而言,这是不可能的:
来源:https://github.com/golang/go/wiki/cgo
As far as I am concerned it isn't possible:
source: https://github.com/golang/go/wiki/cgo