Go runtime 调度器精讲(九):系统调用引起的抢占

前言 第八讲介绍了当 goroutine 运行时间过长会被抢占的情况。这一讲继续看 goroutine 执行系统调用时间过长的抢占。 系统调用时间过长的抢占 看下面的示例: 1func longSyscall() { 2 timeout := syscall.NsecToTimeval(int64(5 * time.Second)) 3 fds := make([]syscall.FdSet, 1) 4 5 if _, err := syscall.Select(0, &fds[0], nil, nil, &timeout); err != nil { 6 fmt.Println("Error:", err) 7 } 8 9 fmt.Println("Select returned after timeout") 10} 11 12func main() { 13 threads := runtime.GOMAXPROCS(0) 14 for i := 0; i < threads; i++ { 15 go longSyscall() 16 } 17 18 time.Sleep(8 * time.Second) 19} longSyscall goroutine 执行一个 5s 的系统调用,在系统调用过程中,sysmon 会监控 longSyscall,发现执行系统调用过长,会对其抢占。 ...

2024-09-16 · xhy

Go runtime 调度器精讲(八):运行时间过长的抢占

前言 在 Go runtime 调度器精讲(七):案例分析 一文我们介绍了一个抢占的案例。从案例分析抢占的实现,并未涉及到源码层面。本文将继续从源码入手,看 Go runtime 调度器是如何实现抢占逻辑的。 ...

2024-09-16 · xhy

Go runtime 调度器精讲(七):案例分析

前言 前面用了六讲介绍 Go runtime 调度器,这一讲我们看一个关于调度 goroutine 的程序案例分析下调度器做了什么。需要说明的是,这个程序和抢占有关,抢占目前为止还没有介绍到,如果看不懂也没有关系,有个印象就行。 ...

2024-09-15 · xhy

Go runtime 调度器精讲(六):非 main goroutine 运行

前言 在 Go runtime 调度器精讲(三):main goroutine 创建 介绍了 main goroutine 的创建,文中我们说 main goroutine 和非 main goroutine 有区别。当时卖了个关子并未往下讲,这一讲我们会继续介绍非 main goroutine (也就是 go 关键字创建的 goroutine,后文统称为 gp) 的运行,并且把这个关子解开,说一说它们的区别在哪儿。 ...

2024-09-14 · xhy

Go runtime 调度器精讲(五):调度策略

前言 在 第四讲 我们介绍了 main goroutine 是如何运行的。其中针对 main goroutine 介绍了调度函数 schedule 是怎么工作的,对于整个调度器的调度策略并没有介绍,这点是不完整的,这一讲会完善调度器的调度策略部分。 ...

2024-09-14 · xhy

Go runtime 调度器精讲(四):运行 main goroutine

前言 皇天不负有心人,终于我们到了运行 main goroutine 环节了。让我们走起来,看看一个 goroutine 到底是怎么运行的。 运行 goroutine 稍微回顾下前面的内容,第一讲 Go 程序初始化,介绍了 Go 程序是怎么进入到 runtime 的,随之揭开 runtime 的面纱。第二讲,介绍了调度器的初始化,要运行 goroutine 调度器是必不可少的,只有调度器准备就绪才能开始工作。第三讲,介绍了 main goroutine 是如何创建出来的,只有创建一个 goroutine 才能开始运行,否则执行代码无从谈起。这一讲,我们继续介绍如何运行 main goroutine。 ...

2024-09-13 · xhy

Go runtime 调度器精讲(三):main goroutine 创建

前言 回顾下 上一讲 的内容。主线程 m0 蓄势待发,准备干活。g0 为 m0 提供了执行环境,P 和 m0 绑定,为 m0 提供活,也就是 goroutine。那么问题来了,活呢?哪里有活给 m0 干? ...

2024-09-13 · xhy

Go runtime 调度器精讲(二):调度器初始化

前言 上一讲 介绍了 Go 程序初始化的过程,这一讲继续往下看,进入调度器的初始化过程。 接着上一讲的执行过程,省略一些不相关的代码,执行到 runtime/asm_amd64.s:rt0_go:343L: 1(dlv) si 2 asm_amd64.s:343 0x45431c* 8b442418 mov eax, dword ptr [rsp+0x18] // [rsp+0x18] 存储的是 argc 的值,eax = argc 3 asm_amd64.s:344 0x454320 890424 mov dword ptr [rsp], eax // 将 argc 移到 rsp,[rsp] = argc 4 asm_amd64.s:345 0x454323 488b442420 mov rax, qword ptr [rsp+0x20] // [rsp+0x20] 存储的是 argv 的值,rax = [rsp+0x20] 5 asm_amd64.s:346 0x454328 4889442408 mov qword ptr [rsp+0x8], rax // 将 argv 移到 [rsp+0x8],[rsp+0x8] = argv 6 asm_amd64.s:347 0x45432d e88e2a0000 call $runtime.args // 调用 runtime.args 处理栈上的 argc 和 argv 7 asm_amd64.s:348 0x454332 e8c9280000 call $runtime.osinit // 调用 runtime.osinit 初始化系统核心数 8 asm_amd64.s:349 0x454337 e8e4290000 call $runtime.schedinit 上述指令调用 runtime.args 处理函数参数,接着调用 runtime.osinit 初始化系统核心数。runtime.osinit 在 runtime.os_linux.go 中定义: ...

2024-09-11 · xhy