首页 > 其他分享 >利用Gdb探索GoRoutine的奥秘!

利用Gdb探索GoRoutine的奥秘!

时间:2022-12-17 22:11:46浏览次数:68  
标签:sched g0 GoRoutine gobuf Gdb 奥秘 main BX MOVQ

主协程(runtime.main)的 创建

gdb main
info files查看程序入口 在程序入口打断点run
image.png
单步调试
image.png
查看下这两个参数
image.png
image.png
ax = di , bx = si
然后分配32字节的空间 让sp对齐16字节之所以要按16字节对齐,是因为CPU有一组SSE指令,这些指令中出现的内存地址必须是16的倍数,将 两个参数放在栈底

image.png
image.png
di = &g0
然后给g0分配SP - 64*1024 + 104 ~ SP栈空间
g0.g_stackguard0,g0.g_stackguard1 = bx,然后将 stack.lo = bx stack.hi = sp

image.png
接下俩就是检查cpu的信息
di = &m0然后设置 tls,采用寄存器传参数所以di,si...,也就是设置 m0.tls
image.pngimage.pngimage.png
然后检查tls是否设置成功。

#ifdef GOARCH_amd64
#define    get_tls(r) MOVQ TLS, r
#define    g(r)   0(r)(TLS*1)
#endif

image.png

然后bx = fs段基地址``cx = &g0 m0.tls[0] = g0 ax = &m0
image.png
实现m 和 g0双向绑定
image.png

argc,argv再拷贝一份,放入栈顶,现在栈从顶到帝的分布时 spargc,argv,argc,argv sp + 24
image.pngimage.png
argc = 1, argv = image.png
然后 osinit获取ncpu

image.png
我的是 8 核,r =32,然后image.png,然后对buf[:r] 看有多少个 1
(1 << 8 )- 1= 255所以8个核
获取最大页,打开文件读取
image.png
image.png
archInit()
获得 系统信息
image.png
image.png
检查什么的

scheinit() 调度系统初始化
image.png 设置M最大个数
初始化m0以及加入全局m
image.png
image.png分配id
image.png加入全局m链表

P的个数,如果设置了就按照设置的来,没有按照cpu核数
image.png
(func procresize())初始化的时候 allp 为空所以会扩容allp 这段扩容还是很常见的。
image.png
在这一步就初始化了所有的p
image.png
初始化操作包括分配初始deferpoll切片(len = 0,cap =32),然后我们以后都会从buf拿。
image.pngimage.pngimage.png

然后会删除超额的
image.png
image.png
P的安置
image.png

创建runtime.main协程
image.png
image.png
image.png

image.png
什么是systemstack,我将汇编代码贴下来
image.png
image.png

初始化栈空间为2048
image.png
将 sched 就是我们调度所需要的信息
image.png

type gobuf struct {
	// The offsets of sp, pc, and g are known to (hard-coded in) libmach.
	//
	// ctxt is unusual with respect to GC: it may be a
	// heap-allocated funcval, so GC needs to track it, but it
	// needs to be set and cleared from assembly, where it's
	// difficult to have write barriers. However, ctxt is really a
	// saved, live register, and we only ever exchange it between
	// the real register and the gobuf. Hence, we treat it as a
	// root during stack scanning, which means assembly that saves
	// and restores it doesn't need write barriers. It's still
	// typed as a pointer so that any other writes from Go get
	// write barriers.
	sp   uintptr
	pc   uintptr
	g    guintptr
	ctxt unsafe.Pointer
	ret  uintptr
	lr   uintptr
	bp   uintptr // for framepointer-enabled architectures
}

image.png
image.png

准备调度

image.png
image.png
image.png
这里不在引用可以看我另外一篇博客 讲解了 schedule()
这里我们重点看execute 双向绑定 g0 m0
gogo的作用

  1. 把gp.sched的成员恢复到CPU的寄存器完成状态以及栈的切换;
  2. 跳转到gp.sched.pc所指的指令地址(runtime.main)处执行。
// 将 g0.m.curg = g 也就是 m现在执行g
func execute(gp *g, inheritTime bool) {
	_g_ := getg() // g0

	// Assign gp.m before entering _Grunning so running Gs have an
	// M.
	_g_.m.curg = gp // newg
	gp.m = _g_.m // 双向绑定
	...
	gogo(&gp.sched) // 上下文切换
}

TEXT runtime·gogo(SB), NOSPLIT, $16-8
    MOVQ   buf+0(FP), BX    // gobuf,buf = &gp.sched 
    MOVQ   gobuf_g(BX), DX  //DX = gp.sched.g

    //检查gp.sched.g是否是nil,如果是nil进程会crash死掉
    MOVQ   0(DX), CX     // make sure g != nil

    get_tls(CX)

    //把要运行的g的指针放入线程本地存储,这样后面的代码就可以通过线程本地存储
    //获取到当前正在执行的goroutine的g结构体对象,从而找到与之关联的m和p
    MOVQ   DX, g(CX)

    // 把CPU的SP寄存器设置为sched.sp,完成了栈的切换 sp 前压入了 goexit()
    MOVQ   gobuf_sp(BX), SP   // restore SP 
    // 设置恢复调度上下文时需要的寄存器
    MOVQ   gobuf_ret(BX), AX 
    MOVQ   gobuf_ctxt(BX), DX
    MOVQ   gobuf_bp(BX), BP
    // 清空sched的值,因为我们已把相关值放入CPU对应的寄存器了,不再需要,这样做可以少gc的工作量
    MOVQ   $0, gobuf_sp(BX)   // clear to help garbage collector
    MOVQ   $0, gobuf_ret(BX)
    MOVQ   $0, gobuf_ctxt(BX)
    MOVQ   $0, gobuf_bp(BX)

    //把sched.pc值放入BX寄存器 pc = start.pc 原来是 goexit 但是 调用newproc. gostartfn(&sched,fn) 将它复制为 runtime.main

    MOVQ   gobuf_pc(BX), BX
    //JMP把BX寄存器的包含的地址值放入CPU的IP寄存器,于是,CPU跳转到该地址继续执行指令
    JMP BX

runtime.main

  1. 启动一个sysmon系统监控线程,该线程负责整个程序的gc、抢占调度以及netpoll等功能的监控
  2. 执行runtime包的初始化;
  3. 执行main包以及main包import的所有包的初始化;
  4. 执行main.main函数;
  5. main.main函数返回后调用exit系统调用退出进程;(也就是我们的函数哈哈哈,结束了)

非主协程的创建(go func())

go tool compile -+ -L -S main.go > a.asm

package main

import (
    "fmt"
)

func main() {
    go test()
    fmt.Println("Hello World!")
}

func test(){
}

===============================================

0x0028 00040 (main.go:6)        LEAQ    "".test·f(SB), AX
0x002f 00047 (main.go:6)        MOVQ    AX, 8(SP)
0x0034 00052 (main.go:6)        PCDATA  $0, $0
0x0034 00052 (main.go:6)        PCDATA  $1, $0
0x0034 00052 (main.go:6)        CALL    runtime.newproc(SB)

返现会将 ax = test , 放入栈后,然后调用 newproc
gcenable()和doInit()都会开启goroutine前期会开启3个,然后再次c就会回到我们的代码里面。
image.png
会发生一次 g->g0栈的切换
image.png

引用连接

  1. https://juejin.cn/post/6986566386176753694#heading-11

标签:sched,g0,GoRoutine,gobuf,Gdb,奥秘,main,BX,MOVQ
From: https://www.cnblogs.com/jgjg/p/16989637.html

相关文章

  • gdb mi接口命令入门大全
    入门的话,先看看我这两个博客​​gdb调试常用概念整理_标biao的博客​​关于调试器和IDE的一些认识_标biao的博客gdbmi简介gdbmi接口协议有3个版本 ​​GDB/MIDevelopmen......
  • 使用qemu搭建ARM64调试环境,支持文件共享,支持gdb调试
    环境主机ubuntu版本:20.04qemu模拟处理器:ARM64Linux内核版本:https://www.kernel.org/最新版step1:安装编译工具链sudoapt-getinstallgcc-aarch64-linux-gnusudoapt-......
  • Linux GDB Debugging
    LinuxGDBDebuggingCatalog1.GDBIntroduction2.GDB基本命令 1.GDBIntroductionGDB是GNU开源组织发布的一个强大的UNIX下的程序调试工具,GDB主......
  • [linux下c++ debug神器]vscode+gdb(debug console下使用)
    不推荐直接用gdb推荐用vscode+gdb(debugconsole下使用)二者可以优势互补:vscode为gdb提供图形化显示,gdb为vscode提供任意的内存可视化和汇编语句显示。经测试,GDB下命......
  • STM32开发之 VS Code + GDB下载调试
    写在前面:本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。在完成上一篇的ST......
  • VSCode GDB调试配置
    VSCodeGDB调试配置1.vscode启动debug窗口按Ctrl+Shift+D,打开Debug窗口默认是“Noconfigurations”,点击“F5”,会提示你配置GDB参数(选择gccbuildanddebugactive......
  • golang的chan用法与fatal error: all goroutines are asleep - deadlock!
    例子1:funcmain(){ch:=make(chanint)ch<-1fmt.Println("发送成功")}上面这段代码能够通过编译,但是执行的时候会出现以下错误:为什么会出现deadlock错误呢?......
  • vscode+gdbserver 开发板调试
    参考嵌入式gdb+gdbserver调试环境搭建与使用参考嵌入式VSCode+gdbserver图形化调试环境搭建与使用参考VSCode+gdbserver嵌入式arm远程调试参考stepbystep使用g......
  • 使用 VSCode 远程 图形化 GDB 调试 嵌入式linux
     目录标题引言环境步骤vscoderemotessh配置Linux编译机配置设备板端配置vscodelaunch设置调试引言之前说了,通过coredump找程序bug,但是有些时候......
  • 8.Go语言编程快速入门学习之并发(Goroutine)和通道(Channel)
    本章目录:0x01Go语言基础之并发和通道1.基础概念2.Goroutine入门示例1.启动单个Goroutine示例2.启动多个Goroutine3.Goroutine特性(1)Goroutine可增长的栈(2)Goroutin......