Go

理解Go程序进程的内存空间布局

2021-10-30
Go

Linux进程内存分布简介 #

在Linux系统中,一个程序进程在内存布局上遵循一定规律,进程的内存空间布局由高地址到低地址大致可分为以下几段:

  • 栈(stack): 用户态的栈,栈的大小是固定的,其大小可以使用ulimit -s查看和调整,一般默认为8Mb,栈从高地址向低地址增长(函数调用)
  • 堆(heap): 动态分配的内存空间,程序在运行时动态分配和释放,堆内存的分配不是连续的,整体上是从低地址向高地址增长
  • bss(未初始化数据区): 未初始化数据区bss, 存放全局的未初始化赋值的变量
  • data(初始化数据区): 存放已经初始化的全局变量数据
  • text: 存放程序代码

memory-layout-c.jpg

...

使用Delve的调试和反汇编功能阅读Go源码

2021-10-28
Go

Delve是一个专门用于Go语言的调试器,之前整理过Delve的基本使用,使用Delve调试Go程序。 本文进一步介绍如何使用Delve阅读Go的源码并找到一些语法在Go的内部是如何实现的。这里将一下在Go里创建channel的语法为例make(chan int, 100),演示如何使用Delve如何找到make chan在Go内部的源码实现以及channel具体的数据结构是什么样的。

...

使用Delve调试Go程序

2021-10-20
Go

Delve是一个使用Go语言开发的专门用于Go语言的调试工具。Delve项目的地址是https://github.com/go-delve/delve, 这个项目的目标是为Go提供一个简单的、功能齐全、易于调用和使用的调试器。当前Go语言支持GDB、LLDB、Delve三种调试工具,LLDB是MacOS系统推荐的,已经成为了XCode默认调试器,但GDB和LLDB对Go的支持比不上Delve这个专门为Go设计和开发的调试工具。 GolandGo for Visual Studio Code这种集成开发环境的调试功能也都是集成了delve的。 本文将学习Delve命令行的基本使用。

...

Go 1.15新特性学习笔记

2020-08-16
Go

Go 1.15继1.14 6个月后发布,主要是在工具链、运行时和类库上的改进,改版本继续保持对Go 1.x版本的兼容性。

Go 1.15值得关注的变化 #

Go 1.15中一些值得关注的地方:

  • 对Go链接器的实质性改进
  • 改进了对高核心计数的小对象的分配
  • X.509 CommonName弃用
  • GOPROXY支持跳过返回错误的代理
  • 新增了一个time/tzdata
  • 核心库的一些改进

从这些变化上可以看出,这个版本变化不太大,其实在工具链和运行时上也不需要过多关注,直观的体验是编译的二进制文件大小确实比1.14小了一点点。

...

理解Go语言struct的内存对齐

2020-08-10
Go

1.为什么要内存对齐? #

先来看一下字长的概念。字长是CPU的主要技术指标之一,指的是CPU一次能并行处理的二进制位数,通常CPU的字长为4字节(32位)、8字节(64位)。 CPU在访问内存时,并不是逐个字节访问的,而是以字长为单位访问。这么做的目的是为了减少CPU访问内存的次数,例如对于64位CPU,一次读取16字节数据只需要读取2次。

...

理解Go Modules

2019-03-12
Go

Go Modules是Go 1.11引入的,为了解决Go项目的依赖问题。从go 1.16开始,go modules开始成为Go默认的包依赖管理工具。

go moudle是一组go package的集合 #

一个Go Module实际上是由一组Go的Package的集合。 构建Go Module的过程需要先确定依赖的其他Go Module的版本、编译包、将编译的目标文件链接到一起。

...

Goroutine调度器学习笔记

2019-02-12
Go

三种多线程模型 #

操作系统的线程由内核的线程调度器负责调度,不同的编程语言对内核线程的封装不同,有以下三个模型:

  • N:1模型: N个用户空间线程运行在1个内核空间线程上。优点是上下文切换快,缺点是无法有效利用多核特性。 在该模型下,线程管理由用户空间的线程库进行,因此上下文切换效率高,但如果某个用户线程执行阻塞系统调用(syscall),其他用户线程也将会阻塞。因为任一时间只能有一个内核空间线程访问内核,所以多个用户线程也就无法并行运行在多核系统上。 由于无法有效利用系统多核特性,所以现在几乎没有编程语言使用这个模型。
  • 1:1模型: 1个用户空间线程运行在1个内核空间线程上。每次调度都要在用户态和内核态之间切换,因此该模型的缺点是上下文切换很慢,而且每创建一个用户线程就需要创建一个内核线程,因此可以创建的用户线程数量也会受限制。 在该模型下,一个用户线程执行阻塞系统调用时,能够允许其他用户线程继续执行,多个用户线程也可以并行的运行在多核系统上。使用此模型的编程语言有Java。
  • M:N模型: 多个用户空间线程运行在多个内核空间线程上。M:N模型综合了N:11:1模型的优点,多路复用多个用户空间线程到一定数量的内核空间线程。这种模型的缺点是调度实现起来十分复杂。Go采用的这种模型,用户空间线程在Go中对应的是Goroutine。

Goroutine调度器和G-M-P模型 #

在Go中用户线程的调度和生命周期管理都是用户层面的,由Go语言自己实现,不依赖OS系统调用,减少系统资源消耗。 Go实现了一个自己的运行时调度器用于调度Goroutine(轻量级的用户空间线程)。

...

Go语言使用常量和iota模拟枚举类型

2019-02-10
Go

模拟枚举类型 #

go语言中虽然没有Java, C++等一些高级语言提供的枚举类型特性,但可以使用常量和iota来模拟。 例如:

 1// BuildResult 构建结果
 2type BuildResult int
 3
 4const (
 5	// Success 成功
 6	Success BuildResult = iota
 7	// Fail 失败
 8	Fail
 9	// Abort 中断
10	Abort
11)
1var buildResult = Success
2println(buildResult) // 0

使用go generate和stringer生成String() string函数 #

为了打印枚举类型值具体的字符串标识,可以使用https://godoc.org/golang.org/x/tools/cmd/stringer工具并结合go generate为枚举类型生成func (t T) String() string函数。这样枚举类型就实现了fmt.Stringer接口。具体的实现方式是在枚举类型BuildResult的定义上加上注释//go:generate stringer -type=BuildResult

...

理解Go interface的两种底层实现:iface和eface

2018-11-06
Go

Go语言interface的运行时实现的源码位于$GOROOT/src/runtime/runtime2.go中。 在Go的不同版本中,interface的实现可能会有不同,但整体结构变化不大,本文基于Go 1.17。

1.两类接口的运行时实现 #

可以在runtime/runtime2.go中找到Go的接口类型变量在运行时的表示,如下是ifaceeface两个结构体:

...

© 2024 青蛙小白