May 21, 2021
认识无锁队列
"无锁队列是 lock-free 中最基本的数据结构,一般应用在需要一款高性能队列的场景下。\n对于多线程用户来说,无锁队列的入队和出队操作是线程安全的,不用再加锁控制\n什么是无锁队列 队列每个开发者都知道,那么什么又是无锁队列呢?字面理解起来就是一个无锁状态的队列,多个线程(消费者)同时操作数据的时候不需要加锁,因为加/解锁都是一个很消耗资源的动作。\n实现原理 我们先看一下无锁队列的底层实现数据结构。\n数据结构 无锁队列底层的数据结构实现方式主要有两种:数组 和 链接。\n数组 在首次初始化时,需要申请一块连接的大的内存。读写数据直接从数据的指定位置操作即可,时间复杂度为O(1)。\n缺点:数组长度有限,一旦数组索引位置写满,则无法继续写入,即队列有上限。\n链表 不用像数组一样,刚开始就申请一块连接的大的内存空间。只有在每次写时数据的时候,申请这个数据节点大小的内存即可,这样就可以实现无限的写入,没有长度限制问题。\n缺点:每次写数据都要申请内存,在写的场景,最差的情况是多少个数据就申请多少次内存,而每次申请都是一个消耗资源的动作。\n可以看到无锁底层的实现的不同各有优势。多数据情况下,我们都采 …"
May 10, 2021
Runtime: goroutine的暂停和恢复源码剖析
"上一节《 GC 对根对象扫描实现的源码分析》中,我们提到过在GC的时候,在对一些goroutine 栈进行扫描时,会在其扫描前触发 G 的暂停([suspendG](https://github.com/golang/go/blob/go1.16.2/src/runtime/preempt.go#L76-L254))和恢复([resumeG](https://github.com/golang/go/blob/go1.16.2/src/runtime/preempt.go#L256-L280))。\n// markroot scans the i\u0026#39;th root. // // Preemption must be disabled (because this uses a gcWork). // // nowritebarrier is only advisory here. // //go:nowritebarrier func markroot(gcw *gcWork, i uint32) { baseFlushCache := uint32(fixedRootCount) …"
May 7, 2021
goroutine栈的申请与释放
"对于提高对 stack 的使用效率,避免重复从heap中分配与释放,对其使用了 pool 的概念,runtime 里为共提供了两个pool, 分别为 stackpool ,另一个为 stackLarge。stack pool\nstackpool: 16b~32k 对应通用的大小的stack。获取时通过调用 stackpoolalloc(), 释放时调用 stackpoolfree()。\nstackLarge:对应 \u0026gt; 32K 的 stack\n在程序全局调度器 初始化 时会通过调用 stackinit() 实现对 stack 初始化。\n当我们执行一个 go func() 语句的时候,runtime 会通过调用 newproc() 函数来创建G。而内部真正创建G的函数为 [newproc1()](https://github.com/golang/go/blob/go1.16.3/src/runtime/proc.go#L3990-L4098),在没有G可以复用的情况下,会通过 newg = malg(_StackMin) 语句创建一个包含stack的G。\n// Allocate a …"
May 7, 2021
Golang的GPM 模型在网络编程中存在的问题
"现状 目前在网络编程中,golang采用的是一种 goroutine-per-connection 的模式,即为每一个连接都分配一个goroutine,一个连接就是一个goroutine,多个连接之间没有关系。\npackage main import ( \u0026#34;fmt\u0026#34; \u0026#34;io/ioutil\u0026#34; \u0026#34;net\u0026#34; \u0026#34;time\u0026#34; ) //模拟server端 func main() { tcpServer, _ := net.ResolveTCPAddr(\u0026#34;tcp4\u0026#34;, \u0026#34;:8080\u0026#34;) listener, _ := net.ListenTCP(\u0026#34;tcp\u0026#34;, tcpServer) for { //当有新客户端请求时拿到与客户端的连接 conn, err := listener.Accept() if err != nil { fmt.Println(err) continue } // 处理逻辑 goroutine-per-connection go handle(conn) } } …"
May 1, 2021
Linux 内核select、poll 和 eventpoll 的实现
"Linux 内核仓库 https://github.com/torvalds/linux\nLinux 内核文档: https://www.kernel.org/doc/html/latest/index.html( 中文)\n开发工具参考: https://www.kernel.org/doc/html/latest/dev-tools/index.html\n也可以使用 VSCode + 插件C/C++ GNU Global\n通过前面三个博客可以得知 select,** poll, eventpoll** 的详细实现,现在来总结对比下它们之间的不同:\nselect 流程图 poll 流程图 eventpoll 流程图 优缺点总结 \u0026lt;1\u0026gt; 监控文件最大数不同:select和poll都是以数组形式传入药监控的文件句柄,而这个数组是有大小限制的1024个左右(不是很清楚).而epoll则是每add一个文件句柄会new一个新epi出来,挂载在ep的红黑树中,监控的文件个数没有明确限制(可能会受限于系统最大打开文件句柄数)从这点上看,epoll是优于select和poll. …"
April 30, 2021
缓存池 bytebufferpool 库实现原理
"上一节 《Runtime: Golang 之 sync.Pool 源码分析》 我们介绍了sync.Pool 的源码分析,本节介绍一个 fasthttp 中引用的一缓存池库 [bytebufferpool](https://github.com/valyala/bytebufferpool),这两个库是同一个开发者。对于这个缓存池库与同类型的几个库的对比,可以参考 https://omgnull.github.io/go-benchmark/buffer/。\n建议大家了解一下[fasthttp](https://github.com/valyala/fasthttp) 这个库,性能要比直接使用内置的 net/http 高出很多,其主要原因是大量的用到了缓存池 sync.Pool 进行性能提升。\n用法 // https://github.com/valyala/bytebufferpool/blob/18533face0/bytebuffer_example_test.go package bytebufferpool_test import ( \u0026#34;fmt\u0026#34; …"
April 29, 2021
初识kubernetes 组件
"对于一个刚刚接触 kubernetes(k8s)的新手来说,想好更好的学习它,首先就要对它有一个大概的认知,所以本文我们先以全局观来介绍一个 kubernetes。\nkubernetes 架构 kubernetes 架构图 kubernets 整体可以分为两大部分,分别为 Master 和 Node ,我们一般称其为节点,这两种角色分别对应着控制节点和计算节点,根据我们的经验可以清楚的知道 Master 是控制节点。\nMaster 节点 控制节点 Master 节点由三部分组成,分别为 Controller Manager 、 API Server 和 Scheduler ,它们相互紧密协作,每个部分负责不同的工作职责。\ncontroller-manager 全称为 kube-controler-manager 组件,主要用来负责容器编排。如一个容器(实际上是 pod,pod 是最基本的调度单元。一般一个 pod 里会部署一个容器服务)服务可以指定副本数量,如果实际运行的副本数据与期望的不一致,则会自动再启动几个容器副本,最终实现期望的数量。这个组件,就是一系列控制器的集合。我们可以查 …"
April 26, 2021
docker如何利用cgroup对容器资源进行限制
"在容器里有两个非常重要的概念,一个是 namespace 用来实现对容器里所有进程进行隔离;另一个就是 cgroup,用来对容器进程内使用资源进行限制。那 cgroup 又是如何实现对资源进行限制的呢,今天我们来了解一下它的实现原理。\n什么是cgroup cgroup 是 Control Groups 的缩写,是 Linux 内核提供的一种可以限制、记录、隔离 进程组 所使用的物理资源(如 cpu、memory、磁盘IO等等) 的机制,被 LXC、docker 等很多项目用于实现进程资源控制。cgroup 是将任意进程进行分组化管理的 Linux 内核功能。 cgroup 本身是提供将进程进行分组化管理的功能和接口的基础结构,I/O 或内存的分配控制等具体的资源管理功能是通过这个功能来实现的。 一定要切记,这里的限制单元为 进程组,而不是进程。\n子系统 上面提到的具体的资源管理功能统称为 cgroup 子系统,所有子系统列表可以通过 cat /proc/cgroups 命令查看,主要有以下几大子系统:\n# cat /proc/cgroups #subsys_name\thierarchy …"
April 12, 2021
Golang 内存组件之mspan、mcache、mcentral 和 mheap 数据结构
"Golang中的内存组件关系如下图所示golang 内存分配组件\n在学习golang 内存时,经常会涉及几个重要的数据结构,如果不熟悉它们的情况下,理解起来就显得格外的吃力,所以本篇主要对相关的几个内存组件做下数据结构的介绍。\n在 Golang 中,mcache、mspan、mcentral 和 mheap 是内存管理的四大组件,mcache 管理线程在本地缓存的 mspan,而 mcentral 管理着全局的 mspan 为所有 mcache 提供所有线程。\n根据分配对象的大小,内部会使用不同的内存分配机制,详细参考函数 mallocgo() ,所于内存分配与回收,参考文件介绍 malloc.go\n\u0026lt;16KB 会使用微小对象内存分配器从 P 中的 mcache 分配,主要使用 mcache.tinyXXX 这类的字段 16-32KB 从 P 中的 mcache 中分配 \u0026gt;32KB 直接从 mheap 中分配 对于golang中的内存申请流程,大家应该都非常熟悉了,这里不再进行详细描述。Golang 内存组件关系\nmcache 在GPM关系中,会在每个 P …"
April 9, 2021
GC 对根对象扫描实现的源码分析
"工作池gcWork 工作缓存池(work pool)实现了生产者和消费者模型,用于指向灰色对象。一个灰色对象在工作队列中被扫描标记,一个黑色对象表示已被标记不在队列中。\n写屏障、根发现、栈扫描和对象扫描都会生成一个指向灰色对象的指针。扫描消费时会指向这个灰色对象,从而将先其变为黑色,再扫描它们,此时可能会产生一个新的指针指向灰色对象。这个就是三色标记法的基本知识点,应该很好理解。\ngcWork 是为垃圾回收器提供的一个生产和消费工作接口。\n它可以用在stack上,如\n(preemption must be disabled) gcw := \u0026amp;getg().m.p.ptr().gcw .. call gcw.put() to produce and gcw.tryGet() to consume .. 在标记阶段使用gcWork可以防止垃圾收集器转换到标记终止,这一点很重要,因为gcWork可能在本地持有GC工作缓冲区。可以通过禁用抢占(systemstack 或 acquirem)来实现。\n数据结构\ntype gcWork struct { wbuf1, wbuf2 …"
April 7, 2021
Runtime: Golang GC源码分析
"在阅读此文前,需要先了解一下三色标记法以及混合写屏障这些概念。\n源文件 [src/runtime/mgc.go](https://github.com/golang/go/blob/go1.16/src/runtime/mgc.go) 版本 1.16.2。\n基本知识 在介绍GC之前,我们需要认识有些与GC相关的基本信息,如GC的状态、模式、统计信息等。\n三种状态 共有三种状态\nconst ( _GCoff = iota // GC not running; sweeping in background, write barrier disabled _GCmark // GC marking roots and workbufs: allocate black, write barrier ENABLED _GCmarktermination // GC mark termination: allocate black, P\u0026#39;s help GC, write barrier ENABLED ) _GCoff GC未运行 _GCmark 标记中, …"
April 6, 2021
Golang中的切片与GC
"今天再看 timer 源码的时候,在函数 [clearDeletedTimers()](https://github.com/golang/go/blob/go1.16.2/src/runtime/time.go#L904-L992) 里看到一段对切片的处理代码,实现目的就是对一个切片内容进行缩容。\n// src/runtime/time.go // The caller must have locked the timers for pp. func clearDeletedTimers(pp *p) { timers := pp.timers ...... // 对无用的切片元素赋值 nil for i := to; i \u0026lt; len(timers); i++ { timers[i] = nil } atomic.Xadd(\u0026amp;pp.deletedTimers, -cdel) atomic.Xadd(\u0026amp;pp.numTimers, -cdel) atomic.Xadd(\u0026amp;pp.adjustTimers, -cearlier) timers = …"
March 29, 2021
Runtime: Golang 定时器实现原理及源码解析
"定时器作为开发经常使用的一种数据类型,是每个开发者需要掌握的,对于一个高级开发很有必要了解它的实现原理,今天我们runtime源码来学习一下它的底层实现。\n定时器分两种,分别为 Timer 和 Ticker,两者差不多,这里重点以Timer为例。\n源文件位于 [src/time/sleep.go](https://github.com/golang/go/blob/go1.16.2/src/time/sleep.go) 和 [src/time/tick.go](https://github.com/golang/go/blob/go1.16.2/src/time/tick.go) 。 go version 1.16.2\n数据结构 Timer 数据结构\n// src/runtime/sleep.go // The Timer type represents a single event. // When the Timer expires, the current time will be sent on C, // unless the Timer was created by …"
March 28, 2021
Golang中的CAS原子操作 和 锁
"在高并发编程中,经常会出现对同一个资源并发访问修改的情况,为了保证最终结果的正确性,一般会使用 锁 和 CAS原子操作 来实现。\n如要对一个变量进行计数统计,两种实现方式分别为\npackage main import ( \u0026#34;fmt\u0026#34; \u0026#34;sync\u0026#34; ) // 锁实现方式 func main() { var count int64 var wg sync.WaitGroup var mu sync.Mutex for i := 0; i \u0026lt; 10000; i++ { wg.Add(1) go func(wg *sync.WaitGroup) { defer wg.Done() mu.Lock() count = count + 1 mu.Unlock() }(\u0026amp;wg) } wg.Wait() // count = 10000 fmt.Println(\u0026#34;count = \u0026#34;, count) } 与\npackage main import ( \u0026#34;fmt\u0026#34; \u0026#34;sync\u0026#34; …"
March 23, 2021
Golang并发同步原语之-信号量Semaphore
"信号量是并发编程中比较常见的一种同步机制,它会保持资源计数器一直在0-N(N表示权重值大小,在用户初始化时指定)之间。当用户获取的时候会减少一点,使用完毕后再恢复过来。当遇到请求时资源不够的情况下,将会进入休眠状态以等待其它进程释放资源。\n在 Golang 官方扩展库中为我们提供了一个基于权重的信号量 [semaphore](https://github.com/golang/sync/blob/master/semaphore/semaphore.go) 并发原语。\n你可以将下面的参数 n 理解为资源权重总和,表示每次获取时的权重;也可以理解为资源数量,表示每次获取时必须一次性获取的资源数量。为了理解方便,这里直接将其理解为资源数量。\n数据结构 [semaphoreWeighted](https://github.com/golang/sync/blob/master/semaphore/semaphore.go#L19-L33) 结构体\ntype waiter struct { n int64 ready chan\u0026lt;- struct{} // Closed when …"