Go Runtime(一):运行时架构与程序启动

本文源码基线为 Go 1.26.2、Linux/amd64。源码链接固定到 go1.26.2 tag;其他操作系统、架构和构建模式的入口汇编会不同。 一个 Go 程序可以只有几行代码,但它的第一条指令并不属于 main.main。在用户函数开始前,可执行文件必须先建立线程本地状态、调度资源、栈和堆等运行环境。 本文先从 ELF 入口这个可观察事实出发,再沿 Linux/amd64 启动链追踪到 main.main。重点不是背诵初始化函数,而是回答:每一步补上了什么运行前提,编译器、链接器、runtime 和操作系统各自负责哪一段。 一个最小Go程序 以下程序表面上只有 goroutine、Channel 和一次函数返回: GOpackage main func main() { ch := make(chan int) go func() { ch <- 1 }() println(<-ch) } 这段程序的启动和运行依次经过编译、链接与操作系统装载。执行期间,runtime 把操作系统提供的线程、虚拟内存、信号和 I/O 事件转换成 Go 的调度、栈、堆和阻塞语义: TEXTGo源码 -> 编译器:机器码 + 类型/栈图/init task 等元数据 -> 链接器:用户包 + 标准库 + runtime -> ELF可执行文件 -> OS loader:建立初始线程和进程地址空间,跳到ELF入口 -> runtime启动:rt0 -> schedinit -> runtime.main | +-- 执行:G / M / P / runq +-- 栈:stack check / morestack / copystack +-- 内存:mcache / mcentral / mheap / GC +-- 等待:gopark / goready / timer / netpoll / syscall -> package init -> main.main 这里没有一个独立安装、解释字节码的“Go 虚拟机”:CPU 执行的是提前编译并链接好的本地机器码,runtime 也是可执行文件的一部分。标准库提供稳定 API,并在内部接入 runtime,例如 net.Conn 通过 internal/poll 使用 netpoll,sync 通过 runtime 等待设施挂起和唤醒 G。 ...

August 18, 2026

Go Runtime系列导读

一次 Go 程序执行会经过编译器、链接器、标准库、runtime 与操作系统。下面从一段程序开始,依次展开各层的职责与协作关系。 一个程序的执行路径 下面的程序把本系列的主要公共抽象放进同一条调用链,并由每个语句引出后续的系统问题: GOpackage main import ( "context" "fmt" "net" "time" ) func main() { ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() result := make(chan error, 1) go func() { var d net.Dialer conn, err := d.DialContext(ctx, "tcp", "127.0.0.1:8080") if err == nil { err = conn.Close() } result <- err }() select { case err := <-result: fmt.Println(err) case <-ctx.Done(): fmt.Println(ctx.Err()) } } 从这段程序及其运行环境,可以继续追问一组系统问题: ...

August 17, 2026

编译原理

编译原理:从源代码到可执行程序 高级语言中的变量、函数、结构体、接口、模块等概念更接近抽象语义;机器实际执行的是指令、寄存器操作、内存访问和跳转。编译器的核心任务是将高级语言程序转换为机器可执行的目标形式,并在这个过程中完成语法约束、类型约束、控制流约束、内存布局和目标平台约束的处理。 典型编译流程可以抽象为: TEXT源代码 -> 词法分析:字符流 -> Token 流 -> 语法分析:Token 流 -> AST -> 语义分析:AST -> 带类型、作用域、绑定关系的 AST -> 中间代码生成:AST -> IR -> 代码优化:IR -> 优化后的 IR -> 目标代码生成:IR -> 汇编 / 机器指令 -> 汇编与链接:目标文件 -> 可执行文件 这条流水线的关键不是“把文本翻译成另一段文本”,而是不断把程序转换为更适合下一阶段处理的结构化表示。Token 适合语法分析,AST 适合表达源代码结构,IR 适合数据流和控制流优化,机器指令适合在具体硬件上执行。 编译过程概览 以一段简单的 Go 代码为例: GOpackage main func add(a, b int) int { return a + b } 在编译器内部,它会经历类似下面的结构转换: ...

June 20, 2026

Go

draft need to be improved Slice GOtype slice struct { array unsafe.Pointer len int cap int } // 扩容 func nextslicecap(newLen, oldCap int) int { newcap := oldCap doublecap := newcap + newcap if newLen > doublecap { return newLen } const threshold = 256 if oldCap < threshold { return doublecap } for { // 1.25x newcap += (newcap + 3*threshold) >> 2 if uint(newcap) >= uint(newLen) { break } } // overflowed if newcap <= 0 { return newLen } return newcap } Map GOtype Map struct { used uint64 // The number of filled slots seed uintptr // the hash seed, computed as a unique random number per map. // The directory of tables. // Normally dirPtr points to an array of table pointers // dirPtr *[dirLen]*table dirPtr unsafe.Pointer dirLen int // 1 << globalDepth globalDepth uint8 // The number of bits to use in table directory lookups. globalShift uint8 // The number of bits to shift out, 64 - globalDepth writing uint8 // detect the race. tombstonePossible bool // whether a table in this map contains a tombstone. clearSeq uint64 // version number, used to detect map clears during iteration. } Swiss Table refer: Faster Go maps with Swiss Tables ...

March 2, 2026