Runtime: Golang是如何处理系统调用阻塞的?
我们知道在Golang中,当一个Goroutine由于执行 系统调用
而阻塞时,会将M从GPM中分离出去,然后P再找一个G和M重新执行,避免浪费CPU资源,那么在内部又是如何实现的呢?今天我们还是通过学习Runtime源码的形式来看下他的内部实现细节有哪些?
go version 1.15.6
我们知道一个P有四种运行状态,而当执行系统调用函数阻塞时,会从 _Prunning
状态切换到 _Psyscall
,等系统调用函数执行完毕后再切换回来。P的状态切换
从上图我们可以看出 P
执行系统调用时会执行 [entersyscall()](https://github.com/golang/go/blob/go1.15.6/src/runtime/proc.go#L3134-L3142)
函数(另还有一个类似的阻塞函数 entersyscallblock()
,注意两者的区别)。当系统调用执行完毕切换回去会执行 exitsyscall()
函数,下面我们看一下这两个函数的实现。
进入系统调用
// Standard syscall entry used by the go syscall library and normal cgo calls.
//
// This is exported via linkname to assembly in the syscall package.
//
//go:nosplit
//go:linkname entersyscall
func entersyscall() {
reentersyscall(getcallerpc(), getcallersp())
}
当通过Golang标准库 syscall
或者 cgo
调用时会执行 entersyscall()
函数,并通过 go:linkname
方式导出为标准包。此函数只是对 [reentersyscall()](https://github.com/golang/go/blob/go1.15.6/src/runtime/proc.go#L3037-L3132)
函数的封装,我们看下这个函数实现了什么。
函数注释比较多,这里只帖子重点的一部分
// The goroutine g is about to enter a system call.
// Record that it's not using the cpu anymore.
// This is called only from the go syscall library and cgocall,
// not from the low-level system calls used by the runtime.
//
// Entersyscall cannot split the stack: the gosave must
// make g->sched refer to the caller's stack segment, because
// entersyscall is going to return immediately after.
//
// Nothing entersyscall calls can split the stack either.
// We cannot safely move the stack during an active call to syscall,
// because we do not know which of the uintptr arguments are
// really pointers (back into the stack).
// In practice, this means that we make the fast path run through
// entersyscall doing no-split things, and the slow path has to use systemstack
// to run bigger things on the system stack.
//
// reentersyscall is the entry point used by cgo callbacks, where explicitly
// saved SP and PC are restored. This is needed when exitsyscall will be called
// from a function further up in the call stack than the parent, as g->syscallsp
// must always point to a valid stack frame. entersyscall below is the normal
// entry point for syscalls, which obtains the SP and PC from the caller.
从注释我们得知以下信息:
By admin
read more