上卷 程序设计
中卷 标准库
- 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 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
enter
进入前,设置相关状态。
提前解除了绑定设置,可被抢走(sysmon retake),但并未上交到闲置队列或唤醒其他 M。
该方式不会提前累加计数器。
// proc.go // Standard syscall entry used by the go syscall library and normal cgo calls. func entersyscall() { reentersyscall(getcallerpc(), getcallersp()) }
func reentersyscall(pc, sp uintptr) { _g_ := getg() _g_.stackguard0 = stackPreempt // 保存执行现场。 save(pc, sp) casgstatus(_g_, _Grunning, _Gsyscall) // 解除当前 M 和 P 关联,存储在 M.oldp 供退出时检查。 // 当前 M 执行系统调用,P 可被抢走。 pp := _g_.m.p.ptr() pp.m = 0 _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) } }
阻塞方式,累加计数器,提前主动交出 P 执行其他任务。
因为累加计数器,sysmon 可能会跳过检查。
// proc.go // The same as entersyscall(), but with a hint that the syscall is blocking. func entersyscallblock() { _g_ := getg() _g_.stackguard0 = stackPreempt // see comment in entersyscall // 阻塞方式,累加计数器。 _g_.m.p.ptr().syscalltick++ // 保存现场。 pc := getcallerpc() sp := getcallersp() save(pc, sp) casgstatus(_g_, _Grunning, _Gsyscall) systemstack(entersyscallblock_handoff) // Resave for traceback during blocked call. save(getcallerpc(), getcallersp()) }
func entersyscallblock_handoff() { handoffp(releasep()) }
exit
退出系统调用时,如果原 P 还在,那么最好。否则找一个闲置 P 继续。
// proc.go // The goroutine g exited its system call. func exitsyscall() { _g_ := getg() oldp := _g_.m.oldp.ptr() // 看老伙计还在不在(entersyscall)。 // 如在,继续执行当前,无需切入调度循环。 if exitsyscallfast(oldp) { // 累加计数,修改状态退出系统调用。 _g_.m.p.ptr().syscalltick++ casgstatus(_g_, _Gsyscall, _Grunning) return } // Call the scheduler. mcall(exitsyscall0) }
func exitsyscallfast(oldp *p) bool { // 继续使用原 P。(运气好,没被抢) if oldp != nil && oldp.status == _Psyscall && atomic.Cas(&oldp.status, _Psyscall, _Pidle) { wirep(oldp) exitsyscallfast_reacquired() return true } // 尝试获取一个空闲 P. if sched.pidle != 0 { systemstack(func() { ok = exitsyscallfast_pidle() }) if ok { return true } } return false }
func exitsyscallfast_pidle() bool { _p_ := pidleget() if _p_ != nil { acquirep(_p_) return true } return false }
如绑定 P 失败,将 G 放回全局队列,M 休眠。
// exitsyscall slow path on g0. // Failed to acquire P, enqueue gp as runnable. func exitsyscall0(gp *g) { // 修改状态,解除 M 关联。 casgstatus(gp, _Gsyscall, _Grunnable) dropg() // 获取闲置 P。 var _p_ *p if schedEnabled(_g_) { _p_ = pidleget() } // 获取失败,将 G 放回全局队列。 if _p_ == nil { globrunqput(gp) } // 关联 P,继续执行 G。 if _p_ != nil { acquirep(_p_) execute(gp, false) // Never returns. } // 锁定模式。 if _g_.m.lockedg != 0 { stoplockedm() execute(gp, false) // Never returns. } // 绑定失败,休眠。 // 等待唤醒。 stopm() schedule() // Never returns. }
cgo
采取类似方式。
// cgocall.go // Call from Go to C. func cgocall(fn, arg unsafe.Pointer) int32 { entersyscall() errno := asmcgocall(fn, arg) exitsyscall() }
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论