【golang】调度系列之sysmon

调度系列
调度系列之goroutine
调度系列之m
调度系列之p
掉地系列之整体介绍

在golang的调度体系中,除了GMP本身,还有另外一个比较重要的角色sysmon。实际上,除了GMP和sysmon,runtime中还有一个全局的调度器对象。但该对象只是维护一些全局的数据,而不承担实际的调度职责,并不值得单独介绍,感兴趣的同学可以自己了解一下。

回到sysmon,sysmon是一个管理线程或者说守护线程,其是对GMP调度架构的补充和兜底。通过前面的几篇介绍,可以知道GMP的调度完全是主动协作式的调度。主动协作式的调度性能很高,但是在某些情况下会出现单个goroutine长期占据时间片甚至一直占据时间片的情况。
比如:

  • 某个goroutine不执行主动调度、不调用系统调用、不做函数调用,就会一直运行直到goroutine退出;
  • 某个goroutine处于syscall状态时也无法触发主动调度,可能会造成该goroutine长时间占据时间片;

sysmon的作用就是处理类似上面情况,其主要的工作内容有:

  • 定期查看netpoll有无就绪的任务,防止netpoll阻塞队列中的goroutine饥饿;
  • 定期查看是否有p长时间(10ms)处于syscall状态,如有则将p的持有权释放以执行其他g;
  • 定期查看是否有p长时间(10ms)没有调度,如有则对当前m发送信号,触发基于信号的异步抢占调度;

在main函数启动时,会调用newm函数创建sysmon线程,sysmon作为mstartfn传入。

// src/runtime/proc.go 145
// The main goroutine.
func main() {...if GOARCH != "wasm" { // no threads on wasm yet, so no sysmonsystemstack(func() {newm(sysmon, nil, -1)})}...
}

在介绍m的时候,我们提到过,mstart中会先调用mstartfn,然后再获取p并调用schedule函数。由于sysmon函数是循环不返回的,所以对应的m(也就是线程)永远运行sysmon,并且不需要获取p。所以并不是所有的m都需要p才可以运行的。
接下来,我们看下sysmon的里面具体做了些什么。

  • 进入sysmon可以看到里面是一个死循环,这和我们上面提到的一样。该循环并非一直忙等,而是会根据系统的情况进行延时睡眠,初始的interval是20us,最大的interval是10ms。
  • 在某些特殊的情况,sysmon可以进入更长时间(超过10ms)的睡眠,条件包括:
    • 系统不需要schedtrace。看起来是和调度相关观测的内容,如果需要schedtrace,则sysmon需要及时输出相关数据;
    • 系统处于停滞状态。这个停滞是我自己描述的,不一定准确,包括两种情况:1. 所有的p都是空闲的,此时系统中没有任务执行;2. 系统在等待进入gc状态,马上要stop the world;
      满足上面两个条件,则可最大进行1min的睡眠。1min是最大强制gc时间(2min)的一半。
  • sysmon的活跃状态,首先会坚持netpoll是否超过10ms没有被检查过,这是为了防止netpoll挂载goroutine的饥饿;
  • 然后会进行retake操作,retake的内容就是对所有p进行检查,查看p是否处于syscall或者被一个goroutine占据时间过长(超过10ms),如果有则进行相应的处理;
  • 最后还会进行gc和schedtrace相关的操作;
// src/runtime.go 5134
func sysmon() {lock(&sched.lock)sched.nmsys++checkdead()unlock(&sched.lock)lasttrace := int64(0)idle := 0 // how many cycles in succession we had not wokeup somebodydelay := uint32(0)for {if idle == 0 { // start with 20us sleep...delay = 20} else if idle > 50 { // start doubling the sleep after 1ms...delay *= 2}if delay > 10*1000 { // up to 10msdelay = 10 * 1000}usleep(delay)now := nanotime()if debug.schedtrace <= 0 && (sched.gcwaiting != 0 || atomic.Load(&sched.npidle) == uint32(gomaxprocs)) {lock(&sched.lock)if atomic.Load(&sched.gcwaiting) != 0 || atomic.Load(&sched.npidle) == uint32(gomaxprocs) {syscallWake := falsenext := timeSleepUntil()if next > now {atomic.Store(&sched.sysmonwait, 1)unlock(&sched.lock)// Make wake-up period small enough// for the sampling to be correct.sleep := forcegcperiod / 2if next-now < sleep {sleep = next - now}shouldRelax := sleep >= osRelaxMinNSif shouldRelax {osRelax(true)}syscallWake = notetsleep(&sched.sysmonnote, sleep)if shouldRelax {osRelax(false)}lock(&sched.lock)atomic.Store(&sched.sysmonwait, 0)noteclear(&sched.sysmonnote)}if syscallWake {idle = 0delay = 20}}unlock(&sched.lock)}lock(&sched.sysmonlock)// Update now in case we blocked on sysmonnote or spent a long time// blocked on schedlock or sysmonlock above.now = nanotime()// trigger libc interceptors if neededif *cgo_yield != nil {...}// poll network if not polled for more than 10mslastpoll := int64(atomic.Load64(&sched.lastpoll))if netpollinited() && lastpoll != 0 && lastpoll+10*1000*1000 < now {atomic.Cas64(&sched.lastpoll, uint64(lastpoll), uint64(now))list := netpoll(0) // non-blocking - returns list of goroutinesif !list.empty() {incidlelocked(-1)injectglist(&list)incidlelocked(1)}}if GOOS == "netbsd" && needSysmonWorkaround {...}if scavenger.sysmonWake.Load() != 0 {// Kick the scavenger awake if someone requested it.scavenger.wake()}// retake P's blocked in syscalls// and preempt long running G'sif retake(now) != 0 {idle = 0} else {idle++}// check if we need to force a GCif t := (gcTrigger{kind: gcTriggerTime, now: now}); t.test() && atomic.Load(&forcegc.idle) != 0 {lock(&forcegc.lock)forcegc.idle = 0var list gListlist.push(forcegc.g)injectglist(&list)unlock(&forcegc.lock)}if debug.schedtrace > 0 && lasttrace+int64(debug.schedtrace)*1000000 <= now {lasttrace = nowschedtrace(debug.scheddetail > 0)}unlock(&sched.sysmonlock)}
}

retake的操作也相对比较好理解。在p的介绍中我们提到过schedtick、syscalltick、sysmontick三个字段,其作用正是为了sysmon的检查。

sysmontick表示sysmon观测到的调度和系统调用情况,schedtick、syscalltick为实际的调度和系统调用情况。因为sysmon会经常睡眠,所以两者之间会有差异。

  • sysmon在检查所有p的过程中,如果发现sysmontick落后于实际情况,就会以实际情况为准更新sysmontick,同时也不会再做校验。因为sysmon睡眠最大时间为10ms,说明对应的p在10ms内做了调度。
  • 如果sysmontick和实际情况一只,则要看p是否运行一个goroutine超过10ms,如果是,则对m发送信号,触发异步抢占调度;如果p处于syscall状态超过10ms,则将p的持有权释放执行其他g。
func retake(now int64) uint32 {n := 0// Prevent allp slice changes. This lock will be completely// uncontended unless we're already stopping the world.lock(&allpLock)// We can't use a range loop over allp because we may// temporarily drop the allpLock. Hence, we need to re-fetch// allp each time around the loop.for i := 0; i < len(allp); i++ {_p_ := allp[i]if _p_ == nil {// This can happen if procresize has grown// allp but not yet created new Ps.continue}pd := &_p_.sysmonticks := _p_.statussysretake := falseif s == _Prunning || s == _Psyscall {// Preempt G if it's running for too long.t := int64(_p_.schedtick)if int64(pd.schedtick) != t {pd.schedtick = uint32(t)pd.schedwhen = now} else if pd.schedwhen+forcePreemptNS <= now {preemptone(_p_)// In case of syscall, preemptone() doesn't// work, because there is no M wired to P.sysretake = true}}if s == _Psyscall {// Retake P from syscall if it's there for more than 1 sysmon tick (at least 20us).t := int64(_p_.syscalltick)if !sysretake && int64(pd.syscalltick) != t {pd.syscalltick = uint32(t)pd.syscallwhen = nowcontinue}// On the one hand we don't want to retake Ps if there is no other work to do,// but on the other hand we want to retake them eventually// because they can prevent the sysmon thread from deep sleep.if runqempty(_p_) && atomic.Load(&sched.nmspinning)+atomic.Load(&sched.npidle) > 0 && pd.syscallwhen+10*1000*1000 > now {continue}// Drop allpLock so we can take sched.lock.unlock(&allpLock)// Need to decrement number of idle locked M's// (pretending that one more is running) before the CAS.// Otherwise the M from which we retake can exit the syscall,// increment nmidle and report deadlock.incidlelocked(-1)if atomic.Cas(&_p_.status, s, _Pidle) {if trace.enabled {traceGoSysBlock(_p_)traceProcStop(_p_)}n++_p_.syscalltick++handoffp(_p_)}incidlelocked(1)lock(&allpLock)}}unlock(&allpLock)return uint32(n)
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/90983.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

浅谈AVL树

文章目录 1.介绍1.1定义1.2来源1.3概念1.特性2.平衡因子[ Balance Factor-- _bf ] 2.BST>AVL1.示例分析2.情况分类3.代码剖析3.1左左型-右单旋3.2右右型-左单旋3.3左右型-左右旋3.4右左型:右左旋3.5总图 3.完整代码3.1AVLTree.h3.2Test.cpp 1.介绍 1.1定义 AVL树 – 平衡二…

RabbitMQ(15672) 消息中间件 NOTE

目录 1、初识 RabbitMQ 消息队列 1.1 MQ 四大核心概念 1.2 消息的发送&#xff08;无交换机态&#xff09; 1.3 关于消息自动重新入队 1.3.1 消息的常见应答方法&#xff08;R&#xff09; 1.4 关于 RabbitMQ 的持久化、不公平分发以及预取值 2、RabbitMQ 消息的发布确认…

centos7用docker安装WireGuard教程

PS:本文章用于帮助组建自己内网或者公司组网操作,该教程不涉及翻墙操作. 1、 检查centos内核版本 uname -r2、升级内核 下载脚本上传到服务器运行脚本进行升级内核 链接&#xff1a;https://pan.baidu.com/s/1vYmqVy2St3nFnJWGPIwdOw 提取码&#xff1a;owac 3、安装WireG…

云原生Kubernetes:K8S安全机制

目录 一、理论 1.K8S安全机制 2.Authentication认证 3.Authorization授权 4.Admission Control准入控制 5.User访问案例 6.ServiceAccount访问案例 二、实验 1.Admission Control准入控制 2.User访问案例 3.ServiceAccount访问案例 三、问题 1.生成资源报错 2.镜…

7.2 怎样定义函数

7.2.1 为什么要定义函数 主要内容&#xff1a; 为什么要定义函数 C语言要求所有在程序中用到的函数必须“先定义&#xff0c;后使用”。这是因为在调用一个函数之前&#xff0c;编译系统需要知道这个函数的名字、返回值类型、功能以及参数的个数与类型。如果没有事先定义&…

多叉树+图实现简单业务流程

文章目录 场景整体架构流程业务界面技术细节小结 场景 这次遇到一个需求,大致就是任务组织成方案,方案组织成预案,预案可裁剪调整.预案关联事件等级配置,告警触发预案产生事件.然后任务执行是有先后的,也就是有流程概念. 整体架构流程 方案管理、预案管理构成任务流程的基础条…

nginx 多层代理 + k8s ingress 后端服务获取客户真实ip 配置

1.nginx http 七层代理 修改命令空间&#xff1a; namespace: nginx-ingress : configmap&#xff1a;nginx-configuration kubectl get cm nginx-configuration -n ingress-nginx -o yaml添加如上配置 compute-full-forwarded-for: “true” forwarded-for-header: X-Forwa…

.balckhoues-V-XXXXXXX勒索病毒数据怎么处理|数据解密恢复

引言&#xff1a; 随着网络犯罪的不断演进&#xff0c;勒索病毒已成为当前数字时代的威胁之一&#xff0c;其中包括.balckhoues-V-XXXXXXX勒索病毒。本文将深入介绍.balckhoues-V-XXXXXXX勒索病毒的特点、数据恢复方法以及预防措施&#xff0c;以帮助您更好地理解和应对这一威…

NPDP产品经理认证怎么报名?考试难度大吗?

PMDA&#xff08;Product Development and Management Association&#xff09;是美国产品开发与管理协会&#xff0c;在中国由中国人才交流基金会培训中心举办NPDP&#xff08;New Product Development Professional&#xff09;考试&#xff0c;该考试是产品经理国际资格认证…

Spring实例化源码解析之ComponentScanAnnotationParser(四)

上一章我们分析了ConfigurationClassParser&#xff0c;配置类的解析源码分析。在ComponentScans和ComponentScan注解修饰的候选配置类的解析过程中&#xff0c;我们需要深入的了解一下ComponentScanAnnotationParser的parse执行流程&#xff0c;SpringBoot启动类为什么这么写&…

WinPcap4.1.3安装失败解决方法,A newer version of winpcap...

WinPcap4.1.3安装失败解决方法&#xff0c;A newer version of winpcap… 如图所示&#xff0c;提示本地安装有更高版本的WinPcap时&#xff0c;按如下操作即可解决。 找到相应文件&#xff0c;扩展名修改成如下&#xff1a; C:\Windows\SysWOW64 的wpcap.dll改成 wpcap.dll.…

【Verilog 教程】6.5 Verilog避免Latch

关键词&#xff1a;触发器&#xff0c;锁存器 Latch 的含义 锁存器&#xff08;Latch&#xff09;&#xff0c;是电平触发的存储单元&#xff0c;数据存储的动作取决于输入时钟&#xff08;或者使能&#xff09;信号的电平值。仅当锁存器处于使能状态时&#xff0c;输出才会随着…

使用 Velocity 模板引擎的 Spring Boot 应用

使用 Velocity 模板引擎的 Spring Boot 应用 模板引擎是构建动态内容的重要工具&#xff0c;特别适用于生成HTML、邮件内容、报告和其他文本文档。Velocity是一个强大的模板引擎&#xff0c;它具有简单易用的语法和灵活性。本文将介绍如何在Spring Boot应用中使用Velocity模板…

C语言入门Day_27 开发环境

前言&#xff1a; 在线编译环境涉及到联网&#xff0c;如果在没有网的情况下&#xff0c;我们就不能写代码了&#xff0c;这一章节&#xff0c;我们将会给大家介绍一下如何搭建一个本地的C语言编译环境。 如果想要设置 C 语言环境&#xff0c;需要确保电脑上有以下两款可用的…

二十二,加上各种贴图

使用pbr的各种贴图&#xff0c;albedo,金属度&#xff0c;ao,法线&#xff0c;粗糙度&#xff0c;可以更好的控制各个片元 1&#xff0c;先加上纹理坐标 texCoords->push_back(osg::Vec2(xSegment, ySegment)); geom->setVertexAttribArray(3, texCoords, osg::Array::BI…

凹凸贴图如何提高物体的真实感

什么是凹凸贴图 凹凸贴图&#xff08;Bump Mapping&#xff09;是一种计算机图形学中的技术&#xff0c;用于在表面上模拟微小的凹凸形状&#xff0c;从而增加了物体的细节和真实感。它可以在不改变物体几何形状的情况下&#xff0c;通过修改光照的反应&#xff0c;使表面看起来…

Java编程技巧:文件上传、下载、预览

目录 1、上传文件1.1、代码1.2、postman测试截图 2、下载resources目录中的模板文件2.1、项目结构2.2、代码2.3、使用场景 3、预览文件3.1、项目结构3.2、代码3.3、使用场景 1、上传文件 1.1、代码 PostMapping("/uploadFile") public String uploadFile(Multipart…

idea Springboot 高校科研资源共享系统VS开发mysql数据库web结构java编程计算机网页源码maven项目

一、源码特点 springboot 高校科研资源共享系统是一套完善的信息系统&#xff0c;结合springboot框架和bootstrap完成本系统&#xff0c;对理解JSP java编程开发语言有帮助系统采用springboot框架&#xff08;MVC模式开发&#xff09;&#xff0c; 系统具有完整的源代码和数据…

嵌入式Linux应用开发-面向对象-分层-分离及总线驱动模型

嵌入式Linux应用开发-面向对象-分层-分离及总线驱动模型 第八章 驱动设计的思想&#xff1a;面向对象/分层/分离8.1 面向对象8.2 分层8.3 分离8.4 写示例代码8.5 课后作业 第九章 驱动进化之路&#xff1a;总线设备驱动模型9.1 驱动编写的 3种方法9.1.1 传统写法9.1.2 总线设备…

BUUCTF reverse wp 21 - 30

[ACTF新生赛2020]rome 无壳, 直接拖进IDA32 y键把v2改成char[49], n键重命名为iuput int func() {int result; // eaxint v1[4]; // [esp14h] [ebp-44h]char input[49]; // [esp24h] [ebp-34h] BYREFstrcpy(&input[23], "Qsw3sj_lz4_Ujwl");printf("Please…