上卷 程序设计
中卷 标准库
- 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.1 新建
新建 M 包装对象,对应一个系统线程。
// runtime2.go type m struct { g0 *g // 系统栈 p puintptr // 绑定。 nextp puintptr // 预定,用于后续绑定。 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) { // allocm adds a new M to allm, but they do not start until created by // the OS in newm1 or the template thread. mp := allocm(_p_, fn, id) mp.nextp.set(_p_) newm1(mp) }
每个 M 都自带 g0(默认 8 KB)栈,执行运行时命令。而当执行用户代码时,则使用 G.stack 内存。
// Allocate a new m unassociated with any thread. func allocm(_p_ *p, fn func(), id int64) *m { mp := new(m) mp.mstartfn = fn 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 || mStackIsSystemAllocated() { mp.g0 = malg(-1) } else { mp.g0 = malg(8192 * sys.StackGuardMultiplier) } mp.g0.m = mp return mp }
// Pre-allocated ID may be passed as 'id', or omitted by passing -1. func mcommoninit(mp *m, id int64) { // 编号。 if id >= 0 { mp.id = id } else { mp.id = mReserveID() } // 信号。 mpreinit(mp) if mp.gsignal != nil { mp.gsignal.stackguard1 = mp.gsignal.stack.lo + _StackGuard } // 加入 allm,避免被垃圾回收。 mp.alllink = allm atomicstorep(unsafe.Pointer(&allm), unsafe.Pointer(mp)) }
创建系统线程(thread),执行入口函数。
func newm1(mp *m) { newosproc(mp) }
// 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) { ret := clone(cloneFlags, stk, unsafe.Pointer(mp), unsafe.Pointer(mp.g0), unsafe.Pointer(abi.FuncPCABI0(mstart))) }
注意,线程入口是 mstart 函数。
- mstart : 线程入口函数。
- startm: 调度器新建或唤醒 M。
- M.mstartfn: 用于一些初始设定。
// proc.go // mstart is the entry-point for new Ms. func mstart() // asm_amd64.s TEXT runtime·mstart(SB),NOSPLIT|TOPFRAME,$0 CALL runtime·mstart0(SB) RET // not reached
// proc.go // mstart0 is the Go entry-point for new Ms. func mstart0() { // 进入调度循环。 mstart1() // 终止线程。(只有在出错时才会调用) mexit(osStack) }
func mstart1() { // g0 _g_ := getg() if _g_ != _g_.m.g0 { throw("bad runtime·mstart") } // 初始化 g0 状态,将 mstart0 作为初始帧。 // 后续调用不会修改这些数据,以便出错时回到 mstart0,执行 mexit 终止。 // 也就是说,g0 栈总是从第二帧开始复用,无需清理调用堆栈。 _g_.sched.g = guintptr(unsafe.Pointer(_g_)) _g_.sched.pc = getcallerpc() _g_.sched.sp = getcallersp() // 执行初始化函数(startm 传入的是设置自旋状态 mspinning) if fn := _g_.m.mstartfn; fn != nil { fn() } // 如果不是主线程,则绑定预存的 P。 if _g_.m != &m0 { acquirep(_g_.m.nextp.ptr()) _g_.m.nextp = 0 } // 进入调度循环。 schedule() }
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论