英文:
C code and goroutine scheduling
问题
当我从goroutine中调用C代码时,是否会以任何方式影响其他goroutine的调度?我知道如果我在Erlang中调用NIF,它会阻塞其他(Erlang)进程,直到函数返回。在Golang中是否也是这种情况?C代码是否会阻塞goroutine的调度?
英文:
When I invoke a C code from a goroutine, does it affect scheduling of other goroutines in any way? I know that if I call a NIF in Erlang, it blocks the other (Erlang) processes, until the function returns. Is this the case in Golang? Does C code block the goroutines scheduler?
答案1
得分: 15
从Go代码中调用C函数并不会阻止其他goroutine的运行。
这确实会对调度器产生影响。运行C函数的goroutine不一定会计入GOMAXPROCS限制。它会一开始计入GOMAXPROCS,但如果C函数在sysmon后台goroutine运行时已经阻塞超过20微秒,那么调度器将被允许启动另一个goroutine,如果有一个准备好运行的话。这些细节取决于具体的Go版本,并且可能会发生变化。
英文:
Calling a C function from Go code does not prevent other goroutines from running.
It does have an effect on the scheduler. A goroutine running a C function does not necessarily count against the GOMAXPROCS limit. It will start out counting against GOMAXPROCS, but if the C function has blocked for more than 20us at the time the sysmon background goroutine runs, then the scheduler will be permitted to start another goroutine if there is one ready to run. These details depend on the specific Go version and are subject to change.
答案2
得分: 11
这是一个非常好的问题,除了在代码中找到的一些官方说明之外,我没有找到任何官方声明。我会很高兴得到任何关于官方文档的提示。
答案是不,cgo调用不会阻塞调度器。
根据代码文档,从G中调用C函数的工作原理如下:
// 要从Go中调用C函数f,cgo生成的代码调用runtime.cgocall(_cgo_Cfunc_f, frame),其中_cgo_Cfunc_f是由cgo编写的gcc编译的函数。
//
// runtime.cgocall(下面)将g锁定到m,调用entersyscall以避免阻塞其他goroutine或垃圾收集器,然后调用runtime.asmcgocall(_cgo_Cfunc_f, frame)。
//
// runtime.asmcgocall(在asm_$GOARCH.s中)切换到m->g0堆栈(假设为操作系统分配的堆栈,因此可以在其上运行gcc编译的代码)并调用_cgo_Cfunc_f(frame)。
//
// _cgo_Cfunc_f使用从frame结构中获取的参数调用实际的C函数f,在frame中记录结果,并返回到runtime.asmcgocall。
//
// 在重新获得控制权后,runtime.asmcgocall切换回原始g(m->curg)的堆栈,并返回到runtime.cgocall。
//
// 在重新获得控制权后,runtime.cgocall调用exitsyscall,该函数会阻塞,直到此m可以在不违反$GOMAXPROCS限制的情况下运行Go代码,并解锁g。
entersyscall
实际上告诉运行时,这个goroutine现在处于“外部”控制之下,就像我们对内核进行系统调用的情况一样。另一个可能有用的信息是,将g
锁定到m
(将cgo调用的goroutine锁定到操作系统线程)使运行时能够分配一个新的操作系统线程(理论上超过GOMAXPROCS
)。
英文:
This is a very good question for which I did not find any official statement on the fly except in the code. I would be glad for any hint to official documentation.
The answer is no, cgo calls do not block the scheduler.
For the following it's good to know that internally Go uses G for goroutines, M for machines (threads) and P for proccessor. Goroutines are run on processors that run on machines.
Calling a C function from G works as follows according to the code documentation:
// To call into the C function f from Go, the cgo-generated code calls
// runtime.cgocall(_cgo_Cfunc_f, frame), where _cgo_Cfunc_f is a
// gcc-compiled function written by cgo.
//
// runtime.cgocall (below) locks g to m, calls entersyscall
// so as not to block other goroutines or the garbage collector,
// and then calls runtime.asmcgocall(_cgo_Cfunc_f, frame).
//
// runtime.asmcgocall (in asm_$GOARCH.s) switches to the m->g0 stack
// (assumed to be an operating system-allocated stack, so safe to run
// gcc-compiled code on) and calls _cgo_Cfunc_f(frame).
//
// _cgo_Cfunc_f invokes the actual C function f with arguments
// taken from the frame structure, records the results in the frame,
// and returns to runtime.asmcgocall.
//
// After it regains control, runtime.asmcgocall switches back to the
// original g (m->curg)'s stack and returns to runtime.cgocall.
//
// After it regains control, runtime.cgocall calls exitsyscall, which blocks
// until this m can run Go code without violating the $GOMAXPROCS limit,
// and then unlocks g from m.
entersyscall
essentially tells the runtime that this goroutine is now under 'external' control, just like in the situation when we do syscalls to the kernel. Another probably useful bit is that locking g
to m
(locking the cgo calling goroutine to the OS thread) enables the runtime to allocate a new OS thread (theoretically exceeding GOMAXPROCS
).
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论