Go Stack

go 栈 操作系统进程内存 虚拟内存空间大小 Linux为每个进程维护了一个单独的虚拟地址空间。在32位系统上,该虚拟地址空间大小为2^32Bytes = 4G,其中内核空间1G,在高地址处,用户空间3G,在低地址处;在64位系统上,该虚拟地址空间大小为2^48Bytes = 256TB,其中用户和内核各128TB,用户空间0x0000000000000000~0x00007fffffffffff,内核空间0xffff800000000000~0xffffffffffffffff,中间16M TB暂未用到。 linux查看cpu可访问地址空间位数 cat /proc/cpuinfo | grep address address sizes : 43 bits physical, 48 bits virtual 48 bits virtual表示可访问地址空间位数为48,即256TB。 进程的虚拟内存空间分布 高地址+---------> +------------------+-----------+ | | | | 进程相关数据结构 | | | | | +------------------+ v | | | 物理内存 | 内核区 | | +------------------+ ^ | | | | 内核代码和数据 | | | | | +------------------------------+ |XXXXXXXXXXXXXXXXXX| +------------------------------+ | | | | 用户栈 | | | | | %rsp +--------> +--------+---------+ | | | | | +--------v---------+ | | | | |共享区内存映射区域| | | | | +--------^---------+ | | | | | brk +--------> +--------+---------+ v | | | 运行时堆 | 用户区 | | +------------------+ ^ | | | | 未初始化数据 | | | | | +------------------+ | | | | | 已初始化数据 | | | | | +------------------+ | | | | | 代码 | | | | | +------------------+ | |XXXXXXXXXXXXXXXXXX| | 低地址+---------> +------------------------------> 64位代码段总是从地址0x400000开始的。...

January 26, 2021 · 3 min · Theme PaperMod

Go Func Stack

go函数调用过程分析 对go的简单函数调用过程通过GDB调试的方式分析一下,查看一下函数的栈帧变化情况。 源代码 源代码: package main func add(a, b int) (int, int) { c := 3 c = c + 1 return a + b, b - a } func main() { add(1, 2) } // GOOS=linux GOARCH=amd64 go build -gcflags=all="-N -l" a.go gdb调试开始 进入gdb调试 通过gdb a进入gdb调试 gdb a (gdb) info files Symbols from "/home/qraffa/gopkg/a". Local exec file: `/home/qraffa/gopkg/a', file type elf64-x86-64. Entry point: 0x465860 0x0000000000401000 - 0x0000000000467907 is .text 0x0000000000468000 - 0x00000000004884a6 is ....

January 26, 2021 · 3 min · Theme PaperMod

Go-GMP

goroutine调度 线程实现模型 用户级线程(M:1) 将多个线程放在用户空间中,对应一个系统线程,线程之间由用户空间来进行调度,因此不需要陷入内核状态。但由于内核线程无法感知用户线程,一旦一个用户线程因为某些原因阻塞,则整个进程就阻塞,即使其他线程是可以运行的。由于没有时间中断,因此一个线程开始执行,那么其他线程将无法执行,除非运行的线程主动让出cpu进行新的调度。 内核级线程(1:1) 将一个用户线程对应一个内核线程,线程由内核进行调度。当用户进程的一个线程阻塞时,内核可以调度其他线程来运行。但由于一个用户线程对应一个内核线程,因此线程之间的调度需要进入内核状态,并且用户线程过多的启动新线程,会影响系统对线程的调度性能。linux使用该线程模型。 混合线程(M:N) 将用户线程与内核线程多路复用,一个进程与多个内核线程关联,多个用户线程可以运行在一个内核线程上。内核只需要调度内核线程,用户线程之间的调度由用户空间完成。 x:y表示x个用户线程对应y个内核线程 GMP调度模型 G表示goroutine,M表示machine,P表示processor G G的结构体定义在runtime2.go#L406 type g struct { stack stack // 表示该g的栈内存 m *m // 指向当前m sched gobuf // 调度上下文,用于协程调度上下文的切换 startpc uintptr // 指向groutine的function atomicstatus uint32 // 表示该g的状态 } // 栈内存地址[lo,hi) type stack struct { lo uintptr hi uintptr } G的几个状态 状态 _Gidle G已经被分配,但未被初始化 _Grunnable G已经在执行队列中等待执行 _Grunning G可以执行用户代码,拥有了栈,并且分配了M和P _Gsyscall G在执行系统调用,未在执行用户代码分配了M _Gwaiting G阻塞,未执行用户代码,未处在等待队列中 _Gdead G不再被使用,可能由一个G刚刚退出,或者在空闲队列中,或者是仅仅被初始化 _Gcopystack G正在发生栈复制,未执行用户代码,未处在等待队列中 _Gpreempted G被抢占,需要通过CAS将状态变为_Gwaiting等待唤醒》 每个goroutine都维护着一个自己的栈区,初始大小为2KB,当goroutine运行时,栈空间不足则会触发morestack,因此就会初始化一块两倍大小的栈空间,然后进行栈复制过程,所以这里会将G状态改为_Gcopystack M M对应操作系统中线程,最多只有GOMAXPROCS个线程在活跃执行。...

January 20, 2021 · 13 min · Theme PaperMod

Go Bootstrap

go启动过程 找到程序入口 使用gdb调试程序找到入口 gdb main调试 在gdb中使用info files找到程序入口Entry point: 0x464820 b *0x464820,在入口处设置断点,找到文件位置 ...

January 2, 2021 · 4 min · Theme PaperMod

Golang Map

golang map分析 map 底层结构 get set del 扩容 ...

December 3, 2020 · 11 min · Theme PaperMod