上卷 程序设计
中卷 标准库
- bufio 1.18
- bytes 1.18
- io 1.18
- container 1.18
- encoding 1.18
- crypto 1.18
- hash 1.18
- index 1.18
- sort 1.18
- context 1.18
- database 1.18
- connection
- query
- queryrow
- exec
- prepare
- transaction
- scan & null
- context
- tcp
- udp
- http
- server
- handler
- client
- h2、tls
- url
- rpc
- exec
- signal
- embed 1.18
- plugin 1.18
- reflect 1.18
- runtime 1.18
- KeepAlived
- ReadMemStats
- SetFinalizer
- Stack
- sync 1.18
- atomic
- mutex
- rwmutex
- waitgroup
- cond
- once
- map
- pool
- copycheck
- nocopy
- unsafe 1.18
- fmt 1.18
- log 1.18
- math 1.18
- time 1.18
- timer
下卷 运行时
源码剖析
附录
文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
4.7.1 系统调用
系统调用分 Syscall、RawSyscall 两种方式,区别在于是否调用 entersyscall、exitsyscall 函数。
// src/syscall/asm_linux_amd64.s // func Syscall(trap int64, a1, a2, a3 uintptr) (r1, r2, err uintptr); // Trap # in AX, args in DI SI DX R10 R8 R9, return in AX DX // Note that this differs from "standard" ABI convention, which // would pass 4th arg in CX, not R10. TEXT ·Syscall(SB),NOSPLIT,$0-56 CALL runtime·entersyscall(SB) MOVQ a1+8(FP), DI MOVQ a2+16(FP), SI MOVQ a3+24(FP), DX MOVQ trap+0(FP), AX // syscall entry SYSCALL CMPQ AX, $0xfffffffffffff001 JLS ok MOVQ $-1, r1+32(FP) MOVQ $0, r2+40(FP) NEGQ AX MOVQ AX, err+48(FP) CALL runtime·exitsyscall(SB) RET ok: MOVQ AX, r1+32(FP) MOVQ DX, r2+40(FP) MOVQ $0, err+48(FP) CALL runtime·exitsyscall(SB) RET
// func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr) TEXT ·RawSyscall(SB),NOSPLIT,$0-56 MOVQ a1+8(FP), DI MOVQ a2+16(FP), SI MOVQ a3+24(FP), DX MOVQ trap+0(FP), AX // syscall entry SYSCALL CMPQ AX, $0xfffffffffffff001 JLS ok1 MOVQ $-1, r1+32(FP) MOVQ $0, r2+40(FP) NEGQ AX MOVQ AX, err+48(FP) RET ok1: MOVQ AX, r1+32(FP) MOVQ DX, r2+40(FP) MOVQ $0, err+48(FP) RET
进入
函数 entersyscall 解除 P 绑定,当前 M 依旧等待系统调用返回。P 并未放入闲置队列,也未主动让其绑定其他 M 积极工作。而是暂存起来,等系统调用结束时重新绑定。
可被 sysmon 抢走(retake)。
// proc.go // Standard syscall entry used by the go syscall library and normal cgo calls. // This is exported via linkname to assembly in the syscall package. func entersyscall() { reentersyscall(getcallerpc(), getcallersp()) }
func reentersyscall(pc, sp uintptr) { _g_ := getg() _g_.m.locks++ // 保存现场(g.sched.pc, sp),设置 syscall 状态。。 save(pc, sp) _g_.syscallsp = sp _g_.syscallpc = pc casgstatus(_g_, _Grunning, _Gsyscall) // 如果 sysmon 休眠,则唤醒它。 if atomic.Load(&sched.sysmonwait) != 0 { systemstack(entersyscall_sysmon) save(pc, sp) } // 类似 forEachP 之类的安全函数。 if _g_.m.p.ptr().runSafePointFn != 0 { systemstack(runSafePointFn) save(pc, sp) } // 执行计数器。 _g_.m.syscalltick = _g_.m.p.ptr().syscalltick // 解除当前 M 和 P 的关联。 pp := _g_.m.p.ptr() pp.m = 0 // P 并未放回闲置列表,而是暂存在 M.oldp。 _g_.m.oldp.set(pp) _g_.m.p = 0 // 修改状态。 atomic.Store(&pp.status, _Psyscall) // STW!!! if sched.gcwaiting != 0 { systemstack(entersyscall_gcwait) save(pc, sp) } _g_.m.locks-- }
阻塞版本则提前累加执行计数器,跳过 sysmon 检查。主动解除 P 绑定,让其匹配其他 M 积极工作。
// proc.go // The same as entersyscall(), but with a hint that the syscall is blocking. func entersyscallblock() { _g_ := getg() _g_.m.locks++ // 提前累加执行计数器。 _g_.m.syscalltick = _g_.m.p.ptr().syscalltick _g_.m.p.ptr().syscalltick++ // 保存现场。 pc := getcallerpc() sp := getcallersp() save(pc, sp) _g_.syscallsp = _g_.sched.sp _g_.syscallpc = _g_.sched.pc // 修改状态。 casgstatus(_g_, _Grunning, _Gsyscall) // 解除 P 绑定,让其匹配其他 M,积极执行任务。 systemstack(entersyscallblock_handoff) save(getcallerpc(), getcallersp()) _g_.m.locks-- }
func entersyscallblock_handoff() { handoffp(releasep()) }
退出
退出系统调用时,如果暂存 P 尚在,那么直接绑定,否则找闲置 P 继续。
// proc.go // The goroutine g exited its system call. // Arrange for it to run on a cpu again. // This is called only from the go syscall library, not // from the low-level system calls used by the runtime. func exitsyscall() { _g_ := getg() _g_.m.locks++ _g_.waitsince = 0 // 暂存 P。 oldp := _g_.m.oldp.ptr() _g_.m.oldp = 0 // 快模式:暂存 P 还在,或有闲置 P,直接绑定继续执行。 if exitsyscallfast(oldp) { // 累加计数器。 _g_.m.p.ptr().syscalltick++ // 修改状态。 casgstatus(_g_, _Gsyscall, _Grunning) _g_.syscallsp = 0 _g_.m.locks-- return } _g_.sysexitticks = 0 _g_.m.locks-- // 慢模式:没有可用 P,放到全局队列,等其他 MP 提取执行。 mcall(exitsyscall0) _g_.syscallsp = 0 _g_.m.p.ptr().syscalltick++ }
func exitsyscallfast(oldp *p) bool { _g_ := getg() // 重新绑定暂存 P。(status != _Psyscall 表示已经被 sysmon 夺走) if oldp != nil && oldp.status == _Psyscall && atomic.Cas(&oldp.status, _Psyscall, _Pidle) { wirep(oldp) exitsyscallfast_reacquired() return true } // 绑定空闲 P。 if sched.pidle != 0 { var ok bool systemstack(func() { ok = exitsyscallfast_pidle() }) if ok { return true } } return false }
// exitsyscall slow path on g0. // Failed to acquire P, enqueue gp as runnable. func exitsyscall0(gp *g) { // 修改状态。 casgstatus(gp, _Gsyscall, _Grunnable) // 解除 M 绑定。 dropg() lock(&sched.lock) // 再次尝试获取一个闲置 P。 var _p_ *p if schedEnabled(gp) { _p_ = pidleget() } // 依旧没找到 P。 if _p_ == nil { // 将 syscall G 放到全局任务队列。 globrunqput(gp) } else if atomic.Load(&sched.sysmonwait) != 0 { // 唤醒 sysmon。 atomic.Store(&sched.sysmonwait, 0) notewakeup(&sched.sysmonnote) } unlock(&sched.lock) // 如果找到 P,绑定,继续 syscall G 执行。 if _p_ != nil { acquirep(_p_) execute(gp, false) // Never returns. } // 当前 M 休眠,等待唤醒后重新进度调度循环。 stopm() schedule() // Never returns. }
CGO
采取和系统调用相同方式。
// cgocall.go // Call from Go to C. func cgocall(fn, arg unsafe.Pointer) int32 { mp := getg().m mp.ncgocall++ mp.ncgo++ entersyscall() mp.incgo = true errno := asmcgocall(fn, arg) mp.incgo = false mp.ncgo-- exitsyscall() KeepAlive(fn) KeepAlive(arg) KeepAlive(mp) return errno }
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论