返回介绍

上卷 程序设计

中卷 标准库

下卷 运行时

源码剖析

附录

4.4.1 新建

发布于 2024-10-12 19:15:58 字数 3196 浏览 0 评论 0 收藏 0

新建 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文