英文:
GoLang CGO file handles
问题
我正在使用一个原生的Linux C二进制文件,其中有一个相当昂贵的初始化调用,我希望在应用程序启动时执行一次。这个调用应该在内部打开一些文件句柄以供以后使用。当我从Go中调用这个昂贵的初始化C函数时,它成功完成并正确地打开了文件,但这些句柄仅在调用C函数的过程中打开!这意味着当我从Go中对同一个库调用连续的C函数时,文件句柄不再打开,调用失败。我使用lsof命令进行了验证。有趣的是,当初始化调用以及对后续行为的调用组合成一个单独的C函数,然后从Go中调用该函数时,文件被打开并保持打开状态,从而成功完成所有所需的功能。
是否存在一些未记录的cgo行为,可以在从Go中多次调用C函数之间“清理”、关闭或甚至泄漏文件句柄或其他有状态的资源?如果是这样,这种行为是否可配置?我们无法访问此库的源代码。
另外,我已经验证了这与线程本地存储无关。调用runtime.LockOSThread()
没有效果,我们已经验证了在控制从C返回到调用的Go代码后,文件已关闭。
以下是我想要编写的Go代码示例:
// Go代码:
func main() {
C.Initialize()
C.do_stuff() // 内部状态已经被清理!因此,此调用失败。:(
}
以下是一个同时调用初始化和行为的C函数示例。这个“包装”函数从Go中调用:
// C代码:
void DoEverything(void)
{
Initialize();
do_stuff(); // 成功,因为所有内部状态都完好无损(未被清理)。
}
英文:
I’m working with a native linux C binary which has a fairly expensive initialization call which I would like to perform once at application startup. This call should open a bunch of file handles internally for later use. When I call this expensive initialization C function from Go, it completes successfully and correctly opens the files but those handles are open only for the duration of the call to the C function! This means that when I call successive C functions against the same library from Go, the file handles are no longer open and the calls fail. I have verified this using the lsof command. Interestingly, when the initialization call as well as calls to subsequent behavior are composed into a single C function which is then called from Go, the files are opened and remain open, allowing successful completion of all desired functionality.
Is there some kind of undocumented cgo behavior which is “cleaning up”, shutting down, or even leaking file handles or other stateful resources between multiple invocations of C functions from Go? If so, is this behavior configurable? We don’t have access to the source code for this library.
Also, I've verified that this is not related to thread-local storage. Calling runtime.LockOSThread()
has no effect and we've verified that the files are closed after control returns from C back to the calling Go code.
Here’s an example of the kind of Go code I’d like to write:
// Go code:
func main() {
C.Initialize()
C.do_stuff() // internal state is already cleaned up! This call fails as a result. :(
}
Here’s an example of a C function that invokes the initialization and behavior all at once. This “wrapping” function is invoked from Go:
// C code:
void DoEverything(void)
{
Initialize();
do_stuff(); // succeeds because all internal state is intact (not cleaned up).
}
答案1
得分: 0
好的,以下是翻译好的内容:
好的,这有点尴尬,但我找到问题所在了。在调用initialize()之后,我调用了defer close(),但实际上应该是defer fmt.Println(close())。因为延迟函数的参数会立即解析(而不是延迟解析),所以close函数在我们调用其他行为之前就被调用了。Golang博客清楚地解释了延迟函数调用的参数解析。
英文:
Ok, this is a bit embarrassing, but I figured it out. Right after calling initialize(), I was calling defer close(), but it was actually defer fmt.Println(close()). Because arguments to deferred functions are resolved immediately (not deferred), the close function was being invoked before we could invoke any other behavior. The golang blog clearly explains argument resolution to deferred function calls.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论