青蛙小白

Go源码学习: channel底层的数据结构是什么, 是如何被创建的

channel是Go语言核心数据结构之一。Go提供了CSP(通信顺序进程Communicating sequential processes)的并发模型,关于Go并发的那句经典的谚语不要通过共享内存的方式进行通信,而是应该通过通信的方式共享内存,goroutine之间的通信通过channel来完成。 本节将通过阅读源码(Go 1.17.2)了解Go语言中channel的数据结构,以及channel是如何被创建的。

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

Linux进程内存分布简介 在Linux系统中,一个程序进程在内存布局上遵循一定规律,进程的内存空间布局由高地址到低地址大致可分为以下几段: 栈(stack): 用户态的栈,栈的大小是固定的,其大小可以使用ulimit -s查看和调整,一般默认为8Mb,栈从高地址向低地址增长(函数调用) 堆(heap): 动态分配的内存空间,程序在运行时动态分配和释放,堆内存的分配不是连续的,整体上是从低地址向高地址增长 bss(未初始化数据区): 未初始化数据区bss, 存放全局的未初始化赋值的变量 data(初始化数据区): 存放已经初始化的全局变量数据 text: 存放程序代码 memory-layout-c.jpg

使用Delve调试Go程序

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

Go 1.15新特性学习笔记

Go 1.15继1.14 6个月后发布,主要是在工具链、运行时和类库上的改进,改版本继续保持对Go 1.x版本的兼容性。 Go 1.15值得关注的变化 Go 1.15中一些值得关注的地方: 对Go链接器的实质性改进 改进了对高核心计数的小对象的分配 X.509 CommonName弃用 GOPROXY支持跳过返回错误的代理 新增了一个time/tzdata包 核心库的一些改进 从这些变化上可以看出,这个版本变化不太大,其实在工具链和运行时上也不需要过多关注,直观的体验是编译的二进制文件大小确实比1.14小了一点点。

Goroutine调度器学习笔记

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

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

模拟枚举类型 go语言中虽然没有Java, C++等一些高级语言提供的枚举类型特性,但可以使用常量和iota来模拟。 例如: // BuildResult 构建结果 type BuildResult int const ( // Success 成功 Success BuildResult = iota // Fail 失败 Fail // Abort 中断 Abort ) var buildResult = Success println(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: