首页 > 其他分享 >go语言调度gmp原理(3)

go语言调度gmp原理(3)

时间:2023-05-16 21:23:39浏览次数:36  
标签:runtime sched gp goroutine 调度 schedule mp go gmp

go语言调度gmp原理(3)

调度循环

调度器启动之后,go语言运行时会调用runtime.mstart和runtime.mstart1,前者会初始化g0的stackguard0和stackguard1字段,后者会初始化线程并调用runtime.schedule进入调度循环

func schedule() {
	mp := getg().m

	if mp.locks != 0 {
		throw("schedule: holding locks")
	}

	if mp.lockedg != 0 {
		stoplockedm()
		execute(mp.lockedg.ptr(), false) // Never returns.
	}

	// We should not schedule away from a g that is executing a cgo call,
	// since the cgo call is using the m's g0 stack.
	if mp.incgo {
		throw("schedule: in cgo")
	}

top:
	pp := mp.p.ptr()
	pp.preempt = false

	// Safety check: if we are spinning, the run queue should be empty.
	// Check this before calling checkTimers, as that might call
	// goready to put a ready goroutine on the local run queue.
	if mp.spinning && (pp.runnext != 0 || pp.runqhead != pp.runqtail) {
		throw("schedule: spinning with local work")
	}

	gp, inheritTime, tryWakeP := findRunnable() // blocks until work is available

	// This thread is going to run a goroutine and is not spinning anymore,
	// so if it was marked as spinning we need to reset it now and potentially
	// start a new spinning M.
	if mp.spinning {
		resetspinning()
	}

	if sched.disable.user && !schedEnabled(gp) {
		// Scheduling of this goroutine is disabled. Put it on
		// the list of pending runnable goroutines for when we
		// re-enable user scheduling and look again.
		lock(&sched.lock)
		if schedEnabled(gp) {
			// Something re-enabled scheduling while we
			// were acquiring the lock.
			unlock(&sched.lock)
		} else {
			sched.disable.runnable.pushBack(gp)
			sched.disable.n++
			unlock(&sched.lock)
			goto top
		}
	}

	// If about to schedule a not-normal goroutine (a GCworker or tracereader),
	// wake a P if there is one.
	if tryWakeP {
		wakep()
	}
	if gp.lockedm != 0 {
		// Hands off own p to the locked m,
		// then blocks waiting for a new p.
		startlockedm(gp)
		goto top
	}

	execute(gp, inheritTime)
}

runtime.schedule函数会从下面几处查找待执行的goroutine

  1. 为了保证公平,当全局运行队列中有待执行的goroutine时,通过schedtick保证有一定概率会从全局的运行队列中查找对应的goroutine
  2. 从处理器本地的运行队列中查找待执行的goroutine
  3. 如果前两种方法都没有找到goroutine,会通过runtime.findrunnable阻塞地查找goroutine

runtime.findrunnable获取goroutine过程

  1. 从本地运行队列、全局运行队列中查找
  2. 从网络轮询器中查找是否有goroutine等待运行
  3. 通过runtime.runqsteal尝试从其他随机的处理器中窃取待运行的goroutine,该函数还可能窃取处理器的计时器

当前函数一定会返回一个可执行的goroutine,如果当前不存在就会阻塞地等待

接下来由runtime.execute执行获取的goroutine,做好准备后,它会通过runtime.gogo将goroutine调度到当前线程上

func execute(gp *g, inheritTime bool) {
	mp := getg().m

	if goroutineProfile.active {
		// Make sure that gp has had its stack written out to the goroutine
		// profile, exactly as it was when the goroutine profiler first stopped
		// the world.
		tryRecordGoroutineProfile(gp, osyield)
	}

	// Assign gp.m before entering _Grunning so running Gs have an
	// M.
	mp.curg = gp
	gp.m = mp
	casgstatus(gp, _Grunnable, _Grunning)
	gp.waitsince = 0
	gp.preempt = false
	gp.stackguard0 = gp.stack.lo + _StackGuard
	if !inheritTime {
		mp.p.ptr().schedtick++
	}

	// Check whether the profiler needs to be turned on or off.
	hz := sched.profilehz
	if mp.profilehz != hz {
		setThreadCPUProfiler(hz)
	}

	if trace.enabled {
		// GoSysExit has to happen when we have a P, but before GoStart.
		// So we emit it here.
		if gp.syscallsp != 0 && gp.sysblocktraced {
			traceGoSysExit(gp.sysexitticks)
		}
		traceGoStart()
	}

	gogo(&gp.sched)
}

runtime.gogo从runtime.gobuf中取出了runtime.goexit的程序计数器和待执行函数的程序计数器,其中:

  • runtime.goexit的程序计数器被放到栈的SP上
  • 待执行函数的程序计数器被放到了寄存器BX上

正常的函数调用都会使用CALL指令,该指令会将调用方的返回地址加入栈寄存器SP中,然后跳转到目标函数;当目标函数返回后,会从堆中查找调用的地址并跳转回调用方继续执行剩余代码

当goroutine中运行的函数返回时,就会跳转到runtime.goexit所在位置执行该函数

经过一系列复杂的函数调用,我们最终在当前线程的g0的栈上调用runtime.goexit0函数,该函数会将goroutine转换为_Gdead状态、清除其中的字段、移除goroutine和线程的关联,并调用runtime.gfput重新加入处理器的goroutine空闲列表gFree

最后runtime.goexit0会重新调用runtime.schedule触发新一轮的goroutine调度,go语言中的运行时,调度循环会从runtime.schedule开始,最终又回到runtime.schedule

这里介绍的是goroutine正常执行并退出的逻辑,实际情况会复杂得多,多数情况下goroutine在执行过程中会经历写作时调度或者抢占式调度

标签:runtime,sched,gp,goroutine,调度,schedule,mp,go,gmp
From: https://www.cnblogs.com/zpf253/p/17406849.html

相关文章

  • go语言调度gmp原理(2)
    go语言调度gmp原理(2)创建goroutine通过runtime.newproc函数调用,runtime.newproc的入参是参数大小和表示函数的指针funcval,它会获取goroutine以及调用方的程序计数器,然后调用runtime.newproc1函数获取新的goroutine、结构体、将其加入处理器的运行队列,并在满足条件时调用runtime......
  • Golang接收者方法语法糖
    1、概述在《Golang常用语法糖》这篇博文中我们讲解Golang中常用的12种语法糖,在本文我们主要讲解下接收者方法语法糖。在介绍Golang接收者方法语法糖前,先简单说下Go语言的指针(Pointer),大致上理解如下:变量名前的& 符号,是取变量的内存地址,不是取值;数据类型前的* 符号,代表......
  • django系列-服务和环境配置(陆续完善中···)
    一、Mysql1、安装服务端yuminstallmariadb-server-ymariadb-server.x86_641:5.5.68-1.el7#版本2、安装客户端yuminstallmariadb-y#软件包1:mariadb-5.5.68-1.el7.x86_64已安装并且是最新版本3、服务配置4、帐号初始化二、Redis三、Python四、虚拟环境......
  • 【Go新手起步01】5步完成 vscode的go插件安装跟激活。
     首先下载vscode,进行两个插件安装,如图所示 然后下载go语言,在官网https://go.dev/doc/install下载 cmd打开,输入goversion验证下载是否成功。在dos页面输入goenv-wGO111MODULE=on                goenv-wGOPROXY=https://goproxy.cn,di......
  • golang vrrp + ipvs 实现简单的服务ha
    比较类似keeplived,但是是比较简单的集成参考图基于vrrp实现vip的处理,同时master以及backup安装基于vrrp+ipvs的程序,基于服务状态进行服务的切换处理 实现说明:对于vrrp处理可以基于包装的vrrpgolang(rongfengliang/vrrp)包,同时对于ipvs可以直接ipvs包(可以使用mqli......
  • c-for-go cgo 绑定自动生成工具
    c-for-go可以快速的生成cgo绑定代码的工具,目前有不少golang项目使用了此工具,比如cloudflare/ipvs也使用了此工具参考处理 参考使用这个是libvpx的一个项目yaml定义文件---GENERATOR:PackageName:vpxPackageDescription:"Packagevpxpro......
  • Django用递归实现查询所有子部门逻辑
    假设你已经定义好了部门模型Department,该模型包含以下字段:classDepartment(models.Model):name=models.CharField(max_length=100)parent_department=models.ForeignKey('self',on_delete=models.CASCADE,null=True,blank=True)其中,name表示部门名称,paren......
  • Golang URL query contains semicolon 报错解决方案
    ​ 报错信息http:URLquerycontainssemicolon,whichisnolongerasupportedseparator;partsofthequerymaybestrippedwhenparsed;seegolang.org/issue/25192 高版本http废除了分号做分隔符,会在http库中做报警输出,基础库代码如下:func(shserverHandle......
  • Go语言并发编程-cnblog
    并发编程并发vs并行举个形象点的例子并发可以理解为一边吃饭,一边喝水,因为人只有一个嘴一个咽喉,所以同一时刻饭和水只能有一样进入,二者只能交替进行并行可以理解为一边走路一边吃东西,因为走路是靠腿脚,吃东西是靠嘴,二者不相干,相当于两个独立的线程,因而可以同时进行Go语言......
  • python3 获取mongodb表中数据的条数
    说明:此处考虑了时区,mongodb默认使用"格林威治时间"1#!/usr/bin/python323importpymongo4importdatetime5importpytz67#统计8"""9/usr/bin/pip3install-Ivpymongo-ihttp://pypi.douban.com/simple/--trusted-hostpypi.douban.com......