golang 调度之wakep和M创建

前端之家收集整理的这篇文章主要介绍了golang 调度之wakep和M创建前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

说说wakep和m的创建吧

wakep调用时机和作用

  • 作用:wakep的作用是添加一个P来执行goroutinue
  • 时机:在有G变为runnable的时候 如:newproc ready
    wakep中调用startm来启动一个新的m

startm

  • 首先如果传递过来P是nil,则需要获取一个idle P,如果获取不到,直接返回
  • 调用mget获得一个已经睡眠m
  • 如果没有获得m,则调用newm创建一个新的m
  • 如果是创建的m则直接返回,后面再具体将newm的功能。如果是获取的m,则调用notewakeup来唤醒m(因为m在mput的时候已经睡眠了)

mget

mget比较简单,就是从midle中取出一个通过mput放入的m,如果没有则返回nil

newm

func newm(fn func(),_p_ *p)

先看看注释:

// Create a new m. It will start off with a call to fn,or else the scheduler.
// fn needs to be static and not a heap allocated closure.
// May run with m.p==nil,so write barriers are not allowed.

创建一个g并执行fm或者是一次调度,所谓创建m就是启动一个系统线程。

  • 调用allocm创建一个m并设置m.nextp为传进来的参数p,allocm还为m创建g0,并设置一个栈
  • 这里有cgo的东西,暂时先不说,我也不懂,呵呵。
  • 如果不是cgo,调用newosproc

newosproc

这个函数就有点系统相关了,这里看os_windows.go下的。
这个函数调用一个系统调用来创建线程
这里创建的线程的启动函数tstart_stdcall

所以我们先不纠结系统调用的问题,直接看线程启动之后会干嘛:

这个函数是用汇编写的(sys_windows_amd64.s)

在这个函数中,第一参数是存放在CX中。参数就是前面创建的m

  • 获取g0
  • 在这个函数中会重新设置栈给g0
  • 这里把当前线程的栈顶给g的栈底,然后预留64K的空间给g0,并设置栈的stack_hi和stack_lo,以及stackguard0和stackguard1.
  • 设置线程本地存储 将m的tls设置到0x28(GS),然后设置m给g0.m,设置g0给tls,一遍g(tls)可以取到当前g。
  • 调用runtime.stackcheck
  • 调用runtime.mstart

stackcheck就是检查当前的SP是不是[stack_hi,stack_lo)区间
然后看mstart

mstart

这里lo != 0, ==0那一段代码得意思就是在这里设置设置栈
意思是说cgo的时候可能值设置了栈大小到stack.hi
所以这里先创建一个局部变量size保存stack.hi,因为size是局部变量,所以就把size的栈地址设置为stack.hi,然后根据size计算出stack.lo

调用mstart1

mstart1会进行一些初始化并保存g0的栈等,

如果这里是m0,还会初始化信号量等。

如果m有startfn这个函数,也就是之前调用newm的时候传递了函数过来的话,就限制性这个函数。sysmon就是通过这种方式执行的。

这里如果是helpgc,则直接停掉这个m,或者如果不是m0,会把m.nextp设置给m.p

然后调用schedule来调度任务。

schedule

这个函数主要是找到一个runnable的g 然后调用execute来启动g

  • 这里有可能会只是trance或者gc的g,这部分还不太了解。

  • 下面是先从全局队列里面取一次,因为如果不先取全局队列,那么有可能两个g互相继续而导致全局的g没有机会运行。

  • 然后尝试从本地(P)的队列获取一个g

  • 调用findrunnable,这个函数会阻塞知道找到一个可运行的g
  • 如果找到一个g且为m为spinning,则清除状态
  • 调用execute执行找到的g

findrunnable

  • 如果gcwatting stopm
  • fingwait没看懂
  • 从本地P队列获取g
  • 从全局队列获取g
  • 如果没有人在进行netpoll,尝试netpoll发现g
  • 如果当前所有的P都是idle状态, 跳过从别的P偷取工作流的流程,否则进行偷取
  • 如果spinning的M大于busy的P,则直接让m睡眠
  • 又是GC的worker
  • 设置P为idle
  • 暂时让nmspinning-1,然后再次检查所有P上的runq,发现有P上有多余的goroutinue,则让M重新得到一个P,然后再次进行上面的查找g的流程。
  • 又是GC
  • 如果没有人在netpool,让当前M永久阻塞在netpool睡眠。
  • 如果有人在netpool了,则直接stopm,让m睡眠

execute

  • 先把g的状态设置为running
  • 设置栈为正常状态而不是preempt状态
  • 调用gogo切换SP和PC等。
原文链接:https://www.f2er.com/go/188655.html

猜你在找的Go相关文章