上卷 程序设计
中卷 标准库
- 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.4.3 新建
新建 M 包装对象,分配 g0 栈。
创建系统线程(thread),执行线程函数 mstart,进入调度循环。
// runtime2.go type m struct { g0 *g // goroutine with scheduling stack p puintptr // attached p for executing go code (nil if not executing go code) mstartfn func() }
// proc.go // Create a new m. It will start off with a call to fn, or else the scheduler. func newm(fn func(), _p_ *p, id int64) { mp := allocm(_p_, fn, id) mp.nextp.set(_p_) newm1(mp) }
每个 M 都自带 g0(默认 8KB),用于执行运行时命令。
某些操作系统由内核提供 g0 内存。
新建 M 加入 allm 链表,总数不能超过限制(默认 10000)。
// Allocate a new m unassociated with any thread. // Can use p for allocation context if needed. // fn is recorded as the new m's m.mstartfn. func allocm(_p_ *p, fn func(), id int64) *m { mp := new(m) mp.mstartfn = fn // 设置 id,保存到 allm 链表等。 // 检查是否超过 sched.maxmcount 限制。 mcommoninit(mp, id) // In case of cgo or Solaris or illumos or Darwin, pthread_create will make us a stack. // Windows and Plan 9 will layout sched stack on OS stack. if iscgo || GOOS == "solaris" || GOOS == "illumos" || GOOS == "windows" || GOOS == "plan9" || GOOS == "darwin" { mp.g0 = malg(-1) } else { mp.g0 = malg(8192 * sys.StackGuardMultiplier) } return mp }
func newm1(mp *m) { newosproc(mp) }
不同操作系统创建线程的方式不同,不过启动函数均为 mstart。
// os_linux.go const cloneFlags = _CLONE_VM | /* share memory */ _CLONE_FS | /* share cwd, etc */ _CLONE_FILES | /* share fd table */ _CLONE_SIGHAND | /* share sig handler table */ _CLONE_SYSVSEM | /* share SysV semaphore undo lists (see issue #20763) */ _CLONE_THREAD /* revisit - okay for now */ func newosproc(mp *m) { stk := unsafe.Pointer(mp.g0.stack.hi) ret := clone(cloneFlags, stk, unsafe.Pointer(mp), unsafe.Pointer(mp.g0), unsafe.Pointer(funcPC(mstart))) }
// os_windows.go func newosproc(mp *m) { thandle := stdcall6(_CreateThread, 0, 0, funcPC(tstart_stdcall), uintptr(unsafe.Pointer(mp)), 0, 0) } // sys_windows_amd64.s TEXT runtime·tstart_stdcall(SB),NOSPLIT,$0 CALL runtime·mstart(SB)
mstart
线程启动后,进入 schedule 调度循环。
注意:
startm 由 wakep 调用,用于新建或唤醒。
mstart 是线程函数。创建线程后执行,进入调度循环。
正常情况下,M 一直在调度循环。
只有在出错时,才会回到 mstart,终止线程。
详情参考《其他》。
// proc.go // Called to start an M. func mstart() { mstart1() // Exit this thread. mexit(osStack) }
func mstart1() { // 执行初始化函数(startm 传入的是设置自旋状态 mspinning) if fn := _g_.m.mstartfn; fn != nil { fn() } // 不是主线程。 if _g_.m != &m0 { // 从 nextp 提取并绑定 P。 acquirep(_g_.m.nextp.ptr()) _g_.m.nextp = 0 } // 进入调度循环。 schedule() }
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论