From faaf1761fdb069b0e5056c6efadab313ec5f11eb Mon Sep 17 00:00:00 2001 From: Xargin Date: Wed, 16 Oct 2019 18:55:11 +0800 Subject: [PATCH 001/120] add readme for v2ray --- v2ray/readme.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 v2ray/readme.md diff --git a/v2ray/readme.md b/v2ray/readme.md new file mode 100644 index 0000000..b716447 --- /dev/null +++ b/v2ray/readme.md @@ -0,0 +1,4 @@ +# v2ray 源码分析 + +分析 v2ray 的实现,虽然代码写的不怎么样。 + From 31c8db155fddbe599ae15c7d59fbf684ed882d23 Mon Sep 17 00:00:00 2001 From: theoneLee <623507020@qq.com> Date: Tue, 31 Dec 2019 17:29:07 +0800 Subject: [PATCH 002/120] Update slice.md --- slice.md | 1 + 1 file changed, 1 insertion(+) diff --git a/slice.md b/slice.md index 0afb29f..1e32544 100644 --- a/slice.md +++ b/slice.md @@ -231,6 +231,7 @@ var arr = make([]int, 0, 10) ```go func main() { var a = make([]int, 10) + doSomeHappyThings(a) fmt.Println(a) } From 3cc997328385815d137bdd78161420ae6a4029c4 Mon Sep 17 00:00:00 2001 From: Xargin Date: Wed, 1 Jan 2020 19:04:21 +0800 Subject: [PATCH 003/120] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=20map=20=E4=B8=80?= =?UTF-8?q?=E8=8A=82=E4=B8=AD=E4=B8=8D=E7=B2=BE=E7=A1=AE=E7=9A=84=E8=A1=A8?= =?UTF-8?q?=E8=BF=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- map.md | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/map.md b/map.md index 9800e13..5c98666 100644 --- a/map.md +++ b/map.md @@ -163,7 +163,7 @@ type bmap struct { make(map[k]v, hint) ``` -的代码,在 hint <= 7 时,会调用 makemap_small 来进行初始化,如果 hint > 7,则调用 makemap。 +的代码,在 hint <= 8(bucketSize) 时,会调用 makemap_small 来进行初始化,如果 hint > 8,则调用 makemap。 ```go make(map[k]v) @@ -217,6 +217,28 @@ func makemap(t *maptype, hint int, h *hmap) *hmap { } ``` +当然,实际选用哪个函数不只要看 hint,还要看逃逸分析结果,比如下面这段代码,在生成的汇编中,你是找不到 makemap 的踪影的: + +```go +package main + +func main() { + var m = make(map[int]int, 4) + m[1] = 1 +} +``` + +编译器确定 make map 函数的位置在:cmd/compile/internal/gc/walk.go:1192: + +```go + case OMAKEMAP: + t := n.Type + hmapType := hmap(t) + hint := n.Left +``` + +有兴趣的同学可以自行查看~ + ## 元素访问 主要是对 key 进行 hash 计算,计算后用 low bits 和高 8 位 hash 找到对应的位置: From 9fcd90c2de13cb1cd2029a174473678a7f499772 Mon Sep 17 00:00:00 2001 From: Xargin Date: Tue, 14 Jan 2020 15:53:29 +0800 Subject: [PATCH 004/120] add 1.14 timer --- 1.13/memory.md | 0 1.13/rwmutex.md | 105 ----- 1.13/select.md | 0 1.13/sync_pool.md | 254 ------------ 1.14/timer.md | 998 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 998 insertions(+), 359 deletions(-) delete mode 100644 1.13/memory.md delete mode 100644 1.13/rwmutex.md delete mode 100644 1.13/select.md delete mode 100644 1.13/sync_pool.md create mode 100644 1.14/timer.md diff --git a/1.13/memory.md b/1.13/memory.md deleted file mode 100644 index e69de29..0000000 diff --git a/1.13/rwmutex.md b/1.13/rwmutex.md deleted file mode 100644 index 210ee95..0000000 --- a/1.13/rwmutex.md +++ /dev/null @@ -1,105 +0,0 @@ -// There is a modified copy of this file in runtime/rwmutex.go. -// If you make any changes here, see if you should make them there. - -// A RWMutex is a reader/writer mutual exclusion lock. -// The lock can be held by an arbitrary number of readers or a single writer. -// The zero value for a RWMutex is an unlocked mutex. -// -// A RWMutex must not be copied after first use. -// -// If a goroutine holds a RWMutex for reading and another goroutine might -// call Lock, no goroutine should expect to be able to acquire a read lock -// until the initial read lock is released. In particular, this prohibits -// recursive read locking. This is to ensure that the lock eventually becomes -// available; a blocked Lock call excludes new readers from acquiring the -// lock. -type RWMutex struct { - w Mutex // held if there are pending writers - writerSem uint32 // semaphore for writers to wait for completing readers - readerSem uint32 // semaphore for readers to wait for completing writers - readerCount int32 // number of pending readers - readerWait int32 // number of departing readers -} - -const rwmutexMaxReaders = 1 << 30 - -// RLock locks rw for reading. -// -// It should not be used for recursive read locking; a blocked Lock -// call excludes new readers from acquiring the lock. See the -// documentation on the RWMutex type. -func (rw *RWMutex) RLock() { - if atomic.AddInt32(&rw.readerCount, 1) < 0 { - // A writer is pending, wait for it. - runtime_SemacquireMutex(&rw.readerSem, false, 0) - } -} - -// RUnlock undoes a single RLock call; -// it does not affect other simultaneous readers. -// It is a run-time error if rw is not locked for reading -// on entry to RUnlock. -func (rw *RWMutex) RUnlock() { - if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 { - // Outlined slow-path to allow the fast-path to be inlined - rw.rUnlockSlow(r) - } -} - -func (rw *RWMutex) rUnlockSlow(r int32) { - if r+1 == 0 || r+1 == -rwmutexMaxReaders { - throw("sync: RUnlock of unlocked RWMutex") - } - // A writer is pending. - if atomic.AddInt32(&rw.readerWait, -1) == 0 { - // The last reader unblocks the writer. - runtime_Semrelease(&rw.writerSem, false, 1) - } -} - -// Lock locks rw for writing. -// If the lock is already locked for reading or writing, -// Lock blocks until the lock is available. -func (rw *RWMutex) Lock() { - // First, resolve competition with other writers. - rw.w.Lock() - // Announce to readers there is a pending writer. - r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders - // Wait for active readers. - if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 { - runtime_SemacquireMutex(&rw.writerSem, false, 0) - } -} - -// Unlock unlocks rw for writing. It is a run-time error if rw is -// not locked for writing on entry to Unlock. -// -// As with Mutexes, a locked RWMutex is not associated with a particular -// goroutine. One goroutine may RLock (Lock) a RWMutex and then -// arrange for another goroutine to RUnlock (Unlock) it. -func (rw *RWMutex) Unlock() { - - // Announce to readers there is no active writer. - r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders) - if r >= rwmutexMaxReaders { - throw("sync: Unlock of unlocked RWMutex") - } - // Unblock blocked readers, if any. - for i := 0; i < int(r); i++ { - runtime_Semrelease(&rw.readerSem, false, 0) - } - // Allow other writers to proceed. - rw.w.Unlock() -} - -// RLocker returns a Locker interface that implements -// the Lock and Unlock methods by calling rw.RLock and rw.RUnlock. -func (rw *RWMutex) RLocker() Locker { - return (*rlocker)(rw) -} - -type rlocker RWMutex - -func (r *rlocker) Lock() { (*RWMutex)(r).RLock() } -func (r *rlocker) Unlock() { (*RWMutex)(r).RUnlock() } - diff --git a/1.13/select.md b/1.13/select.md deleted file mode 100644 index e69de29..0000000 diff --git a/1.13/sync_pool.md b/1.13/sync_pool.md deleted file mode 100644 index 208b947..0000000 --- a/1.13/sync_pool.md +++ /dev/null @@ -1,254 +0,0 @@ -// A Pool is a set of temporary objects that may be individually saved and -// retrieved. -// -// Any item stored in the Pool may be removed automatically at any time without -// notification. If the Pool holds the only reference when this happens, the -// item might be deallocated. -// -// A Pool is safe for use by multiple goroutines simultaneously. -// -// Pool's purpose is to cache allocated but unused items for later reuse, -// relieving pressure on the garbage collector. That is, it makes it easy to -// build efficient, thread-safe free lists. However, it is not suitable for all -// free lists. -// -// An appropriate use of a Pool is to manage a group of temporary items -// silently shared among and potentially reused by concurrent independent -// clients of a package. Pool provides a way to amortize allocation overhead -// across many clients. -// -// An example of good use of a Pool is in the fmt package, which maintains a -// dynamically-sized store of temporary output buffers. The store scales under -// load (when many goroutines are actively printing) and shrinks when -// quiescent. -// -// On the other hand, a free list maintained as part of a short-lived object is -// not a suitable use for a Pool, since the overhead does not amortize well in -// that scenario. It is more efficient to have such objects implement their own -// free list. -// -// A Pool must not be copied after first use. -type Pool struct { - noCopy noCopy - - local unsafe.Pointer // local fixed-size per-P pool, actual type is [P]poolLocal - localSize uintptr // size of the local array - - victim unsafe.Pointer // local from previous cycle - victimSize uintptr // size of victims array - - // New optionally specifies a function to generate - // a value when Get would otherwise return nil. - // It may not be changed concurrently with calls to Get. - New func() interface{} -} - -// Local per-P Pool appendix. -type poolLocalInternal struct { - private interface{} // Can be used only by the respective P. - shared poolChain // Local P can pushHead/popHead; any P can popTail. -} - -type poolLocal struct { - poolLocalInternal - - // Prevents false sharing on widespread platforms with - // 128 mod (cache line size) = 0 . - pad [128 - unsafe.Sizeof(poolLocalInternal{})%128]byte -} - -// from runtime -func fastrand() uint32 - -var poolRaceHash [128]uint64 - -// poolRaceAddr returns an address to use as the synchronization point -// for race detector logic. We don't use the actual pointer stored in x -// directly, for fear of conflicting with other synchronization on that address. -// Instead, we hash the pointer to get an index into poolRaceHash. -// See discussion on golang.org/cl/31589. -func poolRaceAddr(x interface{}) unsafe.Pointer { - ptr := uintptr((*[2]unsafe.Pointer)(unsafe.Pointer(&x))[1]) - h := uint32((uint64(uint32(ptr)) * 0x85ebca6b) >> 16) - return unsafe.Pointer(&poolRaceHash[h%uint32(len(poolRaceHash))]) -} - -// Put adds x to the pool. -func (p *Pool) Put(x interface{}) { - if x == nil { - return - } - - l, _ := p.pin() - if l.private == nil { - l.private = x - x = nil - } - if x != nil { - l.shared.pushHead(x) - } - runtime_procUnpin() -} - -// Get selects an arbitrary item from the Pool, removes it from the -// Pool, and returns it to the caller. -// Get may choose to ignore the pool and treat it as empty. -// Callers should not assume any relation between values passed to Put and -// the values returned by Get. -// -// If Get would otherwise return nil and p.New is non-nil, Get returns -// the result of calling p.New. -func (p *Pool) Get() interface{} { - l, pid := p.pin() - x := l.private - l.private = nil - if x == nil { - // Try to pop the head of the local shard. We prefer - // the head over the tail for temporal locality of - // reuse. - x, _ = l.shared.popHead() - if x == nil { - x = p.getSlow(pid) - } - } - runtime_procUnpin() - - if x == nil && p.New != nil { - x = p.New() - } - return x -} - -func (p *Pool) getSlow(pid int) interface{} { - // See the comment in pin regarding ordering of the loads. - size := atomic.LoadUintptr(&p.localSize) // load-acquire - locals := p.local // load-consume - // Try to steal one element from other procs. - for i := 0; i < int(size); i++ { - l := indexLocal(locals, (pid+i+1)%int(size)) - if x, _ := l.shared.popTail(); x != nil { - return x - } - } - - // Try the victim cache. We do this after attempting to steal - // from all primary caches because we want objects in the - // victim cache to age out if at all possible. - size = atomic.LoadUintptr(&p.victimSize) - if uintptr(pid) >= size { - return nil - } - locals = p.victim - l := indexLocal(locals, pid) - if x := l.private; x != nil { - l.private = nil - return x - } - for i := 0; i < int(size); i++ { - l := indexLocal(locals, (pid+i)%int(size)) - if x, _ := l.shared.popTail(); x != nil { - return x - } - } - - // Mark the victim cache as empty for future gets don't bother - // with it. - atomic.StoreUintptr(&p.victimSize, 0) - - return nil -} - -// pin pins the current goroutine to P, disables preemption and -// returns poolLocal pool for the P and the P's id. -// Caller must call runtime_procUnpin() when done with the pool. -func (p *Pool) pin() (*poolLocal, int) { - pid := runtime_procPin() - // In pinSlow we store to local and then to localSize, here we load in opposite order. - // Since we've disabled preemption, GC cannot happen in between. - // Thus here we must observe local at least as large localSize. - // We can observe a newer/larger local, it is fine (we must observe its zero-initialized-ness). - s := atomic.LoadUintptr(&p.localSize) // load-acquire - l := p.local // load-consume - if uintptr(pid) < s { - return indexLocal(l, pid), pid - } - return p.pinSlow() -} - -func (p *Pool) pinSlow() (*poolLocal, int) { - // Retry under the mutex. - // Can not lock the mutex while pinned. - runtime_procUnpin() - allPoolsMu.Lock() - defer allPoolsMu.Unlock() - pid := runtime_procPin() - // poolCleanup won't be called while we are pinned. - s := p.localSize - l := p.local - if uintptr(pid) < s { - return indexLocal(l, pid), pid - } - if p.local == nil { - allPools = append(allPools, p) - } - // If GOMAXPROCS changes between GCs, we re-allocate the array and lose the old one. - size := runtime.GOMAXPROCS(0) - local := make([]poolLocal, size) - atomic.StorePointer(&p.local, unsafe.Pointer(&local[0])) // store-release - atomic.StoreUintptr(&p.localSize, uintptr(size)) // store-release - return &local[pid], pid -} - -func poolCleanup() { - // This function is called with the world stopped, at the beginning of a garbage collection. - // It must not allocate and probably should not call any runtime functions. - - // Because the world is stopped, no pool user can be in a - // pinned section (in effect, this has all Ps pinned). - - // Drop victim caches from all pools. - for _, p := range oldPools { - p.victim = nil - p.victimSize = 0 - } - - // Move primary cache to victim cache. - for _, p := range allPools { - p.victim = p.local - p.victimSize = p.localSize - p.local = nil - p.localSize = 0 - } - - // The pools with non-empty primary caches now have non-empty - // victim caches and no pools have primary caches. - oldPools, allPools = allPools, nil -} - -var ( - allPoolsMu Mutex - - // allPools is the set of pools that have non-empty primary - // caches. Protected by either 1) allPoolsMu and pinning or 2) - // STW. - allPools []*Pool - - // oldPools is the set of pools that may have non-empty victim - // caches. Protected by STW. - oldPools []*Pool -) - -func init() { - runtime_registerPoolCleanup(poolCleanup) -} - -func indexLocal(l unsafe.Pointer, i int) *poolLocal { - lp := unsafe.Pointer(uintptr(l) + uintptr(i)*unsafe.Sizeof(poolLocal{})) - return (*poolLocal)(lp) -} - -// Implemented in runtime. -func runtime_registerPoolCleanup(cleanup func()) -func runtime_procPin() int -func runtime_procUnpin() - diff --git a/1.14/timer.md b/1.14/timer.md new file mode 100644 index 0000000..fd0b0b4 --- /dev/null +++ b/1.14/timer.md @@ -0,0 +1,998 @@ +# timer + +```go +type timer struct { + pp puintptr // ** 新增字段 + + when int64 + period int64 + f func(interface{}, uintptr) + arg interface{} + seq uintptr + + nextwhen int64 // ** 新增字段,表示当 timer 的状态为 timerModifiedXX 时,下一次 when 应该设置为什么值,这里的 timerModifiedXX 包含 timerModifiedEarlier 和 timerModifiedLater + + status uint32 // ** 新增字段,代表 timer 的状态,值为下面的枚举值 +} +``` + +这里原文的注释写着: + +``` +// 本文件外的代码,在使用 timer 值时应该小心 +// +// pp,status 和 nextwhen 这几个新增加的字段只能在本文件内使用 +// +// 创建 new timer 值时,只应该设置 when,period,f,arg 和 seq 这些字段 +``` + +可以看出 Go 在字段权限控制上的一些弱势,只有较粗粒度的 pub 和 private,而没有办法做文件内的权限保护(因为当前目录(pkg)下的所有文件理论上可访问该 struct 的任意字段)。 + +创建 new timer 值之后,应该将该 timer 值传给 addtimer(实际是在 time.startTimer)中调用的。 + +一个活跃的 timer (即被传给 addtimer) 可能会被传给 deltimer (time.stopTimer),这之后该 timer 便不是一个活跃的 timer。 + +在非活跃 timer 中,period,f,arg 和 seq 字段均会被修改,when 字段则不会发生修改。 + +删除一个非活跃的 timer 并让 GC 回收,这是没什么问题的。但把一个非活跃的 timer 传给 addtimer 就不 OK 了。 + +只有新创建的 timer 值才能允许传给 addtimer。 + +一个活跃的 timer 可能会传给 modtimer。其字段不会发生任何变动。该 timer 依然能够保持活跃。 + +一个非活跃的 timer 可能会被传给 resettimer 函数,被转换为一个活跃的 timer,并且会将其 when 字段进行更新。 + +新创建的 timer 也可以传给 resettimer。 + +timer 共有 addtimer,deltimer,modtimer,resettimer,cleantimers,adjusttimers 和 runtime 这些操作。 + +不允许同时调用 addtimer/deltimer/modtimer/resettimer。 + +但 adjusttimers 和 runtimer 可以同时与上述的任何操作同时调用。 + +活跃的 timer 在和 P 相关联的堆中,即 p 结构体的 timers 字段上。 + +非活跃的 timer 在被移除之前,也临时性地在 timers 堆中。 + +下面是 timer 进行各种操作时的可能状态迁移: + +// TODO, draw status migration picture + +```go +// +// addtimer: +// timerNoStatus -> timerWaiting +// anything else -> panic: invalid value +// deltimer: +// timerWaiting -> timerDeleted +// timerModifiedXX -> timerDeleted +// timerNoStatus -> do nothing +// timerDeleted -> do nothing +// timerRemoving -> do nothing +// timerRemoved -> do nothing +// timerRunning -> wait until status changes +// timerMoving -> wait until status changes +// timerModifying -> panic: concurrent deltimer/modtimer calls +// modtimer: +// timerWaiting -> timerModifying -> timerModifiedXX +// timerModifiedXX -> timerModifying -> timerModifiedYY +// timerNoStatus -> timerWaiting +// timerRemoved -> timerWaiting +// timerRunning -> wait until status changes +// timerMoving -> wait until status changes +// timerRemoving -> wait until status changes +// timerDeleted -> panic: concurrent modtimer/deltimer calls +// timerModifying -> panic: concurrent modtimer calls +// resettimer: +// timerNoStatus -> timerWaiting +// timerRemoved -> timerWaiting +// timerDeleted -> timerModifying -> timerModifiedXX +// timerRemoving -> wait until status changes +// timerRunning -> wait until status changes +// timerWaiting -> panic: resettimer called on active timer +// timerMoving -> panic: resettimer called on active timer +// timerModifiedXX -> panic: resettimer called on active timer +// timerModifying -> panic: resettimer called on active timer +// cleantimers (looks in P's timer heap): +// timerDeleted -> timerRemoving -> timerRemoved +// timerModifiedXX -> timerMoving -> timerWaiting +// adjusttimers (looks in P's timer heap): +// timerDeleted -> timerRemoving -> timerRemoved +// timerModifiedXX -> timerMoving -> timerWaiting +// runtimer (looks in P's timer heap): +// timerNoStatus -> panic: uninitialized timer +// timerWaiting -> timerWaiting or +// timerWaiting -> timerRunning -> timerNoStatus or +// timerWaiting -> timerRunning -> timerWaiting +// timerModifying -> wait until status changes +// timerModifiedXX -> timerMoving -> timerWaiting +// timerDeleted -> timerRemoving -> timerRemoved +// timerRunning -> panic: concurrent runtimer calls +// timerRemoved -> panic: inconsistent timer heap +// timerRemoving -> panic: inconsistent timer heap +// timerMoving -> panic: inconsistent timer heap +``` + +timer 的 status 字段可能有下面这些取值: + +``` +const ( + // Timer has no status set yet. + timerNoStatus = iota + + // Waiting for timer to fire. + // The timer is in some P's heap. + timerWaiting + + // Running the timer function. + // A timer will only have this status briefly. + timerRunning + + // The timer is deleted and should be removed. + // It should not be run, but it is still in some P's heap. + timerDeleted + + // The timer is being removed. + // The timer will only have this status briefly. + timerRemoving + + // The timer has been stopped. + // It is not in any P's heap. + timerRemoved + + // The timer is being modified. + // The timer will only have this status briefly. + timerModifying + + // The timer has been modified to an earlier time. + // The new when value is in the nextwhen field. + // The timer is in some P's heap, possibly in the wrong place. + timerModifiedEarlier + + // The timer has been modified to the same or a later time. + // The new when value is in the nextwhen field. + // The timer is in some P's heap, possibly in the wrong place. + timerModifiedLater + + // The timer has been modified and is being moved. + // The timer will only have this status briefly. + timerMoving +) +``` + +## time.Sleep 的实现: + +```go +//go:linkname timeSleep time.Sleep +func timeSleep(ns int64) { + if ns <= 0 { + return + } + + gp := getg() + t := gp.timer + if t == nil { + t = new(timer) + gp.timer = t + } + t.f = goroutineReady + t.arg = gp + t.nextwhen = nanotime() + ns + gopark(resetForSleep, unsafe.Pointer(t), waitReasonSleep, traceEvGoSleep, 1) +} + +// resetForSleep 在 timeSleep 流程中,goroutine 被 parked 之后,便会调用 +// 不能在 timeSleep 内直接调用 resettimer,因为如果 sleep 的参数是一个很小的时间值 +// 且有很多 goroutine 的情况下,P 可能最终会在执行 gopark 之前就直接调用 timer 对象的 f,即 goroutineReady +func resetForSleep(gp *g, ut unsafe.Pointer) bool { + t := (*timer)(ut) + resettimer(t, t.nextwhen) + return true +} +``` + +在 goroutine 被 parked 之后,会调用传入的 resetForSleep -> resettimer。 + +```go +func resettimer(t *timer, when int64) { + for { + switch s := atomic.Load(&t.status); s { + case timerNoStatus, timerRemoved: + if atomic.Cas(&t.status, s, timerWaiting) { + t.when = when + addInitializedTimer(t) + return + } + } + } + ... +} +``` + +所以流程是 time.Sleep -> gopark -> resetForSleep -> resettimer -> addInitializedTimer -> (cleantimers && doaddtimer -> wakeNetPoller)。 + +相比老的 time.Sleep 实现,这里最大的变化是在 goroutine 被唤醒之前,没有锁了(???)。 + +```go +// 把 t 放进 timer heap +//go:linkname startTimer time.startTimer +func startTimer(t *timer) { + if raceenabled { + racerelease(unsafe.Pointer(t)) + } + addtimer(t) +} + +// 停止一个 timer +// 返回值表示 t 是否在被 run 之前就 stop 了 +//go:linkname stopTimer time.stopTimer +func stopTimer(t *timer) bool { + return deltimer(t) +} + +// reset 一个非活跃的 timer,并把它塞进堆里 +//go:linkname resetTimer time.resetTimer +func resetTimer(t *timer, when int64) { + if raceenabled { + racerelease(unsafe.Pointer(t)) + } + resettimer(t, when) +} + +// addtimer 给当前的 P 增加一个新的 timer +// 只有新创建的 timer 才应该使用该操作 +// 这样可以避免修改某个 P 的堆上的 timer 的 when 字段,以避免导致 heap 从有序变为无序 +func addtimer(t *timer) { + // when must never be negative; otherwise runtimer will overflow + // during its delta calculation and never expire other runtime timers. + if t.when < 0 { + t.when = maxWhen + } + if t.status != timerNoStatus { + badTimer() + } + t.status = timerWaiting + + addInitializedTimer(t) +} + +// 给当前 P 增加一个已初始化的 timer +func addInitializedTimer(t *timer) { + when := t.when + + pp := getg().m.p.ptr() + lock(&pp.timersLock) + ok := cleantimers(pp) && doaddtimer(pp, t) + unlock(&pp.timersLock) + if !ok { + badTimer() + } + + wakeNetPoller(when) +} + +// doaddtimer adds t to the current P's heap. +// It reports whether it saw no problems due to races. +// The caller must have locked the timers for pp. +func doaddtimer(pp *p, t *timer) bool { + // Timers rely on the network poller, so make sure the poller + // has started. + if netpollInited == 0 { + netpollGenericInit() + } + + if t.pp != 0 { + throw("doaddtimer: P already set in timer") + } + t.pp.set(pp) + i := len(pp.timers) + pp.timers = append(pp.timers, t) + return siftupTimer(pp.timers, i) +} +``` + +```go +// deltimer deletes the timer t. It may be on some other P, so we can't +// actually remove it from the timers heap. We can only mark it as deleted. +// It will be removed in due course by the P whose heap it is on. +// Reports whether the timer was removed before it was run. +func deltimer(t *timer) bool { + for { + switch s := atomic.Load(&t.status); s { + case timerWaiting, timerModifiedLater: + if atomic.Cas(&t.status, s, timerDeleted) { + // Timer was not yet run. + return true + } + case timerModifiedEarlier: + tpp := t.pp.ptr() + if atomic.Cas(&t.status, s, timerModifying) { + atomic.Xadd(&tpp.adjustTimers, -1) + if !atomic.Cas(&t.status, timerModifying, timerDeleted) { + badTimer() + } + // Timer was not yet run. + return true + } + case timerDeleted, timerRemoving, timerRemoved: + // Timer was already run. + return false + case timerRunning, timerMoving: + // The timer is being run or moved, by a different P. + // Wait for it to complete. + osyield() + case timerNoStatus: + // Removing timer that was never added or + // has already been run. Also see issue 21874. + return false + case timerModifying: + // Simultaneous calls to deltimer and modtimer. + badTimer() + default: + badTimer() + } + } +} + +// dodeltimer removes timer i from the current P's heap. +// We are locked on the P when this is called. +// It reports whether it saw no problems due to races. +// The caller must have locked the timers for pp. +func dodeltimer(pp *p, i int) bool { + if t := pp.timers[i]; t.pp.ptr() != pp { + throw("dodeltimer: wrong P") + } else { + t.pp = 0 + } + last := len(pp.timers) - 1 + if i != last { + pp.timers[i] = pp.timers[last] + } + pp.timers[last] = nil + pp.timers = pp.timers[:last] + ok := true + if i != last { + // Moving to i may have moved the last timer to a new parent, + // so sift up to preserve the heap guarantee. + if !siftupTimer(pp.timers, i) { + ok = false + } + if !siftdownTimer(pp.timers, i) { + ok = false + } + } + return ok +} + +// dodeltimer0 removes timer 0 from the current P's heap. +// We are locked on the P when this is called. +// It reports whether it saw no problems due to races. +// The caller must have locked the timers for pp. +func dodeltimer0(pp *p) bool { + if t := pp.timers[0]; t.pp.ptr() != pp { + throw("dodeltimer0: wrong P") + } else { + t.pp = 0 + } + last := len(pp.timers) - 1 + if last > 0 { + pp.timers[0] = pp.timers[last] + } + pp.timers[last] = nil + pp.timers = pp.timers[:last] + ok := true + if last > 0 { + ok = siftdownTimer(pp.timers, 0) + } + return ok +} +``` + +```go +// modtimer modifies an existing timer. +// This is called by the netpoll code. +func modtimer(t *timer, when, period int64, f func(interface{}, uintptr), arg interface{}, seq uintptr) { + if when < 0 { + when = maxWhen + } + + status := uint32(timerNoStatus) + wasRemoved := false +loop: + for { + switch status = atomic.Load(&t.status); status { + case timerWaiting, timerModifiedEarlier, timerModifiedLater: + if atomic.Cas(&t.status, status, timerModifying) { + break loop + } + case timerNoStatus, timerRemoved: + // Timer was already run and t is no longer in a heap. + // Act like addtimer. + if atomic.Cas(&t.status, status, timerWaiting) { + wasRemoved = true + break loop + } + case timerRunning, timerRemoving, timerMoving: + // The timer is being run or moved, by a different P. + // Wait for it to complete. + osyield() + case timerDeleted: + // Simultaneous calls to modtimer and deltimer. + badTimer() + case timerModifying: + // Multiple simultaneous calls to modtimer. + badTimer() + default: + badTimer() + } + } + + t.period = period + t.f = f + t.arg = arg + t.seq = seq + + if wasRemoved { + t.when = when + addInitializedTimer(t) + } else { + // The timer is in some other P's heap, so we can't change + // the when field. If we did, the other P's heap would + // be out of order. So we put the new when value in the + // nextwhen field, and let the other P set the when field + // when it is prepared to resort the heap. + t.nextwhen = when + + newStatus := uint32(timerModifiedLater) + if when < t.when { + newStatus = timerModifiedEarlier + } + + // Update the adjustTimers field. Subtract one if we + // are removing a timerModifiedEarlier, add one if we + // are adding a timerModifiedEarlier. + tpp := t.pp.ptr() + adjust := int32(0) + if status == timerModifiedEarlier { + adjust-- + } + if newStatus == timerModifiedEarlier { + adjust++ + } + if adjust != 0 { + atomic.Xadd(&tpp.adjustTimers, adjust) + } + + // Set the new status of the timer. + if !atomic.Cas(&t.status, timerModifying, newStatus) { + badTimer() + } + + // If the new status is earlier, wake up the poller. + if newStatus == timerModifiedEarlier { + wakeNetPoller(when) + } + } +} +``` + +```go +// resettimer resets an existing inactive timer to turn it into an active timer, +// with a new time for when the timer should fire. +// This should be called instead of addtimer if the timer value has been, +// or may have been, used previously. +func resettimer(t *timer, when int64) { + if when < 0 { + when = maxWhen + } + + for { + switch s := atomic.Load(&t.status); s { + case timerNoStatus, timerRemoved: + if atomic.Cas(&t.status, s, timerWaiting) { + t.when = when + addInitializedTimer(t) + return + } + case timerDeleted: + if atomic.Cas(&t.status, s, timerModifying) { + t.nextwhen = when + newStatus := uint32(timerModifiedLater) + if when < t.when { + newStatus = timerModifiedEarlier + atomic.Xadd(&t.pp.ptr().adjustTimers, 1) + } + if !atomic.Cas(&t.status, timerModifying, newStatus) { + badTimer() + } + if newStatus == timerModifiedEarlier { + wakeNetPoller(when) + } + return + } + case timerRemoving: + // Wait for the removal to complete. + osyield() + case timerRunning: + // Even though the timer should not be active, + // we can see timerRunning if the timer function + // permits some other goroutine to call resettimer. + // Wait until the run is complete. + osyield() + case timerWaiting, timerModifying, timerModifiedEarlier, timerModifiedLater, timerMoving: + // Called resettimer on active timer. + badTimer() + default: + badTimer() + } + } +} + +``` + +```go +// cleantimers cleans up the head of the timer queue. This speeds up +// programs that create and delete timers; leaving them in the heap +// slows down addtimer. Reports whether no timer problems were found. +// The caller must have locked the timers for pp. +func cleantimers(pp *p) bool { + for { + if len(pp.timers) == 0 { + return true + } + t := pp.timers[0] + if t.pp.ptr() != pp { + throw("cleantimers: bad p") + } + switch s := atomic.Load(&t.status); s { + case timerDeleted: + if !atomic.Cas(&t.status, s, timerRemoving) { + continue + } + if !dodeltimer0(pp) { + return false + } + if !atomic.Cas(&t.status, timerRemoving, timerRemoved) { + return false + } + case timerModifiedEarlier, timerModifiedLater: + if !atomic.Cas(&t.status, s, timerMoving) { + continue + } + // Now we can change the when field. + t.when = t.nextwhen + // Move t to the right position. + if !dodeltimer0(pp) { + return false + } + if !doaddtimer(pp, t) { + return false + } + if s == timerModifiedEarlier { + atomic.Xadd(&pp.adjustTimers, -1) + } + if !atomic.Cas(&t.status, timerMoving, timerWaiting) { + return false + } + default: + // Head of timers does not need adjustment. + return true + } + } +} +``` + +```go +// moveTimers moves a slice of timers to pp. The slice has been taken +// from a different P. +// This is currently called when the world is stopped, but the caller +// is expected to have locked the timers for pp. +func moveTimers(pp *p, timers []*timer) { + for _, t := range timers { + loop: + for { + switch s := atomic.Load(&t.status); s { + case timerWaiting: + t.pp = 0 + if !doaddtimer(pp, t) { + badTimer() + } + break loop + case timerModifiedEarlier, timerModifiedLater: + if !atomic.Cas(&t.status, s, timerMoving) { + continue + } + t.when = t.nextwhen + t.pp = 0 + if !doaddtimer(pp, t) { + badTimer() + } + if !atomic.Cas(&t.status, timerMoving, timerWaiting) { + badTimer() + } + break loop + case timerDeleted: + if !atomic.Cas(&t.status, s, timerRemoved) { + continue + } + t.pp = 0 + // We no longer need this timer in the heap. + break loop + case timerModifying: + // Loop until the modification is complete. + osyield() + case timerNoStatus, timerRemoved: + // We should not see these status values in a timers heap. + badTimer() + case timerRunning, timerRemoving, timerMoving: + // Some other P thinks it owns this timer, + // which should not happen. + badTimer() + default: + badTimer() + } + } + } +} +``` + +```go +// adjusttimers looks through the timers in the current P's heap for +// any timers that have been modified to run earlier, and puts them in +// the correct place in the heap. While looking for those timers, +// it also moves timers that have been modified to run later, +// and removes deleted timers. The caller must have locked the timers for pp. +func adjusttimers(pp *p) { + if len(pp.timers) == 0 { + return + } + if atomic.Load(&pp.adjustTimers) == 0 { + return + } + var moved []*timer + for i := 0; i < len(pp.timers); i++ { + t := pp.timers[i] + if t.pp.ptr() != pp { + throw("adjusttimers: bad p") + } + switch s := atomic.Load(&t.status); s { + case timerDeleted: + if atomic.Cas(&t.status, s, timerRemoving) { + if !dodeltimer(pp, i) { + badTimer() + } + if !atomic.Cas(&t.status, timerRemoving, timerRemoved) { + badTimer() + } + // Look at this heap position again. + i-- + } + case timerModifiedEarlier, timerModifiedLater: + if atomic.Cas(&t.status, s, timerMoving) { + // Now we can change the when field. + t.when = t.nextwhen + // Take t off the heap, and hold onto it. + // We don't add it back yet because the + // heap manipulation could cause our + // loop to skip some other timer. + if !dodeltimer(pp, i) { + badTimer() + } + moved = append(moved, t) + if s == timerModifiedEarlier { + if n := atomic.Xadd(&pp.adjustTimers, -1); int32(n) <= 0 { + addAdjustedTimers(pp, moved) + return + } + } + } + case timerNoStatus, timerRunning, timerRemoving, timerRemoved, timerMoving: + badTimer() + case timerWaiting: + // OK, nothing to do. + case timerModifying: + // Check again after modification is complete. + osyield() + i-- + default: + badTimer() + } + } + + if len(moved) > 0 { + addAdjustedTimers(pp, moved) + } +} + +// addAdjustedTimers adds any timers we adjusted in adjusttimers +// back to the timer heap. +func addAdjustedTimers(pp *p, moved []*timer) { + for _, t := range moved { + if !doaddtimer(pp, t) { + badTimer() + } + if !atomic.Cas(&t.status, timerMoving, timerWaiting) { + badTimer() + } + } +} + +``` + +```go +// nobarrierWakeTime looks at P's timers and returns the time when we +// should wake up the netpoller. It returns 0 if there are no timers. +// This function is invoked when dropping a P, and must run without +// any write barriers. Therefore, if there are any timers that needs +// to be moved earlier, it conservatively returns the current time. +// The netpoller M will wake up and adjust timers before sleeping again. +//go:nowritebarrierrec +func nobarrierWakeTime(pp *p) int64 { + lock(&pp.timersLock) + ret := int64(0) + if len(pp.timers) > 0 { + if atomic.Load(&pp.adjustTimers) > 0 { + ret = nanotime() + } else { + ret = pp.timers[0].when + } + } + unlock(&pp.timersLock) + return ret +} +``` + +```go +// runtimer examines the first timer in timers. If it is ready based on now, +// it runs the timer and removes or updates it. +// Returns 0 if it ran a timer, -1 if there are no more timers, or the time +// when the first timer should run. +// The caller must have locked the timers for pp. +// If a timer is run, this will temporarily unlock the timers. +//go:systemstack +func runtimer(pp *p, now int64) int64 { + for { + t := pp.timers[0] + if t.pp.ptr() != pp { + throw("runtimer: bad p") + } + switch s := atomic.Load(&t.status); s { + case timerWaiting: + if t.when > now { + // Not ready to run. + return t.when + } + + if !atomic.Cas(&t.status, s, timerRunning) { + continue + } + // Note that runOneTimer may temporarily unlock + // pp.timersLock. + runOneTimer(pp, t, now) + return 0 + + case timerDeleted: + if !atomic.Cas(&t.status, s, timerRemoving) { + continue + } + if !dodeltimer0(pp) { + badTimer() + } + if !atomic.Cas(&t.status, timerRemoving, timerRemoved) { + badTimer() + } + if len(pp.timers) == 0 { + return -1 + } + + case timerModifiedEarlier, timerModifiedLater: + if !atomic.Cas(&t.status, s, timerMoving) { + continue + } + t.when = t.nextwhen + if !dodeltimer0(pp) { + badTimer() + } + if !doaddtimer(pp, t) { + badTimer() + } + if s == timerModifiedEarlier { + atomic.Xadd(&pp.adjustTimers, -1) + } + if !atomic.Cas(&t.status, timerMoving, timerWaiting) { + badTimer() + } + + case timerModifying: + // Wait for modification to complete. + osyield() + + case timerNoStatus, timerRemoved: + // Should not see a new or inactive timer on the heap. + badTimer() + case timerRunning, timerRemoving, timerMoving: + // These should only be set when timers are locked, + // and we didn't do it. + badTimer() + default: + badTimer() + } + } +} +``` + +```go +// runOneTimer runs a single timer. +// The caller must have locked the timers for pp. +// This will temporarily unlock the timers while running the timer function. +//go:systemstack +func runOneTimer(pp *p, t *timer, now int64) { + if raceenabled { + ppcur := getg().m.p.ptr() + if ppcur.timerRaceCtx == 0 { + ppcur.timerRaceCtx = racegostart(funcPC(runtimer) + sys.PCQuantum) + } + raceacquirectx(ppcur.timerRaceCtx, unsafe.Pointer(t)) + } + + f := t.f + arg := t.arg + seq := t.seq + + if t.period > 0 { + // Leave in heap but adjust next time to fire. + delta := t.when - now + t.when += t.period * (1 + -delta/t.period) + if !siftdownTimer(pp.timers, 0) { + badTimer() + } + if !atomic.Cas(&t.status, timerRunning, timerWaiting) { + badTimer() + } + } else { + // Remove from heap. + if !dodeltimer0(pp) { + badTimer() + } + if !atomic.Cas(&t.status, timerRunning, timerNoStatus) { + badTimer() + } + } + + if raceenabled { + // Temporarily use the current P's racectx for g0. + gp := getg() + if gp.racectx != 0 { + throw("runOneTimer: unexpected racectx") + } + gp.racectx = gp.m.p.ptr().timerRaceCtx + } + + unlock(&pp.timersLock) + + f(arg, seq) + + lock(&pp.timersLock) + + if raceenabled { + gp := getg() + gp.racectx = 0 + } +} +``` + +```go +func timejump() *p { + if faketime == 0 { + return nil + } + + // Nothing is running, so we can look at all the P's. + // Determine a timer bucket with minimum when. + var ( + minT *timer + minWhen int64 + minP *p + ) + for _, pp := range allp { + if pp.status != _Pidle && pp.status != _Pdead { + throw("non-idle P in timejump") + } + if len(pp.timers) == 0 { + continue + } + c := pp.adjustTimers + for _, t := range pp.timers { + switch s := atomic.Load(&t.status); s { + case timerWaiting: + if minT == nil || t.when < minWhen { + minT = t + minWhen = t.when + minP = pp + } + case timerModifiedEarlier, timerModifiedLater: + if minT == nil || t.nextwhen < minWhen { + minT = t + minWhen = t.nextwhen + minP = pp + } + if s == timerModifiedEarlier { + c-- + } + case timerRunning, timerModifying, timerMoving: + badTimer() + } + // The timers are sorted, so we only have to check + // the first timer for each P, unless there are + // some timerModifiedEarlier timers. The number + // of timerModifiedEarlier timers is in the adjustTimers + // field, used to initialize c, above. + if c == 0 { + break + } + } + } + + if minT == nil || minWhen <= faketime { + return nil + } + + faketime = minWhen + return minP +} + +// timeSleepUntil returns the time when the next timer should fire. +// This is only called by sysmon. +func timeSleepUntil() int64 { + next := int64(maxWhen) + + // Prevent allp slice changes. This is like retake. + lock(&allpLock) + for _, pp := range allp { + if pp == nil { + // This can happen if procresize has grown + // allp but not yet created new Ps. + continue + } + + lock(&pp.timersLock) + c := atomic.Load(&pp.adjustTimers) + for _, t := range pp.timers { + switch s := atomic.Load(&t.status); s { + case timerWaiting: + if t.when < next { + next = t.when + } + case timerModifiedEarlier, timerModifiedLater: + if t.nextwhen < next { + next = t.nextwhen + } + if s == timerModifiedEarlier { + c-- + } + } + // The timers are sorted, so we only have to check + // the first timer for each P, unless there are + // some timerModifiedEarlier timers. The number + // of timerModifiedEarlier timers is in the adjustTimers + // field, used to initialize c, above. + // + // We don't worry about cases like timerModifying. + // New timers can show up at any time, + // so this function is necessarily imprecise. + // Do a signed check here since we aren't + // synchronizing the read of pp.adjustTimers + // with the check of a timer status. + if int32(c) <= 0 { + break + } + } + unlock(&pp.timersLock) + } + unlock(&allpLock) + + return next +} +``` + +siftUpTimer 和 siftDownTimer 这两个函数的实现和以前没什么不同,只是去除了冗余的 index 赋值操作。 From 3bf83134faf869e0d23ded4b41605e8d538ac0f1 Mon Sep 17 00:00:00 2001 From: Xargin Date: Wed, 15 Jan 2020 15:24:54 +0800 Subject: [PATCH 005/120] update timer --- 1.14/timer.md | 76 ++++++++++++++++----------------------------------- 1 file changed, 24 insertions(+), 52 deletions(-) diff --git a/1.14/timer.md b/1.14/timer.md index fd0b0b4..e0660bc 100644 --- a/1.14/timer.md +++ b/1.14/timer.md @@ -56,6 +56,7 @@ timer 共有 addtimer,deltimer,modtimer,resettimer,cleantimers,adjustt 下面是 timer 进行各种操作时的可能状态迁移: +// TODO, draw status migration picture // TODO, draw status migration picture ```go @@ -211,15 +212,12 @@ func resettimer(t *timer, when int64) { 所以流程是 time.Sleep -> gopark -> resetForSleep -> resettimer -> addInitializedTimer -> (cleantimers && doaddtimer -> wakeNetPoller)。 -相比老的 time.Sleep 实现,这里最大的变化是在 goroutine 被唤醒之前,没有锁了(???)。 +## 添加 timer 流程 ```go // 把 t 放进 timer heap //go:linkname startTimer time.startTimer func startTimer(t *timer) { - if raceenabled { - racerelease(unsafe.Pointer(t)) - } addtimer(t) } @@ -233,9 +231,6 @@ func stopTimer(t *timer) bool { // reset 一个非活跃的 timer,并把它塞进堆里 //go:linkname resetTimer time.resetTimer func resetTimer(t *timer, when int64) { - if raceenabled { - racerelease(unsafe.Pointer(t)) - } resettimer(t, when) } @@ -243,8 +238,7 @@ func resetTimer(t *timer, when int64) { // 只有新创建的 timer 才应该使用该操作 // 这样可以避免修改某个 P 的堆上的 timer 的 when 字段,以避免导致 heap 从有序变为无序 func addtimer(t *timer) { - // when must never be negative; otherwise runtimer will overflow - // during its delta calculation and never expire other runtime timers. + // when 不能是负数,否则在计算 delta 的时候可能溢出,导致其它 timer 永远不过期 if t.when < 0 { t.when = maxWhen } @@ -271,12 +265,10 @@ func addInitializedTimer(t *timer) { wakeNetPoller(when) } -// doaddtimer adds t to the current P's heap. -// It reports whether it saw no problems due to races. -// The caller must have locked the timers for pp. +// 给当前的 p 添加 timer +// 调整小顶堆至合法 func doaddtimer(pp *p, t *timer) bool { - // Timers rely on the network poller, so make sure the poller - // has started. + // 新的 timer 依赖于 network poller,所以需要确保 poller 已经被启动了 if netpollInited == 0 { netpollGenericInit() } @@ -291,11 +283,12 @@ func doaddtimer(pp *p, t *timer) bool { } ``` +## 删除 timer 流程 + ```go -// deltimer deletes the timer t. It may be on some other P, so we can't -// actually remove it from the timers heap. We can only mark it as deleted. -// It will be removed in due course by the P whose heap it is on. -// Reports whether the timer was removed before it was run. +// 删除 timer t,这个 t 可能在别的 P 上,所以我们不能直接从 timer 堆中移除它。只能将其置为 deleted 状态。 +// 在合适的时间,该 timer 会被其所在的 P 的的堆上被删除 +// 本函数的返回值表示该 timer 是否在被运行之前移除了 func deltimer(t *timer) bool { for { switch s := atomic.Load(&t.status); s { @@ -334,10 +327,10 @@ func deltimer(t *timer) bool { } } -// dodeltimer removes timer i from the current P's heap. -// We are locked on the P when this is called. -// It reports whether it saw no problems due to races. -// The caller must have locked the timers for pp. +// 从其所在的 P 的堆上,删除第 i 个 timer。 +// 本函数调用之前会被锁定在 P 上 +// 函数返回值表示是否有因为 race 导致的问题 +// 本函数需要由 caller 确保 pp 对 timers 的锁已经加好了 func dodeltimer(pp *p, i int) bool { if t := pp.timers[i]; t.pp.ptr() != pp { throw("dodeltimer: wrong P") @@ -364,10 +357,10 @@ func dodeltimer(pp *p, i int) bool { return ok } -// dodeltimer0 removes timer 0 from the current P's heap. -// We are locked on the P when this is called. -// It reports whether it saw no problems due to races. -// The caller must have locked the timers for pp. +// 删除 P 的 timer 堆上的第 0 个 timer +// 调用该函数时,需要锁定至 P +// 返回值表示是否有 race 问题 +// caller 必须确保 pp 已对 timers 上了锁 func dodeltimer0(pp *p) bool { if t := pp.timers[0]; t.pp.ptr() != pp { throw("dodeltimer0: wrong P") @@ -389,8 +382,8 @@ func dodeltimer0(pp *p) bool { ``` ```go -// modtimer modifies an existing timer. -// This is called by the netpoll code. +// 修改已经存在的 timer +// 该函数被 netpoll 代码所调用 func modtimer(t *timer, when, period int64, f func(interface{}, uintptr), arg interface{}, seq uintptr) { if when < 0 { when = maxWhen @@ -822,19 +815,11 @@ func runtimer(pp *p, now int64) int64 { ``` ```go -// runOneTimer runs a single timer. -// The caller must have locked the timers for pp. -// This will temporarily unlock the timers while running the timer function. +// 运行一个独立的 timer +// caller 必须让 pp 对 timers 上锁 +// 在运行 timer 函数时会临时将 timers 的锁 unlock 掉 //go:systemstack func runOneTimer(pp *p, t *timer, now int64) { - if raceenabled { - ppcur := getg().m.p.ptr() - if ppcur.timerRaceCtx == 0 { - ppcur.timerRaceCtx = racegostart(funcPC(runtimer) + sys.PCQuantum) - } - raceacquirectx(ppcur.timerRaceCtx, unsafe.Pointer(t)) - } - f := t.f arg := t.arg seq := t.seq @@ -859,25 +844,12 @@ func runOneTimer(pp *p, t *timer, now int64) { } } - if raceenabled { - // Temporarily use the current P's racectx for g0. - gp := getg() - if gp.racectx != 0 { - throw("runOneTimer: unexpected racectx") - } - gp.racectx = gp.m.p.ptr().timerRaceCtx - } - unlock(&pp.timersLock) f(arg, seq) lock(&pp.timersLock) - if raceenabled { - gp := getg() - gp.racectx = 0 - } } ``` From 7d75c1b9a2d9109847bbb47345f0292767233b0b Mon Sep 17 00:00:00 2001 From: Xargin Date: Wed, 15 Jan 2020 15:43:46 +0800 Subject: [PATCH 006/120] update timer --- 1.14/timer.md | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/1.14/timer.md b/1.14/timer.md index e0660bc..6b4dbd6 100644 --- a/1.14/timer.md +++ b/1.14/timer.md @@ -118,45 +118,46 @@ timer 的 status 字段可能有下面这些取值: ``` const ( - // Timer has no status set yet. + // timer 还没有设置任何状态 + // 刚 new 出来 timerNoStatus = iota - // Waiting for timer to fire. - // The timer is in some P's heap. + // 正在等待 timer 被触发 + // 该 timer 在某个 P 的 heap 中 timerWaiting - // Running the timer function. - // A timer will only have this status briefly. + // 正在运行 timer 的执行函数 + // 一个 timer 只会有很短的时间是这个状态 timerRunning - // The timer is deleted and should be removed. - // It should not be run, but it is still in some P's heap. + // timer 已被 deleted,需要进行 remove + // 不应该执行该 timer 了,不过它还在某个 P 的 heap 里 timerDeleted - // The timer is being removed. - // The timer will only have this status briefly. + // timer 正在被移除 + // timer 在这个状态也只会持续很短一段时间 timerRemoving - // The timer has been stopped. - // It is not in any P's heap. + // timer 已被停止: + // 不在任何一个 P 的 heap 中 timerRemoved - // The timer is being modified. - // The timer will only have this status briefly. + // timer 正在被修改 + // 该状态也只持续很短的时间 timerModifying - // The timer has been modified to an earlier time. - // The new when value is in the nextwhen field. - // The timer is in some P's heap, possibly in the wrong place. + // timer 被修改为了较早的时间 + // 新的 when 值存在 nextwhen 字段中 + // timer 在某个 P 的 heap 中,可能是错误的位置 timerModifiedEarlier - // The timer has been modified to the same or a later time. - // The new when value is in the nextwhen field. - // The timer is in some P's heap, possibly in the wrong place. + // timer 被修改为了相同或者较晚的时间 + // 新的 when 值存储在 nextwhen 字段中 + // timer 在某个 P 的 heap 中,可能是在错误的位置 timerModifiedLater - // The timer has been modified and is being moved. - // The timer will only have this status briefly. + // timer 已被修改,并且正在被移动 + // timer 在这个状态也只持续很短的时间 timerMoving ) ``` From 6873d3ea7f5ed21756fa12517e2d5e96312ea300 Mon Sep 17 00:00:00 2001 From: Xargin Date: Wed, 15 Jan 2020 20:41:24 +0800 Subject: [PATCH 007/120] update timer --- 1.14/timer.md | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/1.14/timer.md b/1.14/timer.md index 6b4dbd6..48eda61 100644 --- a/1.14/timer.md +++ b/1.14/timer.md @@ -471,10 +471,9 @@ loop: ``` ```go -// resettimer resets an existing inactive timer to turn it into an active timer, -// with a new time for when the timer should fire. -// This should be called instead of addtimer if the timer value has been, -// or may have been, used previously. +// 该函数将已存在的非活跃 timer 进行重置,并将其变为一个活跃的 timer +// 同时给该 timer 设置新的触发时间 +// 如果某个 timer 已经或者可能已经被使用过了,就不要使用 addtimer,而应该使用这个函数 func resettimer(t *timer, when int64) { if when < 0 { when = maxWhen @@ -505,13 +504,12 @@ func resettimer(t *timer, when int64) { return } case timerRemoving: - // Wait for the removal to complete. + // 等待移除完毕就行了 osyield() case timerRunning: - // Even though the timer should not be active, - // we can see timerRunning if the timer function - // permits some other goroutine to call resettimer. - // Wait until the run is complete. + // 即使 timer 不应该活跃,也可能因为 timer function + // 允许其它 goroutine 调用 resettimer 而导致看到 timerRunning 这个状态 + // 这时候应该等待 timer function 运行完毕 osyield() case timerWaiting, timerModifying, timerModifiedEarlier, timerModifiedLater, timerMoving: // Called resettimer on active timer. @@ -577,10 +575,8 @@ func cleantimers(pp *p) bool { ``` ```go -// moveTimers moves a slice of timers to pp. The slice has been taken -// from a different P. -// This is currently called when the world is stopped, but the caller -// is expected to have locked the timers for pp. +// 移动一个 timer 切片到 pp。该切片中的 timers 是从另外的 P 中获取到的 +// 当前该函数在 STW 期间调用,不过 caller 还是应该锁住 pp 中的 timers lock func moveTimers(pp *p, timers []*timer) { for _, t := range timers { loop: From ca13908c0a8d9c83b0d28216073c259515a8b214 Mon Sep 17 00:00:00 2001 From: Xargin Date: Mon, 20 Jan 2020 11:38:36 +0800 Subject: [PATCH 008/120] update timer --- 1.14/timer.md | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/1.14/timer.md b/1.14/timer.md index 48eda61..26cd956 100644 --- a/1.14/timer.md +++ b/1.14/timer.md @@ -523,10 +523,9 @@ func resettimer(t *timer, when int64) { ``` ```go -// cleantimers cleans up the head of the timer queue. This speeds up -// programs that create and delete timers; leaving them in the heap -// slows down addtimer. Reports whether no timer problems were found. -// The caller must have locked the timers for pp. +// cleantimers 会删除 timer 队列头部的那些 timer。这样会加快程序创建和删除 timer 的速度;如果将 timer 保留在堆上的话会使 addtimer 变慢。 +// 函数返回值表示是否碰到了 timer 问题(错误 +// 调用该函数前 caller 需要保证 pp 已经对 timers 上了锁 func cleantimers(pp *p) bool { for { if len(pp.timers) == 0 { @@ -567,7 +566,7 @@ func cleantimers(pp *p) bool { return false } default: - // Head of timers does not need adjustment. + // timer 堆的头部不需要进行调整 return true } } @@ -609,14 +608,16 @@ func moveTimers(pp *p, timers []*timer) { // We no longer need this timer in the heap. break loop case timerModifying: - // Loop until the modification is complete. + // 循环一直到完成修改操作 osyield() case timerNoStatus, timerRemoved: - // We should not see these status values in a timers heap. + // 理论上不应该在时间堆上看到这两个状态 + // nostatus 是初始化的时候的默认状态,是入堆前的状态 + // removed 是移除之后的状态,在堆之外 badTimer() case timerRunning, timerRemoving, timerMoving: - // Some other P thinks it owns this timer, - // which should not happen. + // 有其它 P 认为他们持有该 timer + // 从原理上讲不应该 badTimer() default: badTimer() From c32a7b7724ba89ce4069b2d15b424eee47b0a2b0 Mon Sep 17 00:00:00 2001 From: Xargin Date: Thu, 16 Apr 2020 10:59:19 +0800 Subject: [PATCH 009/120] fix argsize --- assembly.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assembly.md b/assembly.md index 73232b7..2fc8e4b 100644 --- a/assembly.md +++ b/assembly.md @@ -607,7 +607,7 @@ output.s: #include "textflag.h" // func output(a,b int) int -TEXT ·output(SB), NOSPLIT, $24-8 +TEXT ·output(SB), NOSPLIT, $24-24 MOVQ a+0(FP), DX // arg a MOVQ DX, 0(SP) // arg x MOVQ b+8(FP), CX // arg b From 8363ceb504b8bde01a8137c97fbfe0320f44bf07 Mon Sep 17 00:00:00 2001 From: Xargin Date: Sun, 26 Apr 2020 10:38:09 +0800 Subject: [PATCH 010/120] add new files for 1.14 --- 1.14/defer.md | 3 +++ 1.14/memory.md | 5 +++++ 1.14/signal_based_preemption.md | 3 +++ 3 files changed, 11 insertions(+) create mode 100644 1.14/defer.md create mode 100644 1.14/memory.md create mode 100644 1.14/signal_based_preemption.md diff --git a/1.14/defer.md b/1.14/defer.md new file mode 100644 index 0000000..ed7441f --- /dev/null +++ b/1.14/defer.md @@ -0,0 +1,3 @@ +# 1.14 defer + +TODO diff --git a/1.14/memory.md b/1.14/memory.md new file mode 100644 index 0000000..8400bb9 --- /dev/null +++ b/1.14/memory.md @@ -0,0 +1,5 @@ +# 1.14 memory + +1.14 在内存管理上的变化 + +TODO diff --git a/1.14/signal_based_preemption.md b/1.14/signal_based_preemption.md new file mode 100644 index 0000000..3ca2d51 --- /dev/null +++ b/1.14/signal_based_preemption.md @@ -0,0 +1,3 @@ +# 1.14 scheduler + +TODO From 1dc143d9372cf052b319f64544619ef4e92bebef Mon Sep 17 00:00:00 2001 From: Xargin Date: Thu, 21 May 2020 12:43:25 +0800 Subject: [PATCH 011/120] update mb --- memory_barrier.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/memory_barrier.md b/memory_barrier.md index 56439e2..4c208d7 100644 --- a/memory_barrier.md +++ b/memory_barrier.md @@ -235,7 +235,7 @@ https://preshing.com/20130922/acquire-and-release-fences/ 在 x86/64 平台上,只有 StoreLoad 乱序,所以你使用 acquire release 时,实际上生成的 fence 是 NOP。 -在 Go 语言中也不需要操心这个问题,Go 语言的 atomic 默认是最强的内存序保证,即 sequential consistency。该一致性保证由 Go 保证,在所有运行 Go 的硬件平台上都是一致的。 +在 Go 语言中也不需要操心这个问题,Go 语言的 atomic 默认是最强的内存序保证,即 sequential consistency。该一致性保证由 Go 保证,在所有运行 Go 的硬件平台上都是一致的。当然,这里说的只是 sync/atomic 暴露出来的接口。Go 在 runtime 层有较弱内存序的相关接口,位置在: runtime/internal/atomic。 ## memory order 参数 @@ -460,6 +460,12 @@ var semtable [semTabSize]struct { 用户态的代码对 false sharing 其实关注的比较少。 +## runtime 中的 publicationBarrier + +TODO + +https://github.com/golang/go/issues/35541 + 参考资料: https://homes.cs.washington.edu/~bornholt/post/memory-models.html From 0d704b2aeb688888237bacf84344649ebc98250c Mon Sep 17 00:00:00 2001 From: Xargin Date: Mon, 15 Jun 2020 19:46:48 +0800 Subject: [PATCH 012/120] add fasthttp --- fasthttp/index.md | 127 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 fasthttp/index.md diff --git a/fasthttp/index.md b/fasthttp/index.md new file mode 100644 index 0000000..19360ee --- /dev/null +++ b/fasthttp/index.md @@ -0,0 +1,127 @@ +# fasthttp + +坊间传言 fasthttp 在某些场景下比 nginx 还要快,说明 fasthttp 中应该是做足了优化。我们来做一些相关的验证工作。 + +先是简单的 hello server 压测。下面的结果是在 mac 上得到的,linux 下可能会有差异。 + +fasthttp: +``` +~ ❯❯❯ wrk -c36 -t12 -d 5s http://127.0.0.1:8080 +Running 5s test @ http://127.0.0.1:8080 + 12 threads and 36 connections + Thread Stats Avg Stdev Max +/- Stdev + Latency 267.58us 42.44us 0.90ms 79.18% + Req/Sec 11.05k 391.97 11.79k 87.42% + 672745 requests in 5.10s, 89.18MB read +Requests/sec: 131930.78 +Transfer/sec: 17.49MB +``` + +标准库: +``` +~ ❯❯❯ wrk -c36 -t12 -d 5s http://127.0.0.1:8080 +Running 5s test @ http://127.0.0.1:10002 + 12 threads and 36 connections + Thread Stats Avg Stdev Max +/- Stdev + Latency 310.94us 163.45us 14.41ms 93.42% + Req/Sec 9.74k 1.01k 12.80k 75.82% + 593327 requests in 5.10s, 63.37MB read +Requests/sec: 116348.87 +Transfer/sec: 12.43MB +``` + +rust 的 actix,编译选项带 --release: +``` +~ ❯❯❯ wrk -c36 -t12 -d 5s http://127.0.0.1:9999 +Running 5s test @ http://127.0.0.1:9999 + 12 threads and 36 connections + Thread Stats Avg Stdev Max +/- Stdev + Latency 267.31us 20.52us 622.00us 86.07% + Req/Sec 11.11k 364.03 11.64k 82.68% + 676329 requests in 5.10s, 68.37MB read +Requests/sec: 132629.68 +Transfer/sec: 13.41MB +``` + +好家伙,虽然是 hello 服务,但是 fasthttp 在性能上竟然赶上 rust 编写的服务了,确实有点夸张。这也间接证明了,“某些场景”至少可能真的和不带 GC 的语言性能差不多。 + +说明 fasthttp 里所做的优化是值得我们做点研究的。不过 fasthttp 搞的这些优化点也并不是很神奇,首先是很常见的 goroutine workerpool,对创建的 goroutine 进行了重用,其本身的 workerpool 结构: + +```go +type workerPool struct { + // Function for serving server connections. + // It must leave c unclosed. + WorkerFunc ServeHandler + ..... + ready []*workerChan +} +``` + +核心就是 ready 数组,该数组的元素是已经创建出来的 goroutine 的 job channel。 + +```go +type workerChan struct { + lastUseTime time.Time + ch chan net.Conn +} +``` + +这么多年过去了,基本的 workerpool 的模型还是没什么变化。最早大概是在 [handling 1 million requests with go](https://medium.com/smsjunk/handling-1-million-requests-per-minute-with-golang-f70ac505fcaa) 提到,fasthttp 中的也只是稍有区别。 + +具体的请求处理流程也比较简单: + +> tcp accept -> workerpool.Serve -> 从 workerpool 的 ready 数组中获取一个 channel -> 将当前已 accept 的连接发送到 channel 中 -> 对端消费者调用 workerFunc + +这里这个 workerFunc 其实就是 serveConn,之所以不写死成 serveConn 主要还是为了在测试的时候能替换掉做 mock,不新鲜。 + +主要的 serve 流程: + +```go +func (s *Server) serveConn(c net.Conn) (err error) { + for { + ctx := s.acquireCtx(c) + br = acquireReader(ctx) // or br, err = acquireByteReader(&ctx) + // read request header && body + + bw = acquireWriter(ctx) + + s.Handler(ctx) // 这里就是 listenAndServe 传入的那个 handler + + if br != nil { + releaseReader(s, br) + } + + if bw != nil { + releaseWriter(s, bw) + } + + if ctx != nil { + s.releaseCtx(ctx) + } + } +} +``` + +在整个 serve 流程中,几乎所有对象全部都进行了重用,ctx(其中有 Request 和 Response 结构),reader,writer,body read buffer。可见作者对于内存重用到达了偏执的程度。 + +同时,对于 header 的处理,rawHeaders 是个大 byte 数组。解析后的 header 的 value 如果是字符串类型,其实都是指向这个大 byte 数组的,不会重复生成很多小对象: + +![fasthttp-1](/content/images/2020/06/fasthttp-1.png) + + +如果是我们自己写这种 kv 结构的 header,大概率就直接 map[string][]string 上了。 + +通过阅读 serveConn 的流程我们也可以发现比较明显的问题,在执行完用户的 Handler 之后,fasthttp 会将所有相关的对象全部释放并重新推进对象池中,在某些场景下,这样做显然是不合适的,举个例子: + +![fasthttp-Page-2](/content/images/2020/06/fasthttp-Page-2.png) + +当用户流程中异步启动了 goroutine,并且在 goroutine 中使用 ctx.Request 之类对象时就会遇到并发问题,因为在 fasthttp 的主流程中认为 ctx 的生命周期已经结束,将该 ctx 放回了 sync.Pool,然而用户依然在使用。想要避免这种问题,用户需要将各种 fasthttp 返回的对象人肉拷贝一遍。 + +从这点上来看,基于 sync.Pool 的性能优化往往也是有代价的,无论在什么场景下使用 sync.Pool,都需要对应用程序中的对象生命周期进行一定的假设,这种假设并不见得适用于 100% 的场景,否则这些手段早就进标准库,而非开源库了。 + +对于库的用户来说,这样的优化手段轻则带来更高的心智负担,重则是线上 bug。在使用开源库之前,还是要多多注意。非性能敏感的业务场景,还是用标准库比较踏实。 + +## 参考资料 + +[1] https://github.com/valyala/fasthttp +[2] https://medium.com/smsjunk/handling-1-million-requests-per-minute-with-golang-f70ac505fca From 703f77b5e0583463183a09fbb706c331720fc95a Mon Sep 17 00:00:00 2001 From: Xargin Date: Tue, 30 Jun 2020 15:02:26 +0800 Subject: [PATCH 013/120] add context mono pic --- monodraw/context.monopic | Bin 0 -> 2597 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 monodraw/context.monopic diff --git a/monodraw/context.monopic b/monodraw/context.monopic new file mode 100644 index 0000000000000000000000000000000000000000..0df6ad14cda160f360accbcfb304142b0e317959 GIT binary patch literal 2597 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 Date: Tue, 30 Jun 2020 20:54:58 +0800 Subject: [PATCH 014/120] add context --- context.md | 414 +++++++++++++++++++++++++++++++++++++++++++++++++++++ readme.md | 1 + 2 files changed, 415 insertions(+) create mode 100644 context.md diff --git a/context.md b/context.md new file mode 100644 index 0000000..8d96190 --- /dev/null +++ b/context.md @@ -0,0 +1,414 @@ +# context + +先简单的了解一下几种 ctx: + +- emptyCtx,所有 ctx 类型的根,用 context.TODO(),或 context.Background() 来生成。 +- valueCtx,主要就是为了在 ctx 中嵌入上下文数据,一个简单的 k 和 v 结构,同一个 ctx 内只支持一对 kv,需要更多的 kv 的话,会形成一棵树形结构。 +- cancelCtx,用来取消程序的执行树,一般用 WithCancel,WithTimeout,WithDeadline 返回的取消函数本质上都是对应了 cancelCtx。 +- timerCtx,在 cancelCtx 上包了一层,支持基于时间的 cancel。 + +# basic usage + +## 使用 emptyCtx 初始化 context + +用来实现 context.TODO() 和 context.Background(),一般是所有 context 树的根。 + +```go +// An emptyCtx is never canceled, has no values, and has no deadline. It is not +// struct{}, since vars of this type must have distinct addresses. +type emptyCtx int + +var ( + background = new(emptyCtx) + todo = new(emptyCtx) +) +``` + +todo 和 background 两者本质上只有名字区别,在按 string 输出的时候会有区别。 + +```go +func (e *emptyCtx) String() string { + switch e { + case background: + return "context.Background" + case todo: + return "context.TODO" + } + return "unknown empty Context" +} +``` + +## 使用 valueCtx 嵌入数据 + +### valueCtx 使用 + +```go +package main + +import ( + "context" + "fmt" +) + +type orderID int + +func main() { + var x = context.TODO() + x = context.WithValue(x, orderID(1), "1234") + x = context.WithValue(x, orderID(2), "2345") + x = context.WithValue(x, orderID(3), "3456") + fmt.Println(x.Value(orderID(2))) +} +``` + +这样的代码会生成下面这样的树: + +``` + ┌────────────┐ + │ emptyCtx │ + └────────────┘ + ▲ + │ + │ + │ parent + │ + │ +┌───────────────────────────────────┐ +│ valueCtx{k: 1, v: 1234} │ +└───────────────────────────────────┘ + ▲ + │ + │ + │ parent + │ + │ + │ +┌───────────────────────────────────┐ +│ valueCtx{k: 2, v: 2345} │ +└───────────────────────────────────┘ + ▲ + │ + │ parent + │ + │ +┌───────────────────────────────────┐ +│ valueCtx{k: 3, v: 3456} │ +└───────────────────────────────────┘ +``` + +简单改一下代码,让结果更像一棵树: + +```go +package main + +import ( + "context" + "fmt" +) + +type orderID int + +func main() { + var x = context.TODO() + x = context.WithValue(x, orderID(1), "1234") + x = context.WithValue(x, orderID(2), "2345") + + y := context.WithValue(x, orderID(3), "4567") + x = context.WithValue(x, orderID(3), "3456") + + fmt.Println(x.Value(orderID(3))) + fmt.Println(y.Value(orderID(3))) +} +``` + +就是像下面这样的图了: + +``` + ┌────────────┐ + │ emptyCtx │ + └────────────┘ + ▲ + │ + │ + │ parent + │ + │ + ┌───────────────────────────────────┐ + │ valueCtx{k: 1, v: 1234} │ + └───────────────────────────────────┘ + ▲ + │ + │ + │ parent + │ + │ + │ + ┌───────────────────────────────────┐ + │ valueCtx{k: 2, v: 2345} │ + └───────────────────────────────────┘ + ▲ + │ + ┌──────────────┴──────────────────────────┐ + │ │ + │ │ +┌───────────────────────────────────┐ ┌───────────────────────────────────┐ +│ valueCtx{k: 3, v: 3456} │ │ valueCtx{k: 3, v: 4567} │ +└───────────────────────────────────┘ └───────────────────────────────────┘ + ┌───────┐ ┌───────┐ + │ x │ │ y │ + └───────┘ └───────┘ +``` + +### valueCtx 分析 + +valueCtx 主要就是用来携带贯穿整个逻辑流程的数据的,在分布式系统中最常见的就是 trace_id,在一些业务系统中,一些业务数据项也需要贯穿整个请求的生命周期,如 order_id,payment_id 等。 + +WithValue 时即会生成 valueCtx: + +```go +func WithValue(parent Context, key, val interface{}) Context { + if key == nil { + panic("nil key") + } + if !reflectlite.TypeOf(key).Comparable() { + panic("key is not comparable") + } + return &valueCtx{parent, key, val} +} +``` + +key 必须为非空,且可比较。 + +在查找值,即执行 Value 操作时,会先判断当前节点的 k 是不是等于用户的输入 k,如果相等,返回结果,如果不等,会依次向上从子节点向父节点,一直查找到整个 ctx 的根。没有找到返回 nil。是一个递归流程: + +```go +func (c *valueCtx) Value(key interface{}) interface{} { + if c.key == key { + return c.val + } + return c.Context.Value(key) // 这里发生了递归,c.Context 就是 c.parent +} +``` + +通过分析,ctx 这么设计是为了能让代码每执行到一个点都可以根据当前情况嵌入新的上下文信息,但我们也可以看到,如果我们每次加一个新值都执行 WithValue 会导致 ctx 的树的层数过高,查找成本比较高 O(H)。 + +很多业务场景中,我们希望在请求入口存入值,在请求过程中随时取用。这时候我们可以将 value 作为一个 map 整体存入。 + +```go +context.WithValue(context.Background(), info, + map[string]string{"order_id" : "111", "payment_id" : "222"} +) +``` + +## 使用 cancelCtx 取消流程 + +### cancelCtx 使用 + +在没有 ctx 的时代,我们想要取消一个 go 出去的协程是很难的,因为 go func() 这个操作没有任何返回,所以我们也没有办法去跟踪这么一个新创建的 goroutine。 + +有了 cancelCtx 之后,我们想要取消就比较简单了: + +```go +package main + +import ( + "context" + "fmt" + "time" +) + +func main() { + jobChan := make(chan struct{}) + ctx, cancelFn := context.WithCancel(context.TODO()) + worker := func() { + jobLoop: + for { + select { + case <-jobChan: + fmt.Println("do my job") + case <-ctx.Done(): + fmt.Println("parent call me to quit") + break jobLoop + } + } + } + + // start worker + go worker() + + // stop all worker + cancelFn() + time.Sleep(time.Second) +} +``` + +不过取消操作一定是需要 fork 出的 goroutine 本身要做一些配合动作的: + +```go +select { + case <-jobChan: + // do my job + fmt.Println("do my job") + case <-ctx.Done(): + // parent want me to quit + fmt.Println("parent call me to quit") + break jobLoop +} +``` + +这里我们一边消费自己的 job channel,一边还需要监听 ctx.Done(),如果不监听 ctx.Done(),那显然也就不知道什么时候需要退出了。 + +### cancelCtx 分析 + +```go +// A cancelCtx can be canceled. When canceled, it also cancels any children +// that implement canceler. +type cancelCtx struct { + Context + + mu sync.Mutex // protects following fields + done chan struct{} // created lazily, closed by first cancel call + children map[canceler]struct{} // set to nil by the first cancel call + err error // set to non-nil by the first cancel call +} +``` + +使用 WithCancel 可以得到一个 cancelCtx: + +```go +// WithCancel returns a copy of parent with a new Done channel. The returned +// context's Done channel is closed when the returned cancel function is called +// or when the parent context's Done channel is closed, whichever happens first. +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this Context complete. +func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { + c := newCancelCtx(parent) + propagateCancel(parent, &c) + return &c, func() { c.cancel(true, Canceled) } +} +``` + +```go +// propagateCancel arranges for child to be canceled when parent is. +func propagateCancel(parent Context, child canceler) { + if parent.Done() == nil { + return // 说明父节点一定是 emptyCtx,或者用户自己实现的 context.Context + } + if p, ok := parentCancelCtx(parent); ok { + p.mu.Lock() + if p.err != nil { + // cancel 发生的时候,err 字段一定会被赋值,这里说明父节点已经被赋值了 + child.cancel(false, p.err) + } else { + if p.children == nil { + p.children = make(map[canceler]struct{}) + } + p.children[child] = struct{}{} // 把当前 cancelCtx 追加到父节点去 + } + p.mu.Unlock() + } else { // 如果用户把 context 包在了自己的 struct 内就会到这个分支。 + go func() { + select { + case <-parent.Done(): // 父节点取消,需要将这个取消指令同步给子节点 + child.cancel(false, parent.Err()) + case <-child.Done(): // 子节点取消的话,就不用等父节点了 + } + }() + } +} +``` + +parentCancelCtx 只识别 context 包内的三种类型,如果用户自己的类实现了 context.Context 接口,或者把 ctx 包在了自己的类型内,那这里始终返回的是 nil,false。 + +```go +func parentCancelCtx(parent Context) (*cancelCtx, bool) { + for { + switch c := parent.(type) { + case *cancelCtx: + return c, true + case *timerCtx: + return &c.cancelCtx, true + case *valueCtx: + parent = c.Context + default: + return nil, false + } + } +} +``` + +## 使用 timerCtx 超时取消 + +### timerCtx 使用 + +### timerCtx 分析 + +```go +// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to +// implement Done and Err. It implements cancel by stopping its timer then +// delegating to cancelCtx.cancel. +type timerCtx struct { + cancelCtx + timer *time.Timer // Under cancelCtx.mu. + + deadline time.Time +} +``` + +用 WithTimeout 和 WithDeadline 本质都是生成一个 timerCtx。 + +```go +func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) { + if cur, ok := parent.Deadline(); ok && cur.Before(d) { + // 如果父节点的 dealine 更靠前,那当然以父节点的为准,当前节点的 deadline 可以抛弃 + return WithCancel(parent) + } + c := &timerCtx{ + cancelCtx: newCancelCtx(parent), + deadline: d, + } + + // 向上冒泡,把当前节点的 cancel 函数关联到父 cancelCtx 节点上 + propagateCancel(parent, c) + dur := time.Until(d) + if dur <= 0 { + c.cancel(true, DeadlineExceeded) // 已经超时了,退出吧 + return c, func() { c.cancel(false, Canceled) } + } + c.mu.Lock() + defer c.mu.Unlock() + if c.err == nil { // 说明父节点到现在还没有取消呢 + c.timer = time.AfterFunc(dur, func() { + c.cancel(true, DeadlineExceeded) // 这个方法到时间了之后会自动执行,当前的 goroutine 不会被阻塞 + }) + } + return c, func() { c.cancel(true, Canceled) } +} +``` + +每次执行都会创建新的 timer。 + +## 树形结构 + +为什么设计成树形结构呢。因为对于 fork-join 的模型(Go 的原地 go func 就是这种模型)来说,程序代码的执行本来就是树形的。在进入、退出某个子节点的时候,既要加新的数据,又不能影响父节点的数据,所以这种链式结构可以完美地匹配。 + +TODO,这里应该有一张大图 + +取消某个父节点,又能够用树的遍历算法将该取消指令传导到整棵树去。 + +TODO,这里应该有一张大图 + +## traps + +### cancelCtx 的性能问题 + +TODO + +### ctx 的打印 panic + +TODO + +# 总结 + +ctx 的结构显然是根据代码的执行模型来设计的,虽然设计得比较巧妙,但因为将取消和上下文携带功能混合在一起,在一些情况下还是会给我们埋些比较隐蔽的坑。使用时需要多多注意。 diff --git a/readme.md b/readme.md index 26598f1..febe4d2 100644 --- a/readme.md +++ b/readme.md @@ -25,3 +25,4 @@ 19. [x] [Semaphore](semaphore.md) 20. [x] [Memory Barrier](memory_barrier.md) 21. [ ] [Lock Free](lockfree.md) +22. [ ] [context](context.md) From 757191e7a437b5e131345d53547841ec974cdb9e Mon Sep 17 00:00:00 2001 From: Xargin Date: Wed, 1 Jul 2020 15:38:49 +0800 Subject: [PATCH 015/120] context done --- context.md | 95 +++++++++++++++++++++++++++++++++++++++++++++++++----- readme.md | 2 +- 2 files changed, 88 insertions(+), 9 deletions(-) diff --git a/context.md b/context.md index 8d96190..696017b 100644 --- a/context.md +++ b/context.md @@ -342,6 +342,35 @@ func parentCancelCtx(parent Context) (*cancelCtx, bool) { ### timerCtx 使用 +用 WithDeadline 和 WithTimeout 都可以生成一个 timerCtx: + +```go +package main + +import ( + "context" + "fmt" + "time" +) + +func main() { + // Pass a context with a timeout to tell a blocking function that it + // should abandon its work after the timeout elapses. + ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond) + defer cancel() + + select { + case <-time.After(1 * time.Second): + fmt.Println("overslept") + case <-ctx.Done(): + fmt.Println(ctx.Err()) // prints "context deadline exceeded" + } + +} +``` + +这是从官方的 example 里摘出来的例子,WithTimeout 其实底层是用 WithDeadline 实现的。 + ### timerCtx 分析 ```go @@ -356,7 +385,7 @@ type timerCtx struct { } ``` -用 WithTimeout 和 WithDeadline 本质都是生成一个 timerCtx。 +用 WithTimeout 和 WithDeadline 都会生成一个 timerCtx。 ```go func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) { @@ -387,27 +416,77 @@ func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) { } ``` -每次执行都会创建新的 timer。 +- 每次执行都会创建新的 timer +- 子节点的 deadline 一定不会超过父节点 +- 创建过程中发现已经过期了,立刻返回 ## 树形结构 为什么设计成树形结构呢。因为对于 fork-join 的模型(Go 的原地 go func 就是这种模型)来说,程序代码的执行本来就是树形的。在进入、退出某个子节点的时候,既要加新的数据,又不能影响父节点的数据,所以这种链式结构可以完美地匹配。 -TODO,这里应该有一张大图 - 取消某个父节点,又能够用树的遍历算法将该取消指令传导到整棵树去。 -TODO,这里应该有一张大图 - ## traps ### cancelCtx 的性能问题 -TODO +如果不通过 WithCancel 来复制通知 channel,大家都使用同一个 ctx.Done,那么实际上是在争一把大锁。 + +```go +package main + +import "context" +import "time" + +func main() { + ctx, _ := context.WithCancel(context.TODO()) + for i := 0; i < 100; i++ { + go func() { + select { + case <-ctx.Done(): + } + }() + } + time.Sleep(time.Hour) +} + +``` + +在一些场景可能会有性能问题。 ### ctx 的打印 panic -TODO +http 中的 ctx 还塞了 map,打印会造成 fatal。 + +```go +package main + +import ( + "fmt" + "net/http" + "reflect" +) + +func panic(w http.ResponseWriter, r *http.Request) { + server := r.Context().Value(http.ServerContextKey).(*http.Server) + v := reflect.ValueOf(*server) + + for i := 0; i < v.NumField(); i++ { + if name := v.Type().Field(i).Name; name != "activeConn" { + continue + } + fmt.Println(v.Field(i)) + } +} + +func main() { + http.HandleFunc("/", panic) + err := http.ListenAndServe(":9090", nil) + if err != nil { + fmt.Println(err) + } +} +``` # 总结 diff --git a/readme.md b/readme.md index febe4d2..acf40ed 100644 --- a/readme.md +++ b/readme.md @@ -25,4 +25,4 @@ 19. [x] [Semaphore](semaphore.md) 20. [x] [Memory Barrier](memory_barrier.md) 21. [ ] [Lock Free](lockfree.md) -22. [ ] [context](context.md) +22. [x] [context](context.md) From e9a0d501cc5477033d98ad0ca1c8e56ebf978cb5 Mon Sep 17 00:00:00 2001 From: Xargin Date: Wed, 1 Jul 2020 15:50:47 +0800 Subject: [PATCH 016/120] add cancel tree --- monodraw/cancelTree.monopic | Bin 0 -> 2370 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 monodraw/cancelTree.monopic diff --git a/monodraw/cancelTree.monopic b/monodraw/cancelTree.monopic new file mode 100644 index 0000000000000000000000000000000000000000..9bce25060e3f6b130f921cec183d50ed0eb7a377 GIT binary patch literal 2370 zcmV-I3BC6JO;1iwP)S1pABzY8000000u${WOLF77@hVeXQ&q%|0K9$M?6OR`RJ24} z%9BWkqUX(I%GbyNak|@)`-6~s_f6W~#rj{w6)pfR5 zWv1<>UE9Lq&!=PcErGJ)>1C5u@D=kujvlLQo6oaiu_~*BUJE;e0U7tn`9tNu+Inb- zVjphG#h_+8rTG z4G#y&ob!8@L^`>M>j7mdCr(q1{Vt@?b{odB%>%JYTNsgFyy*{DyqZz>qg%4V>|o z!`g_VFtdc&tc$!uEx@M3N=?BYa&{Q2XC4phc8}C*xJ|SNhXR(n`ll5|>|oyd35x6r z%#n3zW_+ZYOq*Bdd~RSBr%dTk&z)&`s>d8*eKT-9rI`wa2%iVyL)Z8f`d`9yF`*ijjatA7;TXpFT{1ZL{g4)iHH{syT9_>lV@F+ z(VTDcM=x3Sf-E`4NxpPl`npGu&Nl0(V(Y|eVnxmOyt(mp&7@j2_v>Zehyzc}E)VRP zOkxWUWnGtB&w^fv;o_i>0i|^y?@8YL1>-O9UVi}>7L4&Qjl0lBV2HP%Rd{DX%OSo3 z;X~{KacO~4aSN1AYZr)Cpw#qlinT_Lp;C^aQjRXYz)-2UvQViQ9V!jwl9OWf~?S%VH-LG`N%Kd8hOA-%=2h;=Rfp38L7Km?RMr@8< zY-G0sa7R60@K6F&05z0?N)&3%L`*E>%{V{?ka}p#If(c0o?>~^zST1`2`~jHJhbH; z#Cv>i@L^EjLYp_}Alw^y5xXo#Tad#G2jSibtC$EZDd3HTEWsD1L<$cag!`GHF_G{< zx(B9L3g0MrqwtM}HazWlOcRf=hYFzf&=mj*c_;v;0HuenAcb$Z3#+23r@4m|Aih7W z!2EphPy$o{wTHGrh7~yUk0SNl1wOzM7Afv$+`b@^*W}I{HN>iSUAiQzp0b-&|A|(8 zzJ0EL4l(K-QyyeLi1VBD1PyqK20X>!AI0FGnI>0d(o-=y)W?H~9Ld5hpzD+5D@(%3 zR}v`pN7E0+V#(N8EEyGx?F9VZ3&l3l2fFA+r6aPZ2vU+ZpQpczO8$`*-?QB}awIN! z|A|4zj&|^~0@J-Px`8o`2s2DDattfe7@%zO8t?*TD8VL4ldg)55^R-Vvjp2UX2t!W z_~l!(;oZ2TdxR`T`&RC`9-XQsLxncfmt$Wa68yHxtGwC9#=mGU52Ta|n43%kWmBj? zX@Y~C34Lix1p2l_Fzph7ZZ8ptNm2(?qZ?5=Gkbaygu+2cKN^N#mn{~uN)ewd&QlUH{4)mmhf#f zAGCAZxehIPltlMP(krm02)X676!|ySm(!A>c1qPWIV~LohUB!!Bz#Uwst0XZyaKf( z!l@;4+huH-mGm(yZ^AGW?J@2kFoaq888#U6LHmpK5a&`|+DetWtyE3xT&mVm6(Q5) zrK2mU(3MmeXjK?!RadrHrN%B+$v~&;q=Ebl67KLZ`Wc6h$#83bG~5wf>@$uCqhH*h z;U6++Wbd&l&0@P>gB2kz$k|Wy>CXPES0Q#-e?{MopZ5Ctf1bnn4*qHY0Qs$()Bpeg literal 0 HcmV?d00001 From 068ba0556fd3bf2be6fbd71a0de82f1a9e4325e2 Mon Sep 17 00:00:00 2001 From: Xargin Date: Wed, 1 Jul 2020 16:20:08 +0800 Subject: [PATCH 017/120] add ctx code tree --- monodraw/ctx_tree.monopic | Bin 0 -> 1913 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 monodraw/ctx_tree.monopic diff --git a/monodraw/ctx_tree.monopic b/monodraw/ctx_tree.monopic new file mode 100644 index 0000000000000000000000000000000000000000..81616fa1c1a82517198963463ac4406e4a4c9c2d GIT binary patch literal 1913 zcmV-<2Zs3nO;1iwP)S1pABzY8000000u$|A+m53+5dD=FPcsrL;S0v}>%Pv@zD~Cg zNt}co0=+=yvaLw_HTz-vC2JdOE(tvW#vw{)T8hA!qFhxzb+J9KR=W6;EsNZGd=;0! zVJxmz=`zUJ+Ip02z_P7Xl;y0j9v$1tc>mxf&7&ep6YCK`+bVv%upnBMH|kNaj>8Qn z3=Xy=rb)pP{@u^dcG>chMu}ke^Yfvii@m`t`)9K3 zIfAr)TGL0!vfuvXmW8W8e1ubLe$7#&(NDRqUjl-~NXWrL$OfKO5rM#rNIlY`>-UV6 zOSP1zV#l2Ecz%y2JUT!V&M8eu8HVx8He84|xUBPS$UEiiuOQj)7A%v(+dlFT><(moSeg%zDJoqri6@ zDkNTxBH$#Fm3X7b_N*Z_SLw4f0a$UF7e8VqpQc%~i8xc1V>?1f;YPA7xZsVSc;^Qo z9yEd5!mI$ScL?pg*V_i9il#+$RvPCVkHZFACQ zAm1;_VOM>gYogo?cHv8N4tbWAaJpVgTdbr}wMODiKKvh?mr8)8IFarmjf=l$)hxEy zBZ8}CvCY!`W~=oY7Dc$+im{hNSLTcc&lLCEnL@?^F9Ei%aF;?ev(1$#mw#idoIFv& zSqfsdF8QiQ>zw*v*}F)~cTbp37)P6ACz;@PyYws5H|4v0CT#V6v|_;`%~mW^R@t4X zS^&^f6c%Ywq`Ojze2l^#s<9zM2u0l<&p^ObAC1geA*DTBV?5QLp#kiC7=@2IgB%=0cbeFDJ98{)X`4-;# zl2=pdq|}a>?0&hTu>7()^}d)kVY1oBVWuZ6m&7^~CK@KoZJIR%>Iz*{%*am$i>&-= zt5LWE(b$m zAnh9}qXS}YF$IsR&;#FC1sWTwiTPenr9?)Dlj=LzHCErjbb1lS1EX~8;l8C3G_|w@ z4e8kP%+Q7A6pZer9#QHcy`>%%WBF*NQcs(KL8Tt|0;L{2#6!W6Z~{;bCoZ_%ny!nD z)pY$K887j!EaN5kp7G9{@e;RFWb-ed@e)TR4#3i@Ak~G+feC(J880CdGhW)aJII~9 zJIFN@5Ye%_j)=`;DHZyh>qx-7j-(UsyyZvM`(*jy#Brm#PD3k@9?-iab>@2A%0lS%-lk6%l%YGi7wpnKEby zL0xF98tJ;`4vbPmD-7c{tZrGIDjHNCHI@ zD59Wr4(DV2F}lBr;j-zV7pW)js{?RInF^z8D~&tXS*B8<>c~13aBMV7nd(!x;A-oZ zp4`5wIGDZkgq`E*hS*e8@_l!ei9H94qu~7K$DF;AjI#paN*15ELi?gUrQL4u}5%UJ+SG0#E<|zJIw} literal 0 HcmV?d00001 From 83bc809e89563dc40b9daea3e2c124adb629f27b Mon Sep 17 00:00:00 2001 From: Xargin Date: Wed, 1 Jul 2020 16:22:14 +0800 Subject: [PATCH 018/120] update context --- context.md | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) diff --git a/context.md b/context.md index 696017b..d8cec8a 100644 --- a/context.md +++ b/context.md @@ -422,10 +422,85 @@ func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) { ## 树形结构 -为什么设计成树形结构呢。因为对于 fork-join 的模型(Go 的原地 go func 就是这种模型)来说,程序代码的执行本来就是树形的。在进入、退出某个子节点的时候,既要加新的数据,又不能影响父节点的数据,所以这种链式结构可以完美地匹配。 +为什么设计成树形结构呢。因为对于 fork-join 的模型(Go 的原地 go func 就是这种模型)来说,程序代码的执行本来就是树形的。在进入、退出某个子节点的时候,既要加新的数据,又不能影响父节点的数据,所以这种链式树形结构可以完美地匹配。 + +```go + ┌───────────────────────────────┐ + │ ┌─┐ │ +┌────────────────────────┐ │ ╔═════════════════▶└─┘ │ +│ │ │ ║ ▲ │ +│ │ │ ║ ┃ │ +│ │ │ ║ ┏━━━━━━━━━━━━━┫ │ +│ │ │ ║ ┃ ┃ │ +│func() { ════════════╬═══╬═╝ ┃ ┗━━━━━┓ │ +│ │ │ ┃ ┃ │ +│ │ │ ┌─┐ ┃ │ +│ go func1(){}() ═════╬═══╬═════▶└─┘ ┃ │ +│ │ │ ┃ │ +│ │ │ ┃ │ +│ │ │ ┌─┐ │ +│ go func2(){═════════╬═══╬═════════════════════════▶└─┘ │ +│ │ │ ▲ │ +│ │ │ ┃ │ +│ │ │ ┃ │ +│ go func3(){}()══╬═══╬═════════╗ ┃ │ +│ │ │ ║ ┃ │ +│ │ │ ║ ┃ │ +│ │ │ ║ ┌─┐ │ +│ }() │ │ ╚═══════════════▶└─┘ │ +│} │ │ │ +│ │ │ │ +│ │ │ │ +│ │ │ │ +│ │ │ │ +│ │ │ │ +└────────────────────────┘ └───────────────────────────────┘ +``` 取消某个父节点,又能够用树的遍历算法将该取消指令传导到整棵树去。 +```go + ┌──────────┐ + │ emptyCtx │ + ├──────────┤ + │cancelCtx │ + └──────────┘ + ▲ + │ + │ + │ + │ + │ + ┌──────────┐ + │cancelCtx │ cancel here + └──────────┘ + ▲ ┃ + │ ┃ + │ ┃ + ┌────────────┴─────────────┐ ┃ + │ │ ┃ + │ ▢▢▢▢▢▢▢▢▢▢▢▢▢ ┃ + │ ▢▢▢▢▢ │ ▢▢▢▢▢ ┃ + │ ▢▢▢▢ │ ▢▢▢▢ ┃ +┌──────────┐ ▢▢▢▢ ┌──────────┐ ▢▢▢ ┃ +│cancelCtx │ ▢▢ │cancelCtx │◀━━━━━━━━━━━━━━━━━━┛ +└──────────┘ ▢▢ └──────────┘ ▢▢ + ▢▢ ▲ ▢▢ + ▢ ┌────────┴────┬────────────┐ ▢▢ + ▢ │ │ │ ▢ + ▢▢ ┌──────────┐ ┌──────────┐ ┌──────────┐ ▢ + ▢ │cancelCtx │ │cancelCtx │ │cancelCtx │ ▢ + ▢ └──────────┘ └──────────┘ └─────────▢▢▢ + ▢ ▢▢▢▢ + ▢▢ ▢▢▢▢▢ + ▢ ▢▢▢▢▢ + ▢▢▢ ▢▢▢▢ + ▢▢▢▢▢▢ ▢▢▢▢▢▢ + ▢▢▢▢▢▢▢▢▢▢▢▢ +``` + +如图,树上某个节点 cancel 之后,会顺便调用其 children 数组中所有子节点的取消函数,该取消操作一直传导到叶子节点。 + ## traps ### cancelCtx 的性能问题 From 5cd2638d7ee6e74d76ddb8e7bc3d19d8ce4c9b24 Mon Sep 17 00:00:00 2001 From: mcrwayfun Date: Sat, 5 Sep 2020 16:23:56 +0800 Subject: [PATCH 019/120] =?UTF-8?q?=E4=BF=AE=E6=94=B9map=E6=89=A9=E5=AE=B9?= =?UTF-8?q?=E6=8F=8F=E8=BF=B0=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- map.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/map.md b/map.md index f1fda22..f90f8c4 100644 --- a/map.md +++ b/map.md @@ -870,7 +870,7 @@ func evacuate(t *maptype, h *hmap, oldbucket uintptr) { // 1111 // 所以实际上这个就是 // xxx1xxx & 1000 > 0 - // 说明这个元素在扩容后一定会去上半区 + // 说明这个元素在扩容后一定会去下半区 // 所以就是 useY 了 if hash&newbit != 0 { useY = 1 From b8319ceddb63afbf968256103c9630dbf85fe76e Mon Sep 17 00:00:00 2001 From: mcrwayfun Date: Sat, 5 Sep 2020 16:26:34 +0800 Subject: [PATCH 020/120] =?UTF-8?q?=E4=BF=AE=E6=94=B9map=E6=89=A9=E5=AE=B9?= =?UTF-8?q?=E6=8F=8F=E8=BF=B0=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- map.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/map.md b/map.md index f90f8c4..dbe8ac6 100644 --- a/map.md +++ b/map.md @@ -870,7 +870,7 @@ func evacuate(t *maptype, h *hmap, oldbucket uintptr) { // 1111 // 所以实际上这个就是 // xxx1xxx & 1000 > 0 - // 说明这个元素在扩容后一定会去下半区 + // 说明这个元素在扩容后一定会去下半区,即Y部分 // 所以就是 useY 了 if hash&newbit != 0 { useY = 1 From 9f85a469f5d6e8a666e21d4b64a924caa1410d78 Mon Sep 17 00:00:00 2001 From: mcrwayfun Date: Tue, 8 Sep 2020 08:24:52 +0800 Subject: [PATCH 021/120] update parentCancelCtx --- context.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/context.md b/context.md index d8cec8a..4885a38 100644 --- a/context.md +++ b/context.md @@ -319,7 +319,7 @@ func propagateCancel(parent Context, child canceler) { } ``` -parentCancelCtx 只识别 context 包内的三种类型,如果用户自己的类实现了 context.Context 接口,或者把 ctx 包在了自己的类型内,那这里始终返回的是 nil,false。 +parentCancelCtx 只识别 context 包内的三种类型,如果用户自己的类实现了 context.Context 接口,或者把 ctx 包在了自己的类型内,或者是 emptyCtx,那这里始终返回的是 nil,false。 ```go func parentCancelCtx(parent Context) (*cancelCtx, bool) { From 5456cfae384823727c83e16444995a3bc55b1adb Mon Sep 17 00:00:00 2001 From: wziww Date: Thu, 19 Nov 2020 14:34:58 +0800 Subject: [PATCH 022/120] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E6=80=81=20false=20sharing=20=E4=BE=8B=E5=AD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- memory_barrier.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/memory_barrier.md b/memory_barrier.md index 4c208d7..42b7f76 100644 --- a/memory_barrier.md +++ b/memory_barrier.md @@ -460,6 +460,20 @@ var semtable [semTabSize]struct { 用户态的代码对 false sharing 其实关注的比较少。 +例: +sync/pool.go + +```go +type poolLocal struct { + poolLocalInternal + + // Prevents false sharing on widespread platforms with + // 128 mod (cache line size) = 0 . + pad [128 - unsafe.Sizeof(poolLocalInternal{})%128]byte +} +``` + + ## runtime 中的 publicationBarrier TODO From 701c4fc83eb00ecdf39fcf8e05b4dab9bfcfe6fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A4=9A=E6=99=96?= Date: Wed, 25 Nov 2020 11:44:46 +0800 Subject: [PATCH 023/120] add new link on memory barrier --- memory_barrier.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/memory_barrier.md b/memory_barrier.md index 42b7f76..94383c0 100644 --- a/memory_barrier.md +++ b/memory_barrier.md @@ -517,3 +517,5 @@ https://software.intel.com/en-us/articles/detect-and-avoid-memory-bottlenecks#_M https://stackoverflow.com/questions/29880015/lock-prefix-vs-mesi-protocol https://github.com/torvalds/linux/blob/master/Documentation/memory-barriers.txt + +http://www.overbyte.com.au/misc/Lesson3/CacheFun.html From a1dcf5ad93df8de7b1cb501def6e3d702b04cc7f Mon Sep 17 00:00:00 2001 From: wziww Date: Tue, 1 Dec 2020 09:59:30 +0800 Subject: [PATCH 024/120] add `ausyscall` command notes --- syscall.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syscall.md b/syscall.md index 4d05cdb..e1a44bc 100644 --- a/syscall.md +++ b/syscall.md @@ -305,7 +305,7 @@ TEXT runtime·read(SB),NOSPLIT,$0-28 RET ``` -下面是所有 runtime 另外定义的 syscall 列表: +下面是所有 runtime 另外定义的 syscall 列表(不同架构的机器映射各不相同,linux 下具体可通过 ausyscall --dump 命令获取系统调用映射表进行查看): ```go #define SYS_read 0 From ca0c426d09f7122375dbeed6ca0400965c804971 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A4=9A=E6=99=96?= Date: Wed, 2 Dec 2020 13:47:43 +0800 Subject: [PATCH 025/120] update defer --- 1.14/defer.md | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/1.14/defer.md b/1.14/defer.md index ed7441f..8a43e41 100644 --- a/1.14/defer.md +++ b/1.14/defer.md @@ -1,3 +1,43 @@ # 1.14 defer -TODO +在 Go 1.14 中,增加了一种新的 defer 实现:open coded defer。当函数内 defer 不超过 8 个时,则会使用这种实现。 + +```go +package main + +import "fmt" + +var i = 100 + +func main() { + if i == 0 { + defer fmt.Println("1") + } + + if i == 1 { + defer fmt.Println("2") + } + + if i == 2 { + defer fmt.Println("3") + } + + if i == 3 { + defer fmt.Println("4") + } + + if i == 4 { + defer fmt.Println("5") + } + + if i == 5 { + defer fmt.Println("6") + } +} + +``` + +可以分析上面的代码生成的汇编来理解这个新版的 open coded defer 到底是怎么实现的: + +``` +``` From 2c21bf389e61ce5b9e2a75fd85b535c241fd1b17 Mon Sep 17 00:00:00 2001 From: wziww Date: Wed, 2 Dec 2020 17:39:49 +0800 Subject: [PATCH 026/120] stack dump --- runtime_stack.md | 282 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 282 insertions(+) create mode 100644 runtime_stack.md diff --git a/runtime_stack.md b/runtime_stack.md new file mode 100644 index 0000000..8694617 --- /dev/null +++ b/runtime_stack.md @@ -0,0 +1,282 @@ +### Stack Dump + +> 引言:在工程中经常需要在异常发生的时候打印相应的日志,或利用成熟的第三方日志库,或自己进行简单的实现,在相应日志输出的时候,常常会有不同的级别,当遇到 error 或者 panic 之类的情况的时候,通常需要输出相应堆栈信息来辅助我们进行问题的定位以及解决,那么,关于堆栈这块的信息我们该怎么获取,获取的时候需要注意什么,以及堆栈的获取会对程序造成什么影响,会带来多大的性能损耗?日志库的使用或者进行相关配置我们该怎么选择?本章的目的就是深入一下 golang 中 stack dump 的细节 + +#### STACK APIs +- debug.Stack + - debug.PrintStack +- runtime.Stack +- runtime.Callers + - runtime.Caller + +##### debug.Stack & debug.PrintStack +```go +package main + +import ( + "fmt" + "runtime/debug" +) + +func main() { + fmt.Println(debug.Stack()) +} +``` +runtime/debug/stack.go +```go +func Stack() []byte { + buf := make([]byte, 1024) + for { + /* + * 可以看到实质上 debug.Stack 调用的就是 runtime.Stack 方法 + * 并且会不断扩大 buf 大小直到读取完所有 stack 信息 + */ + n := runtime.Stack(buf, false) + if n < len(buf) { + return buf[:n] + } + buf = make([]byte, 2*len(buf)) + } +} +``` +而 runtime.Stack 方法入参为 `buf []byte` 以及 `all bool` 两个参数 +- `buf []byte` 是用来接收 stack dump 信息的 +- `all bool` 参数则是用来决定是否获取所有协程的堆栈 + +`buf` 参数比较好理解,而 `all` 参数如果设置为获取所有协程信息,则会触发 `STW` 操作,这是一个代价十分巨大的操作,整个 runtime 会被暂停以获取所有协程的堆栈信息而后再进行恢复, + +runtime/mprof.go +```go +func Stack(buf []byte, all bool) int { + if all { + stopTheWorld("stack trace") + } + + n := 0 + if len(buf) > 0 { + gp := getg() + sp := getcallersp() + pc := getcallerpc() + systemstack(func() { + g0 := getg() + // Force traceback=1 to override GOTRACEBACK setting, + // so that Stack's results are consistent. + // GOTRACEBACK is only about crash dumps. + g0.m.traceback = 1 + g0.writebuf = buf[0:0:len(buf)] + goroutineheader(gp) + traceback(pc, sp, 0, gp) + if all { + tracebackothers(gp) + } + g0.m.traceback = 0 + n = len(g0.writebuf) + g0.writebuf = nil + }) + } + + if all { + startTheWorld() + } + return n +} +``` +分析下具体调用过程,首先判断是否进行 `STW`,而后通过`getg()` 获取当前 goroutine +- `getcallersp()`: 汇编实现,获取当前调用 stack pointer (SP) +- `getcallerpc()`: 汇编实现,获取当前调用方的程序计数器 program counter (PC) +- `systemstack`: 切换到系统栈执行传入函数(如果当前就在系统栈则直接执行),可以看到在传入的函数中再次调用 getg() 获取到了 g0 (主协程)。并且将传入的 buf 放置在了 g0 的 writebuf 上(slice 底层 data 地址不变) +- `goroutineheader` 获取 gp 协程头部信息 + +runtime/traceback.go +```go +func goroutineheader(gp *g) { + gpstatus := readgstatus(gp) + + isScan := gpstatus&_Gscan != 0 + gpstatus &^= _Gscan // drop the scan bit + + // Basic string status + var status string + if 0 <= gpstatus && gpstatus < uint32(len(gStatusStrings)) { + status = gStatusStrings[gpstatus] + } else { + status = "???" + } + + // Override. + if gpstatus == _Gwaiting && gp.waitreason != waitReasonZero { + status = gp.waitreason.String() + } + + // approx time the G is blocked, in minutes + var waitfor int64 + if (gpstatus == _Gwaiting || gpstatus == _Gsyscall) && gp.waitsince != 0 { + waitfor = (nanotime() - gp.waitsince) / 60e9 + } + print("goroutine ", gp.goid, " [", status) + if isScan { + print(" (scan)") + } + if waitfor >= 1 { + print(", ", waitfor, " minutes") + } + if gp.lockedm != 0 { + print(", locked to thread") + } + print("]:\n") +} +``` +函数内部直接调用内置函数 `print` 将信息输入到刚才设置的 `writebuf` 中,`print` 内置函数具体实现可以查看 `runtime/print.go`,内部是将 `print` 的值通过 copy 复制到当前协程的 `writebuf` 上 + +runtime/print.go +```go +func gwrite(b []byte) { + if len(b) == 0 { + return + } + recordForPanic(b) + gp := getg() + // Don't use the writebuf if gp.m is dying. We want anything + // written through gwrite to appear in the terminal rather + // than be written to in some buffer, if we're in a panicking state. + // Note that we can't just clear writebuf in the gp.m.dying case + // because a panic isn't allowed to have any write barriers. + if gp == nil || gp.writebuf == nil || gp.m.dying > 0 { + writeErr(b) + return + } + + n := copy(gp.writebuf[len(gp.writebuf):cap(gp.writebuf)], b) + gp.writebuf = gp.writebuf[:len(gp.writebuf)+n] +} +``` +继续回到之前的分析 +- `traceback` + +```go +func traceback1(pc, sp, lr uintptr, gp *g, flags uint) { + // If the goroutine is in cgo, and we have a cgo traceback, print that. + if iscgo && gp.m != nil && gp.m.ncgo > 0 && gp.syscallsp != 0 && gp.m.cgoCallers != nil && gp.m.cgoCallers[0] != 0 { + // Lock cgoCallers so that a signal handler won't + // change it, copy the array, reset it, unlock it. + // We are locked to the thread and are not running + // concurrently with a signal handler. + // We just have to stop a signal handler from interrupting + // in the middle of our copy. + atomic.Store(&gp.m.cgoCallersUse, 1) + cgoCallers := *gp.m.cgoCallers + gp.m.cgoCallers[0] = 0 + atomic.Store(&gp.m.cgoCallersUse, 0) + + printCgoTraceback(&cgoCallers) + } + + var n int + if readgstatus(gp)&^_Gscan == _Gsyscall { + // Override registers if blocked in system call. + pc = gp.syscallpc + sp = gp.syscallsp + flags &^= _TraceTrap + } + // Print traceback. By default, omits runtime frames. + // If that means we print nothing at all, repeat forcing all frames printed. + n = gentraceback(pc, sp, lr, gp, 0, nil, _TracebackMaxFrames, nil, nil, flags) + if n == 0 && (flags&_TraceRuntimeFrames) == 0 { + n = gentraceback(pc, sp, lr, gp, 0, nil, _TracebackMaxFrames, nil, nil, flags|_TraceRuntimeFrames) + } + if n == _TracebackMaxFrames { + print("...additional frames elided...\n") + } + printcreatedby(gp) + + if gp.ancestors == nil { + return + } + for _, ancestor := range *gp.ancestors { + printAncestorTraceback(ancestor) + } +} +``` +函数首先判断了 `cgo` 相关信息,如果是处于 `cgo` 的模式中,那么输出一下 `cgo` 的堆栈信息(如果有的话) + +而后调用关键的 `gentraceback` 函数 +```go +func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max int, callback func(*stkframe, unsafe.Pointer) bool, v unsafe.Pointer, flags uint) int +``` +可以看到,通过 `debug.Stack` 函数调用时,max 参数传入的值为 `_TracebackMaxFrames` +```go +// The maximum number of frames we print for a traceback +const _TracebackMaxFrames = 100 +``` +最大帧数为固定值 100,意味着在 stack dump 时需要追溯至多 100 个栈帧的信息,O(N) 复杂度。 + +##### runtime.Caller & runtime.Callers +runtime/extern.go +```go +// Caller reports file and line number information about function invocations on +// the calling goroutine's stack. The argument skip is the number of stack frames +// to ascend, with 0 identifying the caller of Caller. (For historical reasons the +// meaning of skip differs between Caller and Callers.) The return values report the +// program counter, file name, and line number within the file of the corresponding +// call. The boolean ok is false if it was not possible to recover the information. +func Caller(skip int) (pc uintptr, file string, line int, ok bool) { + rpc := make([]uintptr, 1) + n := callers(skip+1, rpc[:]) + if n < 1 { + return + } + frame, _ := CallersFrames(rpc).Next() + return frame.PC, frame.File, frame.Line, frame.PC != 0 +} + +// Callers fills the slice pc with the return program counters of function invocations +// on the calling goroutine's stack. The argument skip is the number of stack frames +// to skip before recording in pc, with 0 identifying the frame for Callers itself and +// 1 identifying the caller of Callers. +// It returns the number of entries written to pc. +// +// To translate these PCs into symbolic information such as function +// names and line numbers, use CallersFrames. CallersFrames accounts +// for inlined functions and adjusts the return program counters into +// call program counters. Iterating over the returned slice of PCs +// directly is discouraged, as is using FuncForPC on any of the +// returned PCs, since these cannot account for inlining or return +// program counter adjustment. +func Callers(skip int, pc []uintptr) int { + // runtime.callers uses pc.array==nil as a signal + // to print a stack trace. Pick off 0-length pc here + // so that we don't let a nil pc slice get to it. + if len(pc) == 0 { + return 0 + } + return callers(skip, pc) +} +``` +以上两个函数最终调用的都是 `callers` 函数 +```go +func callers(skip int, pcbuf []uintptr) int { + sp := getcallersp() + pc := getcallerpc() + gp := getg() + var n int + systemstack(func() { + n = gentraceback(pc, sp, 0, gp, skip, &pcbuf[0], len(pcbuf), nil, nil, 0) + }) + return n +} +``` +`callers` 函数大部分逻辑和之前的 `runtime.Stack`函数中 `traceback` 大同小异,唯一不同的就是 +`gentraceback` 调用的入参 `max` 参数是人为设置的,并且进针对当前 `goroutine` 进行 +#### 总结 +- stack dump 操作是否会有性能损耗,损耗在哪儿(是 + - 与调用方式有关,如果是通过类似 `runtime.Stack` 方法打印所有堆栈信息的, 会触发 `STW` 操作,是一个代价比较大的操作 + - 与需要追溯的栈帧数量有关 + - 会触发协程的切换 + - 更细致化一些例如 `stack dump` 出的信息会进行 `copy` 操作等等 + +- 如何更优雅地获取 `stack dump` 信息? + - 以 `runtime.Callers` 与 `runtime.CallersFrames` 相结合代替 `runtime.Stack` 、`debug.Stack`,避免使用默认的 `stack dump` 配置,根据自己的业务情况选择合适的栈帧数量(如不同级别的日志打印不同数量的栈帧信息),知名的开源日志库 `github.com/uber-go/zap` 正是使用这种思路 + +---- +###### 参考: +[zap stacktrace 实现](https://homes.cs.washington.edu/~bornholt/post/memory-models.html) \ No newline at end of file From 740417c0feaa8ed4c1003bb586986e0f0b3f2f47 Mon Sep 17 00:00:00 2001 From: wziww Date: Wed, 2 Dec 2020 17:43:37 +0800 Subject: [PATCH 027/120] =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=A0=87=E9=A2=98?= =?UTF-8?q?=E5=A4=A7=E5=B0=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- runtime_stack.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/runtime_stack.md b/runtime_stack.md index 8694617..939871e 100644 --- a/runtime_stack.md +++ b/runtime_stack.md @@ -1,15 +1,15 @@ -### Stack Dump +## Stack Dump > 引言:在工程中经常需要在异常发生的时候打印相应的日志,或利用成熟的第三方日志库,或自己进行简单的实现,在相应日志输出的时候,常常会有不同的级别,当遇到 error 或者 panic 之类的情况的时候,通常需要输出相应堆栈信息来辅助我们进行问题的定位以及解决,那么,关于堆栈这块的信息我们该怎么获取,获取的时候需要注意什么,以及堆栈的获取会对程序造成什么影响,会带来多大的性能损耗?日志库的使用或者进行相关配置我们该怎么选择?本章的目的就是深入一下 golang 中 stack dump 的细节 -#### STACK APIs +### STACK APIs - debug.Stack - debug.PrintStack - runtime.Stack - runtime.Callers - runtime.Caller -##### debug.Stack & debug.PrintStack +#### debug.Stack & debug.PrintStack ```go package main @@ -210,7 +210,7 @@ const _TracebackMaxFrames = 100 ``` 最大帧数为固定值 100,意味着在 stack dump 时需要追溯至多 100 个栈帧的信息,O(N) 复杂度。 -##### runtime.Caller & runtime.Callers +#### runtime.Caller & runtime.Callers runtime/extern.go ```go // Caller reports file and line number information about function invocations on @@ -278,5 +278,5 @@ func callers(skip int, pcbuf []uintptr) int { - 以 `runtime.Callers` 与 `runtime.CallersFrames` 相结合代替 `runtime.Stack` 、`debug.Stack`,避免使用默认的 `stack dump` 配置,根据自己的业务情况选择合适的栈帧数量(如不同级别的日志打印不同数量的栈帧信息),知名的开源日志库 `github.com/uber-go/zap` 正是使用这种思路 ---- -###### 参考: +##### 参考: [zap stacktrace 实现](https://homes.cs.washington.edu/~bornholt/post/memory-models.html) \ No newline at end of file From 4de62a2bd1e3796ace2fa751d46b287596feb91b Mon Sep 17 00:00:00 2001 From: wziww Date: Wed, 2 Dec 2020 17:44:47 +0800 Subject: [PATCH 028/120] =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=A0=87=E9=A2=98?= =?UTF-8?q?=E5=A4=A7=E5=B0=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- runtime_stack.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/runtime_stack.md b/runtime_stack.md index 939871e..96dcdfc 100644 --- a/runtime_stack.md +++ b/runtime_stack.md @@ -1,15 +1,15 @@ -## Stack Dump +# Stack Dump > 引言:在工程中经常需要在异常发生的时候打印相应的日志,或利用成熟的第三方日志库,或自己进行简单的实现,在相应日志输出的时候,常常会有不同的级别,当遇到 error 或者 panic 之类的情况的时候,通常需要输出相应堆栈信息来辅助我们进行问题的定位以及解决,那么,关于堆栈这块的信息我们该怎么获取,获取的时候需要注意什么,以及堆栈的获取会对程序造成什么影响,会带来多大的性能损耗?日志库的使用或者进行相关配置我们该怎么选择?本章的目的就是深入一下 golang 中 stack dump 的细节 -### STACK APIs +## STACK APIs - debug.Stack - debug.PrintStack - runtime.Stack - runtime.Callers - runtime.Caller -#### debug.Stack & debug.PrintStack +## debug.Stack & debug.PrintStack ```go package main @@ -210,7 +210,7 @@ const _TracebackMaxFrames = 100 ``` 最大帧数为固定值 100,意味着在 stack dump 时需要追溯至多 100 个栈帧的信息,O(N) 复杂度。 -#### runtime.Caller & runtime.Callers +## runtime.Caller & runtime.Callers runtime/extern.go ```go // Caller reports file and line number information about function invocations on @@ -267,7 +267,7 @@ func callers(skip int, pcbuf []uintptr) int { ``` `callers` 函数大部分逻辑和之前的 `runtime.Stack`函数中 `traceback` 大同小异,唯一不同的就是 `gentraceback` 调用的入参 `max` 参数是人为设置的,并且进针对当前 `goroutine` 进行 -#### 总结 +## 总结 - stack dump 操作是否会有性能损耗,损耗在哪儿(是 - 与调用方式有关,如果是通过类似 `runtime.Stack` 方法打印所有堆栈信息的, 会触发 `STW` 操作,是一个代价比较大的操作 - 与需要追溯的栈帧数量有关 @@ -278,5 +278,5 @@ func callers(skip int, pcbuf []uintptr) int { - 以 `runtime.Callers` 与 `runtime.CallersFrames` 相结合代替 `runtime.Stack` 、`debug.Stack`,避免使用默认的 `stack dump` 配置,根据自己的业务情况选择合适的栈帧数量(如不同级别的日志打印不同数量的栈帧信息),知名的开源日志库 `github.com/uber-go/zap` 正是使用这种思路 ---- -##### 参考: +### 参考: [zap stacktrace 实现](https://homes.cs.washington.edu/~bornholt/post/memory-models.html) \ No newline at end of file From b42978cd073ef14a2b2dc3bc47558ff2be90ff0b Mon Sep 17 00:00:00 2001 From: wziww Date: Wed, 2 Dec 2020 17:45:25 +0800 Subject: [PATCH 029/120] fix --- runtime_stack.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime_stack.md b/runtime_stack.md index 96dcdfc..a873e22 100644 --- a/runtime_stack.md +++ b/runtime_stack.md @@ -268,7 +268,7 @@ func callers(skip int, pcbuf []uintptr) int { `callers` 函数大部分逻辑和之前的 `runtime.Stack`函数中 `traceback` 大同小异,唯一不同的就是 `gentraceback` 调用的入参 `max` 参数是人为设置的,并且进针对当前 `goroutine` 进行 ## 总结 -- stack dump 操作是否会有性能损耗,损耗在哪儿(是 +- stack dump 操作是否会有性能损耗(是),损耗在哪儿 - 与调用方式有关,如果是通过类似 `runtime.Stack` 方法打印所有堆栈信息的, 会触发 `STW` 操作,是一个代价比较大的操作 - 与需要追溯的栈帧数量有关 - 会触发协程的切换 From 4c4b32b8a975bb1934734b91ccba5548078b5cf2 Mon Sep 17 00:00:00 2001 From: wziww Date: Wed, 2 Dec 2020 17:46:14 +0800 Subject: [PATCH 030/120] fix --- runtime_stack.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime_stack.md b/runtime_stack.md index a873e22..ad35e12 100644 --- a/runtime_stack.md +++ b/runtime_stack.md @@ -266,7 +266,7 @@ func callers(skip int, pcbuf []uintptr) int { } ``` `callers` 函数大部分逻辑和之前的 `runtime.Stack`函数中 `traceback` 大同小异,唯一不同的就是 -`gentraceback` 调用的入参 `max` 参数是人为设置的,并且进针对当前 `goroutine` 进行 +`gentraceback` 调用的入参 `max` 参数是人为设置的,并且仅针对当前 `goroutine` 进行 ## 总结 - stack dump 操作是否会有性能损耗(是),损耗在哪儿 - 与调用方式有关,如果是通过类似 `runtime.Stack` 方法打印所有堆栈信息的, 会触发 `STW` 操作,是一个代价比较大的操作 From 4d7bdd4310797729d6d1e571557223912c75a697 Mon Sep 17 00:00:00 2001 From: wziww Date: Wed, 2 Dec 2020 17:51:32 +0800 Subject: [PATCH 031/120] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=9B=AE=E5=BD=95?= =?UTF-8?q?=E7=AB=A0=E8=8A=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- readme.md | 1 + 1 file changed, 1 insertion(+) diff --git a/readme.md b/readme.md index acf40ed..f00d60c 100644 --- a/readme.md +++ b/readme.md @@ -26,3 +26,4 @@ 20. [x] [Memory Barrier](memory_barrier.md) 21. [ ] [Lock Free](lockfree.md) 22. [x] [context](context.md) +23. [x] [stack dump](runtime_stack.md) From fffb8819cada8305aa6adefa72287ef9315e1466 Mon Sep 17 00:00:00 2001 From: wziww Date: Wed, 2 Dec 2020 17:53:26 +0800 Subject: [PATCH 032/120] fix link --- runtime_stack.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime_stack.md b/runtime_stack.md index ad35e12..a92b9b1 100644 --- a/runtime_stack.md +++ b/runtime_stack.md @@ -279,4 +279,4 @@ func callers(skip int, pcbuf []uintptr) int { ---- ### 参考: -[zap stacktrace 实现](https://homes.cs.washington.edu/~bornholt/post/memory-models.html) \ No newline at end of file +[zap stacktrace 实现](https://github.com/uber-go/zap/blob/v1.16.0/stacktrace.go) \ No newline at end of file From bc9c973515e6bc639bd7ddffaba85f73c84a5fe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A4=9A=E6=99=96?= Date: Wed, 2 Dec 2020 17:55:01 +0800 Subject: [PATCH 033/120] update defer --- 1.14/defer.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/1.14/defer.md b/1.14/defer.md index 4c9a447..66de021 100644 --- a/1.14/defer.md +++ b/1.14/defer.md @@ -176,7 +176,8 @@ FUNCTION END, FLAG CHECKS, 这里要检查上面设置的所有的 open coded de 0x02b7 00695 (open-coded-defer.go:31) ADDQ $352, SP 0x02be 00702 (open-coded-defer.go:31) RET -DEFER 逻辑执行部分: +DEFER 逻辑执行部分 + // defer fmt.Println("6"): 0x02bf 00703 (open-coded-defer.go:31) ANDL $-2, DX 0x02c2 00706 (open-coded-defer.go:31) MOVB DL, ""..autotmp_24+55(SP) 0x02c6 00710 (open-coded-defer.go:31) MOVQ ""..autotmp_26+320(SP), AX @@ -188,6 +189,7 @@ DEFER 逻辑执行部分: 0x02ec 00748 (open-coded-defer.go:31) CALL fmt.Println(SB) 0x02f1 00753 (open-coded-defer.go:31) JMP 687 + // defer fmt.Println("5"): 0x02f3 00755 (open-coded-defer.go:31) ANDL $-3, DX 0x02f6 00758 (open-coded-defer.go:31) MOVB DL, ""..autotmp_37+54(SP) 0x02fa 00762 (open-coded-defer.go:31) MOVB DL, ""..autotmp_24+55(SP) @@ -201,6 +203,7 @@ DEFER 逻辑执行部分: 0x0329 00809 (open-coded-defer.go:31) MOVBLZX ""..autotmp_37+54(SP), DX 0x032e 00814 (open-coded-defer.go:31) JMP 682 + // defer fmt.Println("4"): 0x0333 00819 (open-coded-defer.go:31) ANDL $-5, DX 0x0336 00822 (open-coded-defer.go:31) MOVB DL, ""..autotmp_37+54(SP) 0x033a 00826 (open-coded-defer.go:31) MOVB DL, ""..autotmp_24+55(SP) @@ -214,6 +217,7 @@ DEFER 逻辑执行部分: 0x0369 00873 (open-coded-defer.go:31) MOVBLZX ""..autotmp_37+54(SP), DX 0x036e 00878 (open-coded-defer.go:31) JMP 677 + // defer fmt.Println("3"): 0x0373 00883 (open-coded-defer.go:31) ANDL $-9, DX 0x0376 00886 (open-coded-defer.go:31) MOVB DL, ""..autotmp_37+54(SP) 0x037a 00890 (open-coded-defer.go:31) MOVB DL, ""..autotmp_24+55(SP) @@ -227,6 +231,7 @@ DEFER 逻辑执行部分: 0x03a9 00937 (open-coded-defer.go:31) MOVBLZX ""..autotmp_37+54(SP), DX 0x03ae 00942 (open-coded-defer.go:31) JMP 668 + // defer fmt.Println("2"): 0x03b3 00947 (open-coded-defer.go:31) ANDL $-17, DX 0x03b6 00950 (open-coded-defer.go:31) MOVB DL, ""..autotmp_37+54(SP) 0x03ba 00954 (open-coded-defer.go:31) MOVB DL, ""..autotmp_24+55(SP) @@ -240,6 +245,7 @@ DEFER 逻辑执行部分: 0x03e9 01001 (open-coded-defer.go:31) MOVBLZX ""..autotmp_37+54(SP), DX 0x03ee 01006 (open-coded-defer.go:31) JMP 659 + // defer fmt.Println("1"): 0x03f3 01011 (open-coded-defer.go:31) ANDL $-33, DX 0x03f6 01014 (open-coded-defer.go:31) MOVB DL, ""..autotmp_37+54(SP) 0x03fa 01018 (open-coded-defer.go:31) MOVB DL, ""..autotmp_24+55(SP) @@ -258,10 +264,13 @@ DEFER 逻辑执行部分: 0x043a 01082 (open-coded-defer.go:31) LEAQ fmt.Println·f(SB), CX 0x0441 01089 (open-coded-defer.go:8) JMP 188 +这段代码没什么用,因为不会跳过来的,可能是可以优化的一个点 0x0446 01094 (open-coded-defer.go:8) CALL runtime.deferreturn(SB) 0x044b 01099 (open-coded-defer.go:8) MOVQ 344(SP), BP 0x0453 01107 (open-coded-defer.go:8) ADDQ $352, SP 0x045a 01114 (open-coded-defer.go:8) RET + +函数 prologue 栈检查的时候可能会跳到这里 0x045b 01115 (open-coded-defer.go:8) NOP 0x045b 01115 (open-coded-defer.go:7) CALL runtime.morestack_noctxt(SB) 0x0460 01120 (open-coded-defer.go:7) JMP 0 From cda94e98493587cdd65a94e1c02a410564478a8d Mon Sep 17 00:00:00 2001 From: Xargin Date: Thu, 3 Dec 2020 00:02:07 +0800 Subject: [PATCH 034/120] update defer --- 1.14/defer.md | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/1.14/defer.md b/1.14/defer.md index 66de021..bb7ed3b 100644 --- a/1.14/defer.md +++ b/1.14/defer.md @@ -1,5 +1,7 @@ # 1.14 defer +## 正常处理流程 + 在 Go 1.14 中,增加了一种新的 defer 实现:open coded defer。当函数内 defer 不超过 8 个时,则会使用这种实现。 ```go @@ -264,7 +266,7 @@ DEFER 逻辑执行部分 0x043a 01082 (open-coded-defer.go:31) LEAQ fmt.Println·f(SB), CX 0x0441 01089 (open-coded-defer.go:8) JMP 188 -这段代码没什么用,因为不会跳过来的,可能是可以优化的一个点 +这段代码没什么用,只有在 panic 时,才可能跳到这里,但本例中无 panic 0x0446 01094 (open-coded-defer.go:8) CALL runtime.deferreturn(SB) 0x044b 01099 (open-coded-defer.go:8) MOVQ 344(SP), BP 0x0453 01107 (open-coded-defer.go:8) ADDQ $352, SP @@ -275,3 +277,34 @@ DEFER 逻辑执行部分 0x045b 01115 (open-coded-defer.go:7) CALL runtime.morestack_noctxt(SB) 0x0460 01120 (open-coded-defer.go:7) JMP 0 ``` + +超过 8 个 defer 时会退化回以前的 defer 链,也可以观察一下: + +```go +package main + +func main() { + defer println(1) + defer println(1) + defer println(1) + defer println(1) + defer println(1) + defer println(1) + defer println(1) + defer println(1) + defer println(1) + defer println(1) +} +``` + +编译出的汇编就不贴了,和以前的没什么区别。 + +可以做个简单的总结了: + +* open coded defer 在函数内部总 defer 数量少于 8 时才会使用,大于 8 时会退化回老的 defer 链,这个权衡是考虑到程序文件的体积(个人觉得应该也有栈膨胀的考虑在) +* 使用 open coded defer 时,在进入函数内的每个 block 时,都会设置这个 block 相应的 bit(ORL 指令),在执行到相应的 defer 语句时,把 defer 语句的参数和调用函数地址推到栈的相应位置 +* 在栈上需要为这些 defer 调用的参数、函数地址,以及这个用来记录 defer bits 的 byte 预留空间,所以毫无疑问,函数的 framesize 会变大 + +参考资料: + +1. [open coded defer](https://github.com/golang/proposal/blob/master/design/34481-opencoded-defers.md) From 5314c42da5e253c2f34cf520a57d009f835cf9a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A4=9A=E6=99=96?= Date: Thu, 3 Dec 2020 17:49:57 +0800 Subject: [PATCH 035/120] 1.14 defer done --- 1.14/defer.md | 152 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 150 insertions(+), 2 deletions(-) diff --git a/1.14/defer.md b/1.14/defer.md index bb7ed3b..21be46d 100644 --- a/1.14/defer.md +++ b/1.14/defer.md @@ -4,6 +4,41 @@ 在 Go 1.14 中,增加了一种新的 defer 实现:open coded defer。当函数内 defer 不超过 8 个时,则会使用这种实现。 +形如下面这样的代码: + +```go +defer f1(a) +if cond { + defer f2(b) +} +body... +``` + +会被翻译为: + +```go +deferBits |= 1<<0 // 因为函数主 block 有 defer 语句,所以这里要设置第 0 位为 1 +tmpF1 = f1 // 将函数地址搬运到预留的栈上空间 +tmpA = a // 将函数参数复制到预留的栈上空间 +if cond { + deferBits |= 1<<1 // 因为进入了 if block,所以这里要设置第 1 位为 1 + tmpF2 = f2 // 将函数地址搬运到预留的栈上空间 + tmpB = b // 将函数参数复制到预留的栈上空间 +} +body... +exit: // 退出函数 body +if deferBits & 1<<1 != 0 { // 检查第 1 个 bit,如果是 1,执行保存好的函数 + deferBits &^= 1<<1 + tmpF2(tmpB) +} +if deferBits & 1<<0 != 0 { // 检查第 0 个 bit,如果是 1,执行保存好的函数 + deferBits &^= 1<<0 + tmpF1(tmpA) +} +``` + +这样看我们不一定知道他具体是怎么实现的,可以写个 block 更多的例子来看看实际生成的代码是什么样的: + ```go 1 package main 2 @@ -38,7 +73,7 @@ 31 } ``` -可以分析上面的代码生成的汇编来理解这个新版的 open coded defer 到底是怎么实现的: +下面是 go tool compile -S 生成的汇编代码,简单划分一下 block 来理解一下: ``` "".main STEXT size=1125 args=0x0 locals=0x160 @@ -278,6 +313,8 @@ DEFER 逻辑执行部分 0x0460 01120 (open-coded-defer.go:7) JMP 0 ``` +整体流程还好,不过比起以前堆上分配一个 `_defer` 链表,函数执行完之后直接遍历链表还是复杂太多了。 + 超过 8 个 defer 时会退化回以前的 defer 链,也可以观察一下: ```go @@ -299,12 +336,123 @@ func main() { 编译出的汇编就不贴了,和以前的没什么区别。 -可以做个简单的总结了: +结合代码分析过程和 proposal 中的描述,总结: * open coded defer 在函数内部总 defer 数量少于 8 时才会使用,大于 8 时会退化回老的 defer 链,这个权衡是考虑到程序文件的体积(个人觉得应该也有栈膨胀的考虑在) * 使用 open coded defer 时,在进入函数内的每个 block 时,都会设置这个 block 相应的 bit(ORL 指令),在执行到相应的 defer 语句时,把 defer 语句的参数和调用函数地址推到栈的相应位置 * 在栈上需要为这些 defer 调用的参数、函数地址,以及这个用来记录 defer bits 的 byte 预留空间,所以毫无疑问,函数的 framesize 会变大 +## panic 处理部分 + +proposal 有两部分,一部分是讲 open coded defer 的实现,另一部分是说 panic 的处理,现在在函数中发生 panic 时,我们没有办法获得原来的 `_defer` 结构体了,所以必须在编译期,给每个函数准备一项 FUNCDATA,存储每一个 defer block 的地址。这样 runtime 就可以通过 FUNCDATA 和偏移找到需要执行的 defer block 在什么地方。 + +``` + 0x004a 00074 (defer.go:8) FUNCDATA $2, gclocals·951c056c1b0a4d6097d387fbe928bbbf(SB) + 0x004a 00074 (defer.go:8) FUNCDATA $3, "".main.stkobj(SB) + 0x004a 00074 (defer.go:8) FUNCDATA $5, "".main.opendefer(SB) + + .... + + "".main.opendefer SRODATA dupok size=29 + 0x0000 30 c1 01 04 30 80 01 01 60 18 00 30 78 01 48 18 0...0...`..0x.H. + 0x0010 00 30 70 01 30 18 00 30 68 01 18 18 00 .0p.0..0h.... +``` + +这里的 main.opendefer 在编译为二进制之后,可能会被优化掉。不过我们理解基本的执行流程就可以了。 + +跟踪 runtime.gopanic 的流程,可以看到现在的 defer 上有 openDefer 的 bool 值: + +``` +(dlv) p d +*runtime._defer { + siz: 48, + started: true, + heap: false, + openDefer: false, + sp: 824634391712, + pc: 17590156, + fn: *runtime.funcval {fn: 17565744}, + _panic: *runtime._panic nil, + link: *runtime._defer { + siz: 8, + started: false, + heap: true, + openDefer: true, + sp: 824634392456, + pc: 17001293, + fn: *runtime.funcval nil, + _panic: *runtime._panic nil, + link: *runtime._defer nil, + fd: unsafe.Pointer(0x10fea48), + varp: 824634392528, + framepc: 17000984,}, + fd: unsafe.Pointer(0x0), + varp: 0, + framepc: 0,} +(dlv) n +``` + +执行具体的 defer 逻辑在 runtime.runOpenDeferFrame,也没啥神奇的: + +```go +func runOpenDeferFrame(gp *g, d *_defer) bool { + done := true + fd := d.fd + + // Skip the maxargsize + _, fd = readvarintUnsafe(fd) + deferBitsOffset, fd := readvarintUnsafe(fd) + nDefers, fd := readvarintUnsafe(fd) + deferBits := *(*uint8)(unsafe.Pointer(d.varp - uintptr(deferBitsOffset))) + + for i := int(nDefers) - 1; i >= 0; i-- { + // read the funcdata info for this defer + var argWidth, closureOffset, nArgs uint32 + argWidth, fd = readvarintUnsafe(fd) + closureOffset, fd = readvarintUnsafe(fd) + nArgs, fd = readvarintUnsafe(fd) + if deferBits&(1< Date: Wed, 9 Dec 2020 16:41:37 +0800 Subject: [PATCH 036/120] =?UTF-8?q?=E6=96=B0=E5=A2=9E=20atomic=20=E8=AF=A6?= =?UTF-8?q?=E7=BB=86=E5=88=86=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- atomic.md | 362 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ readme.md | 1 + sync.md | 1 + 3 files changed, 364 insertions(+) create mode 100644 atomic.md diff --git a/atomic.md b/atomic.md new file mode 100644 index 0000000..59b12b4 --- /dev/null +++ b/atomic.md @@ -0,0 +1,362 @@ +# atomic 详解 +> golang 中 atomic 类操作最终是使用 assembly 进行 cpu 指令调用实现的。本章将会对相应 api 及对应 assembly 进行分析 +> 本章所有函数分析均以 amd64 架构为例 + +sync/atomic/doc.go +```go +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package atomic provides low-level atomic memory primitives +// useful for implementing synchronization algorithms. +// +// These functions require great care to be used correctly. +// Except for special, low-level applications, synchronization is better +// done with channels or the facilities of the sync package. +// Share memory by communicating; +// don't communicate by sharing memory. +// +// The swap operation, implemented by the SwapT functions, is the atomic +// equivalent of: +// +// old = *addr +// *addr = new +// return old +// +// The compare-and-swap operation, implemented by the CompareAndSwapT +// functions, is the atomic equivalent of: +// +// if *addr == old { +// *addr = new +// return true +// } +// return false +// +// The add operation, implemented by the AddT functions, is the atomic +// equivalent of: +// +// *addr += delta +// return *addr +// +// The load and store operations, implemented by the LoadT and StoreT +// functions, are the atomic equivalents of "return *addr" and +// "*addr = val". +// +package atomic + +import ( + "unsafe" +) + +// BUG(rsc): On x86-32, the 64-bit functions use instructions unavailable before the Pentium MMX. +// +// On non-Linux ARM, the 64-bit functions use instructions unavailable before the ARMv6k core. +// +// On ARM, x86-32, and 32-bit MIPS, +// it is the caller's responsibility to arrange for 64-bit +// alignment of 64-bit words accessed atomically. The first word in a +// variable or in an allocated struct, array, or slice can be relied upon to be +// 64-bit aligned. + +// SwapInt32 atomically stores new into *addr and returns the previous *addr value. +func SwapInt32(addr *int32, new int32) (old int32) + +// SwapInt64 atomically stores new into *addr and returns the previous *addr value. +func SwapInt64(addr *int64, new int64) (old int64) + +// SwapUint32 atomically stores new into *addr and returns the previous *addr value. +func SwapUint32(addr *uint32, new uint32) (old uint32) + +// SwapUint64 atomically stores new into *addr and returns the previous *addr value. +func SwapUint64(addr *uint64, new uint64) (old uint64) + +// SwapUintptr atomically stores new into *addr and returns the previous *addr value. +func SwapUintptr(addr *uintptr, new uintptr) (old uintptr) + +// SwapPointer atomically stores new into *addr and returns the previous *addr value. +func SwapPointer(addr *unsafe.Pointer, new unsafe.Pointer) (old unsafe.Pointer) + +// CompareAndSwapInt32 executes the compare-and-swap operation for an int32 value. +func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool) + +// CompareAndSwapInt64 executes the compare-and-swap operation for an int64 value. +func CompareAndSwapInt64(addr *int64, old, new int64) (swapped bool) + +// CompareAndSwapUint32 executes the compare-and-swap operation for a uint32 value. +func CompareAndSwapUint32(addr *uint32, old, new uint32) (swapped bool) + +// CompareAndSwapUint64 executes the compare-and-swap operation for a uint64 value. +func CompareAndSwapUint64(addr *uint64, old, new uint64) (swapped bool) + +// CompareAndSwapUintptr executes the compare-and-swap operation for a uintptr value. +func CompareAndSwapUintptr(addr *uintptr, old, new uintptr) (swapped bool) + +// CompareAndSwapPointer executes the compare-and-swap operation for a unsafe.Pointer value. +func CompareAndSwapPointer(addr *unsafe.Pointer, old, new unsafe.Pointer) (swapped bool) + +// AddInt32 atomically adds delta to *addr and returns the new value. +func AddInt32(addr *int32, delta int32) (new int32) + +// AddUint32 atomically adds delta to *addr and returns the new value. +// To subtract a signed positive constant value c from x, do AddUint32(&x, ^uint32(c-1)). +// In particular, to decrement x, do AddUint32(&x, ^uint32(0)). +func AddUint32(addr *uint32, delta uint32) (new uint32) + +// AddInt64 atomically adds delta to *addr and returns the new value. +func AddInt64(addr *int64, delta int64) (new int64) + +// AddUint64 atomically adds delta to *addr and returns the new value. +// To subtract a signed positive constant value c from x, do AddUint64(&x, ^uint64(c-1)). +// In particular, to decrement x, do AddUint64(&x, ^uint64(0)). +func AddUint64(addr *uint64, delta uint64) (new uint64) + +// AddUintptr atomically adds delta to *addr and returns the new value. +func AddUintptr(addr *uintptr, delta uintptr) (new uintptr) + +// LoadInt32 atomically loads *addr. +func LoadInt32(addr *int32) (val int32) + +// LoadInt64 atomically loads *addr. +func LoadInt64(addr *int64) (val int64) + +// LoadUint32 atomically loads *addr. +func LoadUint32(addr *uint32) (val uint32) + +// LoadUint64 atomically loads *addr. +func LoadUint64(addr *uint64) (val uint64) + +// LoadUintptr atomically loads *addr. +func LoadUintptr(addr *uintptr) (val uintptr) + +// LoadPointer atomically loads *addr. +func LoadPointer(addr *unsafe.Pointer) (val unsafe.Pointer) + +// StoreInt32 atomically stores val into *addr. +func StoreInt32(addr *int32, val int32) + +// StoreInt64 atomically stores val into *addr. +func StoreInt64(addr *int64, val int64) + +// StoreUint32 atomically stores val into *addr. +func StoreUint32(addr *uint32, val uint32) + +// StoreUint64 atomically stores val into *addr. +func StoreUint64(addr *uint64, val uint64) + +// StoreUintptr atomically stores val into *addr. +func StoreUintptr(addr *uintptr, val uintptr) + +// StorePointer atomically stores val into *addr. +func StorePointer(addr *unsafe.Pointer, val unsafe.Pointer) + +// Helper for ARM. Linker will discard on other systems +func panic64() { + panic("sync/atomic: broken 64-bit atomic operations (buggy QEMU)") +} + +``` +> go 文件内只进行了函数的定义,具体实现为在 asm 文件中 + +sync/atomic/asm.s +```assembly +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !race + +#include "textflag.h" + +TEXT ·SwapInt32(SB),NOSPLIT,$0 + JMP runtime∕internal∕atomic·Xchg(SB) + +TEXT ·SwapUint32(SB),NOSPLIT,$0 + JMP runtime∕internal∕atomic·Xchg(SB) + +TEXT ·SwapInt64(SB),NOSPLIT,$0 + JMP runtime∕internal∕atomic·Xchg64(SB) + +TEXT ·SwapUint64(SB),NOSPLIT,$0 + JMP runtime∕internal∕atomic·Xchg64(SB) + +TEXT ·SwapUintptr(SB),NOSPLIT,$0 + JMP runtime∕internal∕atomic·Xchguintptr(SB) + +TEXT ·CompareAndSwapInt32(SB),NOSPLIT,$0 + JMP runtime∕internal∕atomic·Cas(SB) + +TEXT ·CompareAndSwapUint32(SB),NOSPLIT,$0 + JMP runtime∕internal∕atomic·Cas(SB) + +TEXT ·CompareAndSwapUintptr(SB),NOSPLIT,$0 + JMP runtime∕internal∕atomic·Casuintptr(SB) + +TEXT ·CompareAndSwapInt64(SB),NOSPLIT,$0 + JMP runtime∕internal∕atomic·Cas64(SB) + +TEXT ·CompareAndSwapUint64(SB),NOSPLIT,$0 + JMP runtime∕internal∕atomic·Cas64(SB) + +TEXT ·AddInt32(SB),NOSPLIT,$0 + JMP runtime∕internal∕atomic·Xadd(SB) + +TEXT ·AddUint32(SB),NOSPLIT,$0 + JMP runtime∕internal∕atomic·Xadd(SB) + +TEXT ·AddUintptr(SB),NOSPLIT,$0 + JMP runtime∕internal∕atomic·Xadduintptr(SB) + +TEXT ·AddInt64(SB),NOSPLIT,$0 + JMP runtime∕internal∕atomic·Xadd64(SB) + +TEXT ·AddUint64(SB),NOSPLIT,$0 + JMP runtime∕internal∕atomic·Xadd64(SB) + +TEXT ·LoadInt32(SB),NOSPLIT,$0 + JMP runtime∕internal∕atomic·Load(SB) + +TEXT ·LoadUint32(SB),NOSPLIT,$0 + JMP runtime∕internal∕atomic·Load(SB) + +TEXT ·LoadInt64(SB),NOSPLIT,$0 + JMP runtime∕internal∕atomic·Load64(SB) + +TEXT ·LoadUint64(SB),NOSPLIT,$0 + JMP runtime∕internal∕atomic·Load64(SB) + +TEXT ·LoadUintptr(SB),NOSPLIT,$0 + JMP runtime∕internal∕atomic·Loaduintptr(SB) + +TEXT ·LoadPointer(SB),NOSPLIT,$0 + JMP runtime∕internal∕atomic·Loadp(SB) + +TEXT ·StoreInt32(SB),NOSPLIT,$0 + JMP runtime∕internal∕atomic·Store(SB) + +TEXT ·StoreUint32(SB),NOSPLIT,$0 + JMP runtime∕internal∕atomic·Store(SB) + +TEXT ·StoreInt64(SB),NOSPLIT,$0 + JMP runtime∕internal∕atomic·Store64(SB) + +TEXT ·StoreUint64(SB),NOSPLIT,$0 + JMP runtime∕internal∕atomic·Store64(SB) + +TEXT ·StoreUintptr(SB),NOSPLIT,$0 + JMP runtime∕internal∕atomic·Storeuintptr(SB) + +``` +可以看到这边函数的实现仅为 JMP 跳转指令,统一跳转到 runtime/internal/atomic 下的各个函数进行 + + +runtime/internal/atomic/stubs.go +```go +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !wasm + +package atomic + +import "unsafe" + +//go:noescape +func Cas(ptr *uint32, old, new uint32) bool + +// NO go:noescape annotation; see atomic_pointer.go. +func Casp1(ptr *unsafe.Pointer, old, new unsafe.Pointer) bool + +//go:noescape +func Casuintptr(ptr *uintptr, old, new uintptr) bool + +//go:noescape +func Storeuintptr(ptr *uintptr, new uintptr) + +//go:noescape +func Loaduintptr(ptr *uintptr) uintptr + +//go:noescape +func Loaduint(ptr *uint) uint + +// TODO(matloob): Should these functions have the go:noescape annotation? + +//go:noescape +func Loadint64(ptr *int64) int64 + +//go:noescape +func Xaddint64(ptr *int64, delta int64) int64 + +``` +#### func Cas(ptr *uint32, old, new uint32) bool +- 入参为 uint32 指针,旧值,新值 +- 返回值为是否 cas 成功 + +go 文件内同样只进行了函数的定义,具体实现为在各平台的 asm 文件中: + +runtime/internal/atomic/asm_amd64.s +```assembly +TEXT runtime∕internal∕atomic·Cas(SB),NOSPLIT,$0-17 // 17 = sizeof(*uint32 + sizeof(uint32) + sizeof(uint32) + sizeof(uint8/bool)) + MOVQ ptr+0(FP), BX // 入参 1 ,8 字节 uint32 指针 + MOVL old+8(FP), AX // 入参 2 ,4 字节 uint32 + MOVL new+12(FP), CX // 入参 3 ,4 字节 uint32 + LOCK // lock 指令前缀 + /* + * CMPXCHGL r, [m] + * if AX == [m] { + * ZF = 1; + * [m] = r; + * } else { + * ZF = 0; + * AX = [m]; + * } + */ + CMPXCHGL CX, 0(BX) // 比较并交换指令, ZF set to 1 if success + SETEQ ret+16(FP) // 1 if ZF set to 1 + RET +``` +> ZF: 标志寄存器的一种,零标志:用于判断结果是否为0。运算结果0,ZF置1,否则置0。 + +#### func SwapInt32(addr *int32, new int32) (old int32) +> cmd/compile/internal/gc/ssa.go +> alias("sync/atomic", "SwapInt32", "runtime/internal/atomic", "Xchg", all...) +> 以上可知 SwapInt32 函数为 runtime/internal/atomic·Xchg 函数的一个别名。 + +runtime/internal/atomic/asm_amd64.s +```assembly +TEXT runtime∕internal∕atomic·Xchg(SB), NOSPLIT, $0-20 + MOVQ ptr+0(FP), BX + MOVL new+8(FP), AX + XCHGL AX, 0(BX) // 交换指令 + MOVL AX, ret+16(FP) // 交换后的 AX(old value) 写入 FP 返回值位 + RET +``` + +#### func AddInt32(addr *int32, new int32) (old int32) +> alias("sync/atomic", "AddInt32", "runtime/internal/atomic", "Xadd", all...) + +runtime/internal/atomic/asm_amd64.s +```assembly +TEXT runtime∕internal∕atomic·Xadd(SB), NOSPLIT, $0-20 + MOVQ ptr+0(FP), BX + MOVL delta+8(FP), AX + MOVL AX, CX + LOCK + XADDL AX, 0(BX) // Exchange and Add + ADDL CX, AX // AX += CX + MOVL AX, ret+16(FP) + RET +``` +#### func StoreInt32(addr *int32, val int32) +> alias("sync/atomic", "StoreInt32", "runtime/internal/atomic", "Store", all...) + +runtime/internal/atomic/asm_amd64.s + +```assembly +TEXT runtime∕internal∕atomic·Store(SB), NOSPLIT, $0-12 + MOVQ ptr+0(FP), BX + MOVL val+8(FP), AX + XCHGL AX, 0(BX) // 交换指令 + RET +``` \ No newline at end of file diff --git a/readme.md b/readme.md index f00d60c..2b8af93 100644 --- a/readme.md +++ b/readme.md @@ -27,3 +27,4 @@ 21. [ ] [Lock Free](lockfree.md) 22. [x] [context](context.md) 23. [x] [stack dump](runtime_stack.md) +24. [x] [Atomic](atomic.md) diff --git a/sync.md b/sync.md index bee363d..0e9fb5a 100644 --- a/sync.md +++ b/sync.md @@ -111,6 +111,7 @@ atomic.CompareAndSwap 即是使用 lock cmpxchg 来实现的。 > Actually modern Intel CPUs (since the Pentium pro) only lock the bus in very rare exceptions. Generally they use cache locking which is much, much more efficient and basically just follows from the usual cache coherence protocol (e.g. exclusive state in MESI). +[Atomic 详解](atomic.md) ## waitgroup ```go From c80623d1a3b5b6868ae0c9765e92f98304ae0cdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A4=9A=E6=99=96?= Date: Sun, 13 Dec 2020 20:08:09 +0800 Subject: [PATCH 037/120] update schedule --- 1.14/signal_based_preemption.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/1.14/signal_based_preemption.md b/1.14/signal_based_preemption.md index 3ca2d51..d7b76ff 100644 --- a/1.14/signal_based_preemption.md +++ b/1.14/signal_based_preemption.md @@ -1,3 +1,13 @@ # 1.14 scheduler -TODO +## 流程概述 + +## 信号处理与 gsignal + +## 现场保存和恢复 + +### GC 抢占流程 + +### retake 抢占流程 + +## 参考资料 From 497361796c1b3155cdc6d27625f3d281eec4548c Mon Sep 17 00:00:00 2001 From: wziww Date: Tue, 15 Dec 2020 14:01:26 +0800 Subject: [PATCH 038/120] add epoll notes --- netpoll.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/netpoll.md b/netpoll.md index 04288c2..942adad 100644 --- a/netpoll.md +++ b/netpoll.md @@ -397,6 +397,10 @@ func poll_runtime_pollOpen(fd uintptr) (*pollDesc, int) { ```go func netpollopen(fd uintptr, pd *pollDesc) int32 { var ev epollevent + // linux 下 epoll 采用了 ET 模式 + // epoll 下用户可以 watch 的 descriptors 是有上限的 + // 具体上限的查看及配置可以通过 /proc/sys/fs/epoll/max_user_watches 系统虚拟文件进行操作 + // https://github.com/torvalds/linux/blob/v4.9/fs/eventpoll.c#L259-L263 ev.events = _EPOLLIN | _EPOLLOUT | _EPOLLRDHUP | _EPOLLET *(**pollDesc)(unsafe.Pointer(&ev.data)) = pd return -epollctl(epfd, _EPOLL_CTL_ADD, int32(fd), &ev) From ddcbe4cc9e09710662deeecf9deb485a7f9d001d Mon Sep 17 00:00:00 2001 From: Xargin Date: Wed, 23 Dec 2020 21:12:46 +0800 Subject: [PATCH 039/120] add signal --- 1.14/signal_based_preemption.md | 185 +++++++++++++++++++++++++++++++- images/signal.png | Bin 0 -> 306836 bytes map.md | 6 ++ 3 files changed, 188 insertions(+), 3 deletions(-) create mode 100644 images/signal.png diff --git a/1.14/signal_based_preemption.md b/1.14/signal_based_preemption.md index d7b76ff..1643c90 100644 --- a/1.14/signal_based_preemption.md +++ b/1.14/signal_based_preemption.md @@ -1,13 +1,192 @@ # 1.14 scheduler +## 信号概念 + +信号是一种发给进程的通知,以告知有事件发生。有时信号也被称为软件中断。从中断用户控制流来说,信号和硬件中断是类似的;在大多场景下,信号何时到达进程是无法预测的。 + +信号可以由内核发给用户进程,可以用户进程发给自己,也可以用户进程发给其它的用户进程。 + +在进程内部,信号还可以发给某个具体的线程。在 Go 语言中,抢占的信号就是发给指定的线程的。 + +## 信号是易失的么 + +有一种传言认为信号是易失的,但实际上在早期的 unix 实现中才是易失的。这里的易失说的是完全没有把信号传递给相应的进程。由 POSIX.1-1990 标准定义之后实现的信号基本都是可靠信号了。 + +但不易失不代表你向同一个进程重复发送相同的信号 100 次,进程就能收到 100 次这个信号,这是怎么回事? + +## 不丢失不代表每次收到的信号都会被触发 + +当进程不能处理某个具体的信号时,内核会自动将该信号加入到 signal mask 中,以 block 住后面来的相同信号。 + +比如进程正在执行 SIGINT 的 sighandler,还没执行完。这时候 SIGINT 是在 sigmask 中的,如果又来了一个 SIGINT,内核会发现 sigmask 里有 SIGINT,便会把这个 SIGINT 放进 pending signals 的集合里。注意这里是个集合,所以同一个信号在 block 期间如果多次触发,那在 pending signals 的集合里会被去重,unblock 了以后,只会触发一次 sighandler。 + +TODO,画个图 + +实时信号比较特殊,不过不在我们讨论范围内,感兴趣的可以去看 TLPI。 + +## 信号处理 + +![](../images/signal.png) + +信号处理涉及几个 syscall: + +### tigkill + +向某个进程的某个线程发信号。 + +### sigaction + +设置信号执行时的 sighandler, + +### sigaltstack + +修改信号执行时所用的函数栈。 + +## 简单的信号处理函数 + +简单起见,这里我们使用 C 来做演示: + +## 执行 non-local goto + +### goto 的限制 + +goto 虽然看起来无所不能,但实际上高级语言的 goto 是跳不出函数作用域的,比如下面这样的代码就没法通过编译: + +```go +TODO +``` + +在其它语言里也一样: + +```c +TODO +``` + +### non-local goto + +``` +#include +#define __USE_GNU +#include +#include + +void myhandle(int mysignal, siginfo_t *si, void* arg) +{ + ucontext_t *context = (ucontext_t *)arg; + printf("Address from where crash happen is %x \n",context->uc_mcontext.gregs[REG_RIP]); + context->uc_mcontext.gregs[REG_RIP] = context->uc_mcontext.gregs[REG_RIP] + 0x04 ; + +} + +int main(int argc, char *argv[]) +{ + struct sigaction action; + action.sa_sigaction = &myhandle; + action.sa_flags = SA_SIGINFO; + sigaction(11,&action,NULL); + + printf("Before segfault\n"); + + int *a=NULL; + int b; + b =*a; // Here crash will hapen + + printf("I am still alive\n"); + + return 0; +} +``` + +## Go 语言中的信号式抢占 + +有了上述的储备知识,我们在看 Go 的信号式抢占前,至少知道了以下几个知识点: + +* 信号可以在任意位置打断用户代码 +* 信号的处理函数可以自定义 +* 信号的执行栈可以自定义 +* 信号执行完毕之后默认返回用户代码的下一条汇编指令继续执行 +* 可以通过修改 pc 寄存器的值,使信号处理完之后执行非本地的 goto,其实就是跳到任意的代码位置去 + +### SIGURG + +为什么选用了 SIGURG。 + +## gsignal + +gsignal 是一个特殊的 goroutine,类似 g0,每一个线程都有一个,创建的 m 的时候,就会创建好这个 gsignal,在 linux 中为 gsignal 分配 32KB 的内存: + +newm -> allocm -> mcommoninit -> mpreinit -> malg(32 * 1024) + +在线程处理信号时,会短暂地将栈从用户栈切换到 gsignal 的栈,执行 sighandler,执行完成之后,会重新切换回用户的栈继续执行用户逻辑。 + ## 流程概述 -## 信号处理与 gsignal +抢占流程主要有两个入口,GC 和 sysmon。 -## 现场保存和恢复 +TODO,control flow pic show ### GC 抢占流程 -### retake 抢占流程 +markroot -> fetch g from allgs -> suspendG -> scan g stack -> resumeG + +除了 running 以外,任何状态(如 dead,runnable,waiting)的 g 实际上都不是正在运行的 g,对于这些 g 来说,只要将其相应的字段打包返回就可以了。 + +running 状态的 g 正在系统线程上执行代码,是需要真正发送信号来抢占的。 + +### sysmon 抢占流程 + +preemptone -> asyncPreempt -> globalrunqput + +### sighandler + +```go +func sighandler(...) + if sig == sigPreempt { + doSigPreempt(gp, c) + } +} + +func doSigPreempt(gp *g, ctxt *sigctxt) { + if wantAsyncPreempt(gp) && isAsyncSafePoint(gp, ctxt.sigpc(), ctxt.sigsp(), ctxt.siglr()) { + ctxt.pushCall(funcPC(asyncPreempt)) + } +} +``` + +```go +func (c *sigctxt) pushCall(targetPC uintptr) { + // Make it look like the signaled instruction called target. + pc := uintptr(c.rip()) + sp := uintptr(c.rsp()) + sp -= sys.PtrSize + *(*uintptr)(unsafe.Pointer(sp)) = pc + c.set_rsp(uint64(sp)) + c.set_rip(uint64(targetPC)) +} +``` + +pushCall 其实就是把用户代码中即将执行的下一条指令的地址(即 pc 寄存器的值),保存在栈顶,然后 sp 寄存器下移(就是扩栈,栈从高地址向低地址增长)。 + +TODO,这里需要图 + +### 现场保存和恢复 + +在 sighandler 修改了信号返回时的 pc 寄存器,所以从 sighandler 返回之后,之前在 running 的 g 不是执行下一条指令,而是执行 pc 寄存器中保存的函数去了,这里就是 asyncPreempt。 + +asyncPreempt 是汇编实现的,分为三个部分: + +1. 将当前 g 的所有寄存器均保存在 g 的栈上 +2. 执行 asyncPreempt2 +3. 恢复 g 的所有寄存器,继续执行用户代码 + +TODO,图 + +## 总结 + +TODO,总览图 ## 参考资料 + +1. The Linux Programming Interface +2. [non-cooperative goroutine preemption](https://github.com/golang/proposal/blob/master/design/24543-non-cooperative-preemption.md) +3. https://stackoverflow.com/questions/34989829/linux-signal-handling-how-to-get-address-of-interrupted-instruction diff --git a/images/signal.png b/images/signal.png new file mode 100644 index 0000000000000000000000000000000000000000..6ba194a04268127a0722865df2f085a763b808f2 GIT binary patch literal 306836 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%0Ag`F_j9dO!V12kB*qwg{MUyc8^BMq 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 z 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%0DeX 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

)-=n=hxyJ1bINn>oL6{W0 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 C{Ygdu literal 0 HcmV?d00001 diff --git a/readme.md b/readme.md index 8c35149..64b69e6 100644 --- a/readme.md +++ b/readme.md @@ -36,3 +36,6 @@ [wziww](https://github.com/wziww) +# 公众号 + +![](images/gzh.png) From 3f4f3330ff270940528d4628bcd9331af9d2e76d Mon Sep 17 00:00:00 2001 From: Xargin Date: Wed, 31 Mar 2021 22:28:29 +0800 Subject: [PATCH 069/120] update img size --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 64b69e6..cbd4555 100644 --- a/readme.md +++ b/readme.md @@ -38,4 +38,4 @@ # 公众号 -![](images/gzh.png) + From 4464c64741c6d79356719e1768e91c005f0c6608 Mon Sep 17 00:00:00 2001 From: wziww Date: Wed, 7 Apr 2021 14:54:53 +0800 Subject: [PATCH 070/120] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E8=B0=83=E8=AF=95?= =?UTF-8?q?=E6=8F=90=E9=86=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scheduler.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scheduler.md b/scheduler.md index 083d871..ec9ed3e 100644 --- a/scheduler.md +++ b/scheduler.md @@ -1,3 +1,4 @@ +> 注: 在抢占式调度的 go 版本下如果需要对 runtime 进行调试,诸如使用 gdb, lldb, [delve](https://github.com/go-delve/delve) 等工具时,需要注意 GODEBUG=asyncpreemptoff=1 环境变量,该变量会导致 runtime 是否进行抢占式调度,由于 https://github.com/golang/go/issues/36494 ,导致部分系统下该变量会被一些(如 delve)工具配置开启,从而导致超出预期的调试情况,需要读者自行关注 # 调度 ## 基本数据结构 @@ -2344,3 +2345,4 @@ gcMarkDone --> forEachP 可见只有 gc 和 retake 才会去真正地抢占 g,并没有其它的入口,其它的地方就只是恢复一下可能在 newstack 中被清除掉的抢占标记。 当然,这里 entersyscall 和 entersyscallblock 比较特殊,虽然这俩函数的实现中有设置抢占标记,但实际上这两段逻辑是不会被走到的。因为 syscall 执行时是在 m 的 g0 栈上,如果在执行时被抢占,那么会直接 throw,而无法恢复。 + From 05be760d6227924808d14f89ac119a01ffead602 Mon Sep 17 00:00:00 2001 From: wziww Date: Mon, 12 Apr 2021 16:44:33 +0800 Subject: [PATCH 071/120] io-directio --- io.md | 196 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 io.md diff --git a/io.md b/io.md new file mode 100644 index 0000000..547d439 --- /dev/null +++ b/io.md @@ -0,0 +1,196 @@ +# directio +## page cache +页面缓存(Page Cache)是 Linux 内核中针对文件I/O的一项优化, 众所周知磁盘 I/O 的成本远比内存访问来得高, 如果每次进行文件读写都需要直接进行磁盘操作, 那成本会是非常高的, 因此, kernel 针对于文件 I/O 设计了 page cache, 简单来说就是将目标读写的文件页缓存在内存中, 而后操作这块缓存进行读写 (而且例如针对机械磁盘来说, 为了降低磁头寻道的耗时, page cache 通常会采用预读的机制), 写入新数据后该页变为脏页, 等待刷盘, 刷脏的操作可由用户主动请求 (fsync) 或者由内核在合适的时机进行操作 + +### 总结下 page cache 的好处: +- 缓存最近被访问的数据, 提高文件 I/O 的效率 +- 预读功能减少磁头寻道损耗 + +## 那么 page cache 的设计一定是能提高服务效率的么? + +来考虑下一个场景: +> 某服务正在正常工作, 存放了许许多多的静态资源等待访问, 大小为小到几十 kb, 大到几百 GB (大到无法全都加载到内存, 只能存放在本地磁盘)的大文件不等。小文件为热点文件, 大文件为冷门资源, 某天, 突然有用户进行了大文件的访问。 + +这时候会发生什么? + +正常工作的情况下假设内存足够缓存所有小文件, 服务无需进行磁盘 I/O, 而这时候突然来了个大文件的缓存, 直接跑满了大部分的 page cache, 造成内核不得不通过淘汰策略将 page cache 置换了出来(先暂不考虑各种内存拷贝的损耗), 那么接下去再去访问那一堆热点小文件就不得不去进行磁盘 I/O, 然后写入 page cache, 两者之间的矛盾无法调和不断重复, 尤其是大量的小文件基本都为随机读写。从而服务的压力增加, 效率降低。 + +### 如何优化这种场景? +> 各架构机器支持程度不一, 此处仅讨论 linux x86 + +```shell +dd if=/dev/zero of=test bs=1M count=1000 # 生成 1000MB 文件以供测试 +``` +先来测试下正常情况下文件读写的情况: +```go +package main + +import ( + "log" + "os" + "syscall" + "time" + "unsafe" +) + +const ( + // Size to align the buffer to + AlignSize = 4096 + + // Minimum block size + BlockSize = 4096 +) + +func alignment(block []byte, AlignSize int) int { + return int(uintptr(unsafe.Pointer(&block[0])) & uintptr(AlignSize-1)) +} + +func AlignedBlock(BlockSize int) []byte { + block := make([]byte, BlockSize+AlignSize) + if AlignSize == 0 { + return block + } + a := alignment(block, AlignSize) + offset := 0 + if a != 0 { + offset = AlignSize - a + } + block = block[offset : offset+BlockSize] + // Can't check alignment of a zero sized block + if BlockSize != 0 { + a = alignment(block, AlignSize) + if a != 0 { + log.Fatal("Failed to align block") + } + } + return block +} +func main() { + fd, err := os.OpenFile("/disk/data/tmp/test", os.O_RDWR|syscall.O_DIRECT, 0666) + block := AlignedBlock(BlockSize) + if err != nil { + panic(err) + } + defer fd.Close() + for i := range block { + block[i] = 1 + } + for { + n, err := fd.Write(block) + _ = n + if err != nil { + panic(err) + } + fd.Seek(0, 0) + time.Sleep(time.Nanosecond * 10) + } +} +``` +```shell +iotop +Total DISK READ : 0.00 B/s +Actual DISK READ: 0.00 B/s +``` +可以观察到除了初次 I/O 的时候产生磁盘读写, 待测试代码稳定下后是没有产生磁盘读写的, 再看看文件 page cache 的情况 +```shell +tmp vmtouch /disk/data/tmp/test + Files: 1 + Directories: 0 + Resident Pages: 4/256000 16K/1000M 0.00156% + Elapsed: 0.007374 seconds +``` +与预期中的一样缓存了前 16K 文件页 +```shell +# 强刷 page cache +echo 3 > /proc/sys/vm/drop_caches +``` +再来测试下绕过 page cache 进行文件读写的方法 +```go +package main + +import ( + "log" + "os" + "syscall" + "time" + "unsafe" +) + +const ( + // Size to align the buffer to + AlignSize = 4096 + + // Minimum block size + BlockSize = 4096 +) + +func alignment(block []byte, AlignSize int) int { + return int(uintptr(unsafe.Pointer(&block[0])) & uintptr(AlignSize-1)) +} + +func AlignedBlock(BlockSize int) []byte { + block := make([]byte, BlockSize+AlignSize) + if AlignSize == 0 { + return block + } + a := alignment(block, AlignSize) + offset := 0 + if a != 0 { + offset = AlignSize - a + } + block = block[offset : offset+BlockSize] + // Can't check alignment of a zero sized block + if BlockSize != 0 { + a = alignment(block, AlignSize) + if a != 0 { + log.Fatal("Failed to align block") + } + } + return block +} +func main() { + // syscall.O_DIRECT fsfd 直接 I/O 选项 + fd, err := os.OpenFile("/disk/data/tmp/test", os.O_RDWR|syscall.O_DIRECT, 0666) + block := AlignedBlock(BlockSize) + if err != nil { + panic(err) + } + defer fd.Close() + for i := range block { + block[i] = 1 + } + for { + n, err := fd.Read(block) + _ = n + if err != nil { + panic(err) + } + fd.Seek(0, 0) + time.Sleep(time.Nanosecond * 10) + } +} +``` +> 注: 要使用 O_DIRECT 的方式进行文件 I/O 的话, 文件每次操作的大小得进行文件 lock size 以及 memory address 的对齐 + +```shell +iotop +Total DISK READ : 17.45 M/s +Actual DISK READ: 17.42 M/s +``` +再来看看 page cache 的情况: +```shell +vmtouch /disk/data/tmp/test + Files: 1 + Directories: 0 + Resident Pages: 0/256000 0/1000M 0% + Elapsed: 0.006608 seconds +``` + +## 回到 golang +当前标准库如 http.ServeFile, os.Open 等默认采用的访问静态资源的方式均为非直接 I/O, 因此如果有特定场景需要用户自己进行这方面的考量及优化 +## 参考资料 +http://nginx.org/en/docs/http/ngx_http_core_module.html#directio +https://tech.meituan.com/2017/05/19/about-desk-io.html +https://github.com/ncw/directio +# DMA +# Zero-Copy \ No newline at end of file From 2ead47eefd0496a45181b2a6d2d48f498dcb054a Mon Sep 17 00:00:00 2001 From: wziww Date: Mon, 12 Apr 2021 16:46:50 +0800 Subject: [PATCH 072/120] fix shell --- io.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io.md b/io.md index 547d439..106575e 100644 --- a/io.md +++ b/io.md @@ -93,7 +93,7 @@ Actual DISK READ: 0.00 B/s ``` 可以观察到除了初次 I/O 的时候产生磁盘读写, 待测试代码稳定下后是没有产生磁盘读写的, 再看看文件 page cache 的情况 ```shell -tmp vmtouch /disk/data/tmp/test +vmtouch /disk/data/tmp/test Files: 1 Directories: 0 Resident Pages: 4/256000 16K/1000M 0.00156% From 7c8265146bd3861a635b204e41137389c9a10f1e Mon Sep 17 00:00:00 2001 From: wziww Date: Mon, 12 Apr 2021 16:47:57 +0800 Subject: [PATCH 073/120] =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- io.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/io.md b/io.md index 106575e..2bbac62 100644 --- a/io.md +++ b/io.md @@ -189,8 +189,8 @@ vmtouch /disk/data/tmp/test ## 回到 golang 当前标准库如 http.ServeFile, os.Open 等默认采用的访问静态资源的方式均为非直接 I/O, 因此如果有特定场景需要用户自己进行这方面的考量及优化 ## 参考资料 -http://nginx.org/en/docs/http/ngx_http_core_module.html#directio -https://tech.meituan.com/2017/05/19/about-desk-io.html -https://github.com/ncw/directio +- http://nginx.org/en/docs/http/ngx_http_core_module.html#directio +- https://tech.meituan.com/2017/05/19/about-desk-io.html +- https://github.com/ncw/directio # DMA # Zero-Copy \ No newline at end of file From 1aa2a474711d9c61c20c8976c00b81dfdbbd7e26 Mon Sep 17 00:00:00 2001 From: Xargin Date: Tue, 13 Apr 2021 14:02:44 +0800 Subject: [PATCH 074/120] add tricolor images --- generics.md | 42 +++++++++++++++++++++++++ images/garbage_collection/tricolor.png | Bin 0 -> 26515 bytes 2 files changed, 42 insertions(+) create mode 100644 images/garbage_collection/tricolor.png diff --git a/generics.md b/generics.md index e69de29..caf2ba5 100644 --- a/generics.md +++ b/generics.md @@ -0,0 +1,42 @@ +# Generics + +在泛型出现之前,社区里也有一些伪泛型方案,例如 [genny](https://github.com/cheekybits/genny)。 + +genny 本质是基于文本替换的,比如 example 里的例子: + +```go +package queue + +import "github.com/cheekybits/genny/generic" + +// NOTE: this is how easy it is to define a generic type +type Something generic.Type + +// SomethingQueue is a queue of Somethings. +type SomethingQueue struct { + items []Something +} + +func NewSomethingQueue() *SomethingQueue { + return &SomethingQueue{items: make([]Something, 0)} +} +func (q *SomethingQueue) Push(item Something) { + q.items = append(q.items, item) +} +func (q *SomethingQueue) Pop() Something { + item := q.items[0] + q.items = q.items[1:] + return item +} +``` + +执行替换命令: + +```shell +cat source.go | genny gen "Something=string" +``` + +然后 genny 会将代码中所有 Something 替换成 string,同时确保大小写与原来一致,不影响字段/类型的导出特征。 + +没有官方的泛型支持,社区怎么搞都是邪道。2021 年 1 月,官方的方案已经基本上成型,并释出了 [draft design](https://go.googlesource.com/proposal/+/refs/heads/master/design/go2draft-type-parameters.md)。 + diff --git a/images/garbage_collection/tricolor.png b/images/garbage_collection/tricolor.png new file mode 100644 index 0000000000000000000000000000000000000000..b711acfbe5a6f41002fb48a4f2a74e321130c024 GIT binary patch literal 26515 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 Date: Mon, 19 Apr 2021 18:12:01 +0800 Subject: [PATCH 075/120] =?UTF-8?q?panic=20-=20recover=20=E8=A1=A5?= =?UTF-8?q?=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- panic.md | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/panic.md b/panic.md index c0c0fbc..7765ec1 100644 --- a/panic.md +++ b/panic.md @@ -194,3 +194,105 @@ TEXT runtime·gogo(SB), NOSPLIT, $16-8 ``` 这样所有流程就都打通了。 + +--- + +同样需要注意的是, 如多次`panic(defer)`后再利用`recover`恢复后获取到的`panic`信息仅仅为最近一次的信息, 是获取不了`_panic`整个链表中记录的所有信息的, 如果坚持想这样做, 那只能从 TLS 中获取当前的上下文, 通过基址加变址寻址的方式从当前的`g`中来获取`_panic`信息 + +go 1.14.3 为例: +```assembly +#include "textflag.h" +// main.s +// func GetPanicPtr() uintptr +TEXT ·GetPanicPtr(SB), NOSPLIT, $0-8 + MOVQ TLS, CX + MOVQ 0(CX)(TLS*1), AX + MOVQ $32, BX // 32 根据不同版本 type g struct 中 _panic 字段计算偏移量即可 + LEAQ 0(AX)(BX*1), DX + MOVQ (DX), AX + MOVQ AX, ret+0(FP) + RET +``` +```go +// main.go +package main + +import ( + "fmt" + "unsafe" +) + +func GetPanicPtr() uintptr + +type gobuf struct { + // The offsets of sp, pc, and g are known to (hard-coded in) libmach. + // + // ctxt is unusual with respect to GC: it may be a + // heap-allocated funcval, so GC needs to track it, but it + // needs to be set and cleared from assembly, where it's + // difficult to have write barriers. However, ctxt is really a + // saved, live register, and we only ever exchange it between + // the real register and the gobuf. Hence, we treat it as a + // root during stack scanning, which means assembly that saves + // and restores it doesn't need write barriers. It's still + // typed as a pointer so that any other writes from Go get + // write barriers. + sp uintptr + pc uintptr + g uintptr + ctxt unsafe.Pointer + ret uint64 + lr uintptr + bp uintptr // for GOEXPERIMENT=framepointer +} +type stack struct { + lo uintptr + hi uintptr +} +type g struct { + // Stack parameters. + // stack describes the actual stack memory: [stack.lo, stack.hi). + // stackguard0 is the stack pointer compared in the Go stack growth prologue. + // It is stack.lo+StackGuard normally, but can be StackPreempt to trigger a preemption. + // stackguard1 is the stack pointer compared in the C stack growth prologue. + // It is stack.lo+StackGuard on g0 and gsignal stacks. + // It is ~0 on other goroutine stacks, to trigger a call to morestackc (and crash). + stack stack // offset known to runtime/cgo + stackguard0 uintptr // offset known to liblink + stackguard1 uintptr // offset known to liblink + + _panic uintptr // innermost panic - offset known to liblink + _defer uintptr // innermost defer + m uintptr // current m; offset known to arm liblink + sched gobuf + syscallsp uintptr // if status==Gsyscall, syscallsp = sched.sp to use during gc + syscallpc uintptr // if status==Gsyscall, syscallpc = sched.pc to use during gc + stktopsp uintptr // expected sp at top of stack, to check in traceback + param unsafe.Pointer // passed parameter on wakeup + atomicstatus uint32 + stackLock uint32 // sigprof/scang lock; TODO: fold in to atomicstatus + goid int64 +} +type _panic struct { + argp unsafe.Pointer // pointer to arguments of deferred call run during panic; cannot move - known to liblink + arg interface{} // argument to panic + link *_panic // link to earlier panic + pc uintptr // where to return to in runtime if this panic is bypassed + sp unsafe.Pointer // where to return to in runtime if this panic is bypassed + recovered bool // whether this panic is over + aborted bool // the panic was aborted + goexit bool +} + +func main() { + defer func() { + var __panic *_panic + var _panic_ptr uintptr = GetPanicPtr() + __panic = (*_panic)(unsafe.Pointer(_panic_ptr)) + fmt.Println(__panic) + }() + defer panic(3) + defer panic(2) + panic(1) +} +``` \ No newline at end of file From aeb04fcd35871c0db1e22635799ac870f235b3d2 Mon Sep 17 00:00:00 2001 From: wziww Date: Thu, 22 Apr 2021 10:53:50 +0800 Subject: [PATCH 076/120] splice --- io.md | 526 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 525 insertions(+), 1 deletion(-) diff --git a/io.md b/io.md index 2bbac62..282ffff 100644 --- a/io.md +++ b/io.md @@ -193,4 +193,528 @@ vmtouch /disk/data/tmp/test - https://tech.meituan.com/2017/05/19/about-desk-io.html - https://github.com/ncw/directio # DMA -# Zero-Copy \ No newline at end of file +# Zero-Copy + +## splice +在介绍 splice 及其在 golang 中的应用之前, 先从一段简单的网络代理代码开始入手 +#### read & write +```go +var ( + p sync.Pool +) + +func init() { + p.New = func() interface{} { + return make([]byte, DEFAULTSIZE) + } +} +// src 客户端 tcp 链接 +// dst mock server tcp 链接 +func normal(src, dst net.Conn) { + var bts []byte = p.Get().([]byte) + var bts2 []byte = p.Get().([]byte) + defer p.Put(bts) + defer p.Put(bts2) + // mock server to client + go func() { + for { + num, err := dst.Read(bts2[:]) + if err != nil { + break + } + var num_write int + for num > 0 { + num_write, err = src.Write(bts2[num_write:num]) + if err != nil { + return + } + num -= num_write + } + } + }() + // client to mock server + for { + num, err := src.Read(bts[:]) + if err != nil { + break + } + var num_write int + for num > 0 { + num_write, err = dst.Write(bts[num_write:num]) + if err != nil { + return + } + num -= num_write + } + } +} +``` +以上片段实现了一个简单的功能: 将客户端请求的 tcp 数据通过`read`系统调用读出放入本地用户空间 缓存, 而后再调用`write`发送给目标服务器,反之亦然 + +整个过程如下图所示(暂不考虑 IO 模型调度以及 DMA 等细节部分) + +```shell +[ user space ] + + -------------------------------------------- + | application | + -------------------------------------------- + ····················|·································|·················· + | read() | write() +[ kernel space ] | | + ----------------- ----------------- + | socket buffer | | socket buffer | + ----------------- ----------------- + | copy | + ····················|·································|·················· +[ hardware sapce ] | | + ------------------------------------------------------ + | network interface | + ------------------------------------------------------ + +``` +对于透传或者部分透传(例如七层头部解析后透明代理请求体)之类的需求场景来说, 这种流程的成本无疑是很高的, 可以总结下涉及的几个浪费点 + +- 数据需要从内核态拷贝到用户态 +- 应用层在 read 及 write 的过程中对这部分 byte 的操作开销(申请、释放、对象池维护等) + +#### splice 介绍 +```c +/* + splice() moves data between two file descriptors without copying + between kernel address space and user address space. It + transfers up to len bytes of data from the file descriptor fd_in + to the file descriptor fd_out, where one of the file descriptors + must refer to a pipe. +*/ +ssize_t splice(int fd_in, off64_t *off_in, int fd_out, + off64_t *off_out, size_t len, unsigned int flags); +``` +一句话概括就是, `splice` 不需要从内核空间复制这部分数据到用户空间就可以支持将数据从两个文件描述符之间进行转移, 不过两个描述符至少得有一个是 `pipe`, 一下列举如何利用`splice`完成 `socket->socket` 的数据代理 + +example: +```go +func example(src, dst net.Conn) { + // 由于 src 及 dst 都是 socket, 没法直接使用 splice, 因此先创建临时 pipe + const flags = syscall.O_CLOEXEC | syscall.O_NONBLOCK + var fds [2]int // readfd, writefd + if err := syscall.Pipe2(fds[:], flags); err != nil { + panic(err) + } + // 使用完后关闭 pipe + defer syscall.Close(fds[0]) + defer syscall.Close(fds[1]) + // 获取 src fd + srcfile, err := src.(*net.TCPConn).File() + if err != nil { + panic(err) + } + srcfd := int(srcfile.Fd()) + syscall.SetNonblock(srcfd, true) + ... + // 从 srcfd 读出, 写入 fds[1] (pipe write fd) + num, err := syscall.Splice(srcfd, nil, fds[1], nil, DEFAULTSIZE/* size to splice */, SPLICE_F_NONBLOCK) + ... +} +``` +此时的调用过程变为: + +```shell +[ user space ] + + ----------------------------------------------------------------------------------------------- + | application | + ----------------------------------------------------------------------------------------------- + ········································|····················|·····················|·································· + | splice() | pipe() | splice() +[ kernel space ] | | | + ----------------- ”copy“ ----------------------- ”copy“ ----------------- + | socket buffer |· · · · · · · · · >| pipe writefd & readfd |· · · · · · · · >| socket buffer | + ----------------- ----------------------- ----------------- + | copy | + ····················|··············································································| +[ hardware sapce ] | | + ----------------------------------------------------------------------------------------------- + | network interface | + ----------------------------------------------------------------------------------------------- +``` +此时产生的系统调用为 +- 首先 `pipe()` 调用, 创建临时管道 +- 调用 `splice()` 将 `srcfd` 数据 ”拷贝“ 到 `pipe` +- 调用 `splice()` 将 `pipe` 中的数据 ”拷贝“ 到 `dstfd` + +可以注意到图中以及总结部分的”拷贝“给加了引号, 具体了解过`pipe`底层实现的小伙伴应该理解, 在这边简单表述下, `splice` 是基于 `pipe buffer` 实现的, 本质上在数据传输的时候并没有进行数据的拷贝, 而是仅仅将数据的内存地址等信息塞进了`pipe_buffer`中对应的字段 + +至此, 完成了 kernel-user space 的拷贝优化, 不过可能细心的人会发现, 这种方式虽然减少了数据的拷贝, 但是同样额外增加了系统调用(create pipe & close pipe), 接下来就关于这部分的取舍与具体场景进行具体讨论 + +#### splice 还是 read & write? +如何取舍使用哪种方式? + +两种方法各有各的好处, 往往选择层面的考虑在于应用层的具体策略, 如是否进行透传(/部分), 饥饿问题, 对象池策略等等 + +下面提供几种场景下的测试以供参考 +benchmark 代码: +```go +/* + * 测试环境: go 1.14.3 centos7 + */ +package main + +import ( + "bytes" + "io" + "net" + "net/http" + "sync" + "sync/atomic" + "testing" + "time" +) + +var ( + p sync.Pool +) + +func init() { + p.New = func() interface{} { + return make([]byte, DEFAULTSIZE) + } +} + +const ( + // mock http 请求体大小 + REQUESTBYTESIZE = 0 + // 应用层对象池 byte 大小 + DEFAULTSIZE = 1 << 10 + + SPLICE_F_MOVE = 0x1 + SPLICE_F_NONBLOCK = 0x2 + SPLICE_F_MORE = 0x4 + SPLICE_F_GIFT = 0x8 +) +// io.Copy 该场景下内部调用 splice syscall, 感兴趣的自行查看源码 +func gosplice(src, dst net.Conn) { + go func() { + io.Copy(src, dst) + }() + io.Copy(dst, src) +} + +func normal(src, dst net.Conn) { + var bts []byte = p.Get().([]byte) + var bts2 []byte = p.Get().([]byte) + defer p.Put(bts) + defer p.Put(bts2) + go func() { + for { + num, err := dst.Read(bts2[:]) + if err != nil { + break + } + var num_write int + for num > 0 { + num_write, err = src.Write(bts2[num_write:num]) + num -= num_write + if err != nil { + return + } + } + } + }() + // local to mock serve + for { + num, err := src.Read(bts[:]) + if err != nil { + break + } + var num_write int + for num > 0 { + num_write, err = dst.Write(bts[num_write:num]) + num -= num_write + if err != nil { + return + } + } + } +} + +// Server http server +var Server *http.Server + +type s struct{} + +func (ss s) ServeHTTP(res http.ResponseWriter, req *http.Request) { + res.WriteHeader(200) + return +} +func TestMain(m *testing.M) { + // mock tcp server + var ss s + go func() { + Server = &http.Server{ + Addr: "0.0.0.0:9610", + Handler: ss, + WriteTimeout: 60 * time.Second, + ReadTimeout: 60 * time.Second, + } + err := Server.ListenAndServe() + if err != nil { + panic(err) + } + }() + go func() { // normal 9611 + l, err := net.ListenTCP("tcp4", &net.TCPAddr{ + IP: net.ParseIP("0.0.0.0"), + Port: 9611, + }) + if err != nil { + panic(err) + } + for { + n, err := l.Accept() + if err != nil { + continue + } + defer n.Close() + remote, err := net.DialTCP("tcp4", &net.TCPAddr{ + IP: net.ParseIP("0.0.0.0"), Port: 0, + }, &net.TCPAddr{ + IP: net.ParseIP("0.0.0.0"), Port: 9610, + }) + if err != nil { + continue + } + go normal(n, remote) + } + }() + go func() { // gosplice 9612 + l, err := net.ListenTCP("tcp4", &net.TCPAddr{ + IP: net.ParseIP("0.0.0.0"), + Port: 9612, + }) + if err != nil { + panic(err) + } + for { + n, err := l.Accept() + if err != nil { + continue + } + defer n.Close() + remote, err := net.DialTCP("tcp4", &net.TCPAddr{ + IP: net.ParseIP("0.0.0.0"), Port: 0, + }, &net.TCPAddr{ + IP: net.ParseIP("0.0.0.0"), Port: 9610, + }) + if err != nil { + continue + } + go gosplice(n, remote) + } + }() + m.Run() +} +func BenchmarkNormalReadWrite(b *testing.B) { + // normal 9611 + c := http.Client{ + Timeout: time.Minute, + } + var total, success uint32 + b.ResetTimer() + for i := 0; i < b.N; i++ { + atomic.AddUint32(&total, 1) + req, err := http.NewRequest("POST", "http://0.0.0.0:9611", bytes.NewReader(make([]byte, REQUESTBYTESIZE))) + if err != nil { + b.Fatalf("%s", err.Error()) + } + res, err := c.Do(req) + if err == nil && res.StatusCode == 200 { + atomic.AddUint32(&success, 1) + } + } + b.Logf("test:%s,total: %d,rate: %.2f%%\n", b.Name(), total, float64(success*100/total)) +} + +func BenchmarkGoSplice(b *testing.B) { + // normal 9612 + c := http.Client{ + Timeout: time.Minute, + } + var total, success uint32 + b.ResetTimer() + for i := 0; i < b.N; i++ { + atomic.AddUint32(&total, 1) + req, err := http.NewRequest("POST", "http://0.0.0.0:9612", bytes.NewReader(make([]byte, REQUESTBYTESIZE))) + if err != nil { + b.Fatalf("%s", err.Error()) + } + res, err := c.Do(req) + if err == nil && res.StatusCode == 200 { + atomic.AddUint32(&success, 1) + } + } + b.Logf("test:%s, total: %d, success rate: %.2f%%\n", b.Name(), total, float64(success*100/total)) +} +``` +- 场景一: 单次请求数据量较少, 应用维护单个 buffer 较小 +```go +REQUESTBYTESIZE = 0 // http request body +DEFAULTSIZE = 1 << 10 // buffer size 1kb +``` +```shell +RRunning tool: /usr/local/bin/go test -benchmem -run=^$ -bench ^(BenchmarkNormalReadWrite|BenchmarkGoSplice)$ barrier/t + +goos: linux +goarch: amd64 +pkg: barrier/t +BenchmarkNormalReadWrite-4 6348 179699 ns/op 4847 B/op 62 allocs/op +--- BENCH: BenchmarkNormalReadWrite-4 + test_test.go:173: test:BenchmarkNormalReadWrite,total: 1,rate: 100.00% + test_test.go:173: test:BenchmarkNormalReadWrite,total: 100,rate: 100.00% + test_test.go:173: test:BenchmarkNormalReadWrite,total: 6348,rate: 100.00% +BenchmarkGoSplice-4 6652 179622 ns/op 4852 B/op 62 allocs/op +--- BENCH: BenchmarkGoSplice-4 + test_test.go:194: test:BenchmarkGoSplice, total: 1, success rate: 100.00% + test_test.go:194: test:BenchmarkGoSplice, total: 100, success rate: 100.00% + test_test.go:194: test:BenchmarkGoSplice, total: 6652, success rate: 100.00% +PASS +ok barrier/t 2.391s +``` +两种方式无明显性能差异 +- 场景二: 单次请求数据量多, 应用维护单个 buffer 较小 +```go +REQUESTBYTESIZE = 1 << 20 // 1 MB +DEFAULTSIZE = 1 << 10 // buffer size 1kb +``` +```shell +Running tool: /usr/local/bin/go test -benchmem -run=^$ -bench ^(BenchmarkNormalReadWrite|BenchmarkGoSplice)$ barrier/t + +goos: linux +goarch: amd64 +pkg: barrier/t +BenchmarkNormalReadWrite-4 465 2329209 ns/op 1073956 B/op 163 allocs/op +--- BENCH: BenchmarkNormalReadWrite-4 + test_test.go:173: test:BenchmarkNormalReadWrite,total: 1,rate: 100.00% + test_test.go:173: test:BenchmarkNormalReadWrite,total: 100,rate: 100.00% + test_test.go:173: test:BenchmarkNormalReadWrite,total: 376,rate: 100.00% + test_test.go:173: test:BenchmarkNormalReadWrite,total: 465,rate: 100.00% +BenchmarkGoSplice-4 963 1555386 ns/op 1070506 B/op 157 allocs/op +--- BENCH: BenchmarkGoSplice-4 + test_test.go:194: test:BenchmarkGoSplice, total: 1, success rate: 100.00% + test_test.go:194: test:BenchmarkGoSplice, total: 100, success rate: 100.00% + test_test.go:194: test:BenchmarkGoSplice, total: 963, success rate: 100.00% +PASS +ok barrier/t 4.056s +``` +当链接需要处理的数据量较多而应用层每次处理的 buffer 相比起来较小, 以至于需要 read & write 的次数更多的时候, 差异就会比较明显 + +#### go 中的 splice +在上面的介绍过程中简单说了下 `io.Copy` 在 `socket` 之间操作的时候, 当机器架构支持的时候会采取 `splice`, 接下来就此进行详细分析来介绍下 `runtime` 在 `splice` 上的一些决策以及当前`runtime`在 `splice` 上的一些不足 +```go +/* + * src/net/spice_linux.go + */ +// splice transfers data from r to c using the splice system call to minimize +// copies from and to userspace. c must be a TCP connection. Currently, splice +// is only enabled if r is a TCP or a stream-oriented Unix connection. +// +// If splice returns handled == false, it has performed no work. +func splice(c *netFD, r io.Reader) (written int64, err error, handled bool) { + /* + * 因为前面介绍过 splice 是通过 pipe buffer 实现的 + * 在调用的时候 kernel无需进行数据拷贝, 仅操作数据原信息(基础字段的指针等) + * 所以这边默认 splice 的 len 开得比较大, 读到 EOF 为止 + */ + var remain int64 = 1 << 62 // by default, copy until EOF + lr, ok := r.(*io.LimitedReader) + if ok { + remain, r = lr.N, lr.R + if remain <= 0 { + return 0, nil, true + } + } + + var s *netFD + if tc, ok := r.(*TCPConn); ok { + s = tc.fd + } else if uc, ok := r.(*UnixConn); ok { + if uc.fd.net != "unix" { + return 0, nil, false + } + s = uc.fd + } else { + return 0, nil, false + } + + written, handled, sc, err := poll.Splice(&c.pfd, &s.pfd, remain) + if lr != nil { + lr.N -= written + } + return written, wrapSyscallError(sc, err), handled +} +``` +```go +/* + * go 1.14.3 + * src/internal/poll/splice_linux.go + */ +// Splice transfers at most remain bytes of data from src to dst, using the +// splice system call to minimize copies of data from and to userspace. +// +// Splice creates a temporary pipe, to serve as a buffer for the data transfer. +// src and dst must both be stream-oriented sockets. +// +// If err != nil, sc is the system call which caused the error. +func Splice(dst, src *FD, remain int64) (written int64, handled bool, sc string, err error) { + // dst 以及 src 均为 socket.fd, 因此若想使用 splice 则需要借助 pipe + // 创建临时 pipe + prfd, pwfd, sc, err := newTempPipe() + if err != nil { + return 0, false, sc, err + } + defer destroyTempPipe(prfd, pwfd) + var inPipe, n int + for err == nil && remain > 0 { + max := maxSpliceSize + if int64(max) > remain { + max = int(remain) + } + // spliceDrain 调用 splice syscall + inPipe, err = spliceDrain(pwfd, src, max) + // The operation is considered handled if splice returns no + // error, or an error other than EINVAL. An EINVAL means the + // kernel does not support splice for the socket type of src. + // The failed syscall does not consume any data so it is safe + // to fall back to a generic copy. + // + // spliceDrain should never return EAGAIN, so if err != nil, + // Splice cannot continue. + // + // If inPipe == 0 && err == nil, src is at EOF, and the + // transfer is complete. + handled = handled || (err != syscall.EINVAL) + if err != nil || (inPipe == 0 && err == nil) { + break + } + // splicePump 调用 splice syscall + n, err = splicePump(dst, prfd, inPipe) + if n > 0 { + written += int64(n) + remain -= int64(n) + } + } + if err != nil { + return written, handled, "splice", err + } + return written, true, "", nil +} +``` + +相信上面简短的分析大家也可以看到, 每次在进行 `splice` 的时候都会利用临时 `pipe`, 频繁的创建、销毁, 用户态-内核态的切换会带来非常多不必要的开销, 当前社区内也有关于 `splice temp-pipe` 生命周期的[讨论](https://go-review.googlesource.com/c/go/+/271537/)。 + +再者, 因为当前关联到 `socket` 的 `splice` 实现在 `runtime` 层面和内置 `io 模型(epoll 等)`高度耦合, 基本无法解耦单独应用, 而如果想自己来实现 `splice(syscall.Splice)` 的话则不得不顺带在用户层面实现自己的 `io 模型`再来使用, 会比较繁琐(上面测试用例使用内置 `splice api` 也是因为这个原因) + +## 参考资料 + +- https://go-review.googlesource.com/c/go/+/271537/ +- https://zhuanlan.zhihu.com/p/308054212 \ No newline at end of file From 9c2bd4bee9b650866bf49d8c7894281de8a07ef7 Mon Sep 17 00:00:00 2001 From: wziww Date: Thu, 22 Apr 2021 11:13:11 +0800 Subject: [PATCH 077/120] =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- io.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/io.md b/io.md index 282ffff..c0c7d72 100644 --- a/io.md +++ b/io.md @@ -326,11 +326,11 @@ func example(src, dst net.Conn) { | application | ----------------------------------------------------------------------------------------------- ········································|····················|·····················|·································· - | splice() | pipe() | splice() + | splice() | pipe() | splice() [ kernel space ] | | | ----------------- ”copy“ ----------------------- ”copy“ ----------------- | socket buffer |· · · · · · · · · >| pipe writefd & readfd |· · · · · · · · >| socket buffer | - ----------------- ----------------------- ----------------- + ----------------- ----------------------- ----------------- | copy | ····················|··············································································| [ hardware sapce ] | | From 456ab4f0fb4ff9d62ed71d826a9a6f166477ea70 Mon Sep 17 00:00:00 2001 From: wziww Date: Thu, 22 Apr 2021 13:44:13 +0800 Subject: [PATCH 078/120] =?UTF-8?q?=E4=B9=B1=E7=A0=81=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- io.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/io.md b/io.md index c0c7d72..ce6a200 100644 --- a/io.md +++ b/io.md @@ -710,9 +710,9 @@ func Splice(dst, src *FD, remain int64) (written int64, handled bool, sc string, } ``` -相信上面简短的分析大家也可以看到, 每次在进行 `splice` 的时候都会利用临时 `pipe`, 频繁的创建、销毁, 用户态-内核态的切换会带来非常多不必要的开销, 当前社区内也有关于 `splice temp-pipe` 生命周期的[讨论](https://go-review.googlesource.com/c/go/+/271537/)。 +相信上面简短的分析大家也可以看到, 每次在进行 `splice` 的时候都会利用临时 `pipe`, 频繁的创建、销毁, 用户态-内核态的切换会带来非常多不必要的开销, 当前社区内也有关于 `splice temp-pipe` 生命周期的[讨论](https://go-review.googlesource.com/c/go/+/271537/)。 -再者, 因为当前关联到 `socket` 的 `splice` 实现在 `runtime` 层面和内置 `io 模型(epoll 等)`高度耦合, 基本无法解耦单独应用, 而如果想自己来实现 `splice(syscall.Splice)` 的话则不得不顺带在用户层面实现自己的 `io 模型`再来使用, 会比较繁琐(上面测试用例使用内置 `splice api` 也是因为这个原因) +再者, 因为当前关联到 `socket` 的 `splice` 实现在 `runtime` 层面和内置 `io 模型(epoll 等)`高度耦合, 基本无法解耦单独应用, 而如果想自己来实现 `splice(syscall.Splice)` 的话则不得不顺带在用户层面实现自己的`io 模型`再来使用, 会比较繁琐(上面测试用例使用内置 `splice api` 也是因为这个原因) ## 参考资料 From 321a751fc3a728240a2875f1683af392d394f7e1 Mon Sep 17 00:00:00 2001 From: wziww Date: Thu, 22 Apr 2021 13:45:20 +0800 Subject: [PATCH 079/120] =?UTF-8?q?=E4=B9=B1=E7=A0=81=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- io.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io.md b/io.md index ce6a200..722a7f8 100644 --- a/io.md +++ b/io.md @@ -712,7 +712,7 @@ func Splice(dst, src *FD, remain int64) (written int64, handled bool, sc string, 相信上面简短的分析大家也可以看到, 每次在进行 `splice` 的时候都会利用临时 `pipe`, 频繁的创建、销毁, 用户态-内核态的切换会带来非常多不必要的开销, 当前社区内也有关于 `splice temp-pipe` 生命周期的[讨论](https://go-review.googlesource.com/c/go/+/271537/)。 -再者, 因为当前关联到 `socket` 的 `splice` 实现在 `runtime` 层面和内置 `io 模型(epoll 等)`高度耦合, 基本无法解耦单独应用, 而如果想自己来实现 `splice(syscall.Splice)` 的话则不得不顺带在用户层面实现自己的`io 模型`再来使用, 会比较繁琐(上面测试用例使用内置 `splice api` 也是因为这个原因) +再者, 因为当前关联到 `socket` 的 `splice` 实现在 `runtime` 层面和内置 `io 模型(epoll 等)`高度耦合, 基本无法解耦单独应用, 而如果想自己来实现 `splice(syscall.Splice)` 的话则不得不顺带在用户层面实现自己的`io 模型`再来使用, 会比较繁琐(上面测试用例使用内置 `splice api` 也是因为这个原因) ## 参考资料 From 27c9896daec443ccaa80f9a64ce31844c488a424 Mon Sep 17 00:00:00 2001 From: Xargin Date: Thu, 22 Apr 2021 16:05:29 +0800 Subject: [PATCH 080/120] update readme, add io --- readme.md | 1 + 1 file changed, 1 insertion(+) diff --git a/readme.md b/readme.md index cbd4555..4e982e5 100644 --- a/readme.md +++ b/readme.md @@ -29,6 +29,7 @@ 23. [x] [stack dump](runtime_stack.md) 24. [x] [Atomic](atomic.md) 25. [ ] [Generics](generics.md) +26. [x] [IO](io.md) # Authors From b575c76b0f828d9f31a625f377bb3961e5d7379e Mon Sep 17 00:00:00 2001 From: wziww Date: Tue, 27 Apr 2021 15:59:41 +0800 Subject: [PATCH 081/120] pprof --- pprof.md | 232 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 232 insertions(+) create mode 100644 pprof.md diff --git a/pprof.md b/pprof.md new file mode 100644 index 0000000..0d090a0 --- /dev/null +++ b/pprof.md @@ -0,0 +1,232 @@ +# pprof +在进行深入本章节之前, 让我们来看三个问题, 相信下面这几个问题也是大部分人在使用 pprof 的时候对它最大的困惑, 那么可以带着这三个问题来进行接下去的分析 +- 开启 pprof 会对 runtime 产生多大的压力? +- 能否选择性在合适阶段对生产环境的应用进行 pprof 的开启 / 关闭操作? +- pprof 的原理是什么? + +go 内置的 `pprof API` 在 `runtime/pprof` 包内, 它提供给了用户与 `runtime` 交互的能力, 让我们能够在应用运行的过程中分析当前应用的各项指标来辅助进行性能优化以及问题排查, 当然也可以直接加载 `_ "net/http/pprof"` 包使用内置的 `http 接口` 来进行使用, `net` 模块内的 pprof 即为 go 替我们封装好的一系列调用 `runtime/pprof` 的方法, 当然也可以自己直接使用 +```go +// src/runtime/pprof/pprof.go +// 可观察类目 +profiles.m = map[string]*Profile{ + "goroutine": goroutineProfile, + "threadcreate": threadcreateProfile, + "heap": heapProfile, + "allocs": allocsProfile, + "block": blockProfile, + "mutex": mutexProfile, + } +``` + +## allocs +```go + +var allocsProfile = &Profile{ + name: "allocs", + count: countHeap, // identical to heap profile + write: writeAlloc, +} +``` +- writeAlloc (主要涉及以下几个 api) + - ReadMemStats(m *MemStats) + - MemProfile(p []MemProfileRecord, inuseZero bool) + +```go +// ReadMemStats populates m with memory allocator statistics. +// +// The returned memory allocator statistics are up to date as of the +// call to ReadMemStats. This is in contrast with a heap profile, +// which is a snapshot as of the most recently completed garbage +// collection cycle. +func ReadMemStats(m *MemStats) { + // STW 操作 + stopTheWorld("read mem stats") + // systemstack 切换 + systemstack(func() { + // 将 memstats 通过 copy 操作复制给 m + readmemstats_m(m) + }) + + startTheWorld() +} +``` + +```go +// MemProfile returns a profile of memory allocated and freed per allocation +// site. +// +// MemProfile returns n, the number of records in the current memory profile. +// If len(p) >= n, MemProfile copies the profile into p and returns n, true. +// If len(p) < n, MemProfile does not change p and returns n, false. +// +// If inuseZero is true, the profile includes allocation records +// where r.AllocBytes > 0 but r.AllocBytes == r.FreeBytes. +// These are sites where memory was allocated, but it has all +// been released back to the runtime. +// +// The returned profile may be up to two garbage collection cycles old. +// This is to avoid skewing the profile toward allocations; because +// allocations happen in real time but frees are delayed until the garbage +// collector performs sweeping, the profile only accounts for allocations +// that have had a chance to be freed by the garbage collector. +// +// Most clients should use the runtime/pprof package or +// the testing package's -test.memprofile flag instead +// of calling MemProfile directly. +func MemProfile(p []MemProfileRecord, inuseZero bool) (n int, ok bool) { + lock(&proflock) + // If we're between mProf_NextCycle and mProf_Flush, take care + // of flushing to the active profile so we only have to look + // at the active profile below. + mProf_FlushLocked() + clear := true + /* + * 记住这个 mbuckets -- memory profile buckets + * allocs 的采样都是记录在这个全局变量内, 下面会进行详细分析 + * ------------------------------------------------- + * (gdb) info variables mbuckets + * All variables matching regular expression "mbuckets": + + * File runtime: + * runtime.bucket *runtime.mbuckets; + * (gdb) + */ + for b := mbuckets; b != nil; b = b.allnext { + mp := b.mp() + if inuseZero || mp.active.alloc_bytes != mp.active.free_bytes { + n++ + } + if mp.active.allocs != 0 || mp.active.frees != 0 { + clear = false + } + } + if clear { + // Absolutely no data, suggesting that a garbage collection + // has not yet happened. In order to allow profiling when + // garbage collection is disabled from the beginning of execution, + // accumulate all of the cycles, and recount buckets. + n = 0 + for b := mbuckets; b != nil; b = b.allnext { + mp := b.mp() + for c := range mp.future { + mp.active.add(&mp.future[c]) + mp.future[c] = memRecordCycle{} + } + if inuseZero || mp.active.alloc_bytes != mp.active.free_bytes { + n++ + } + } + } + if n <= len(p) { + ok = true + idx := 0 + for b := mbuckets; b != nil; b = b.allnext { + mp := b.mp() + if inuseZero || mp.active.alloc_bytes != mp.active.free_bytes { + // mbuckets 数据拷贝 + record(&p[idx], b) + idx++ + } + } + } + unlock(&proflock) + return +} +``` + +总结一下 `pprof/allocs` 所涉及的操作 +- 短暂的 `STW` 以及 `systemstack` 切换来获取 `runtime` 相关信息 +- 拷贝全局对象 `mbuckets` 值返回给用户 + +### mbuckets +上文提到, `pprof/allocs` 的核心在于对 `mbuckets` 的操作, 下面用一张图来简单描述下 `mbuckets` 的相关操作 +```go +var mbuckets *bucket // memory profile buckets +type bucket struct { + next *bucket + allnext *bucket + typ bucketType // memBucket or blockBucket (includes mutexProfile) + hash uintptr + size uintptr + nstk uintptr +} +``` + + +```shell + --------------- + | user access | + --------------- + | + ------------------ | +| mbuckets list | copy | +| (global) | ------------------------------------- + ------------------ + | + | + | create && insert new bucket into mbuckets + | + | + -------------------------------------- +| func stkbucket & typ == memProfile | + -------------------------------------- + | + ---------------- + | mProf_Malloc | // 堆栈等信息记录 + ---------------- + | + ---------------- + | profilealloc | // next_sample 计算 + ---------------- + | + | /* + | * if rate := MemProfileRate; rate > 0 { + | * if rate != 1 && size < c.next_sample { + | * c.next_sample -= size + | 采样 * } else { + | 记录 * mp := acquirem() + | * profilealloc(mp, x, size) + | * releasem(mp) + | * } + | * } + | */ + | + ------------ 不采样 + | mallocgc |-----------... + ------------ +``` + +由上图我们可以清晰的看见, `runtime` 在内存分配的时候会根据一定策略进行采样, 记录到 `mbuckets` 中让用户得以进行分析, 而采样算法有个重要的依赖 `MemProfileRate` + +```go +// MemProfileRate controls the fraction of memory allocations +// that are recorded and reported in the memory profile. +// The profiler aims to sample an average of +// one allocation per MemProfileRate bytes allocated. +// +// To include every allocated block in the profile, set MemProfileRate to 1. +// To turn off profiling entirely, set MemProfileRate to 0. +// +// The tools that process the memory profiles assume that the +// profile rate is constant across the lifetime of the program +// and equal to the current value. Programs that change the +// memory profiling rate should do so just once, as early as +// possible in the execution of the program (for example, +// at the beginning of main). +var MemProfileRate int = 512 * 1024 +``` +默认大小是 512 KB, 可以由用户自行配置. + +值的注意的是, 由于开启了 pprof 会产生一些采样的额外压力及开销, go 团队已经在较新的编译器中有选择地进行了这个变量的配置以[改变](https://go-review.googlesource.com/c/go/+/299671/8/src/runtime/mprof.go)默认开启的现状 + +具体方式为代码未进行相关引用则编译器将初始值配置为 0, 否则则为默认(512 KB) + +(本文讨论的基于 1.14.3 版本, 如有差异请进行版本确认) + +#### pprof/mallocs 总结 +- 开启后会对 runtime 产生额外压力, 采样时会在 `runtime malloc` 时记录额外信息以供后续分析 +- 可以人为选择是否开启, 以及采样频率, 通过设置 `runtime.MemProfileRate` 参数, 不同 go 版本存在差异(是否默认开启), 与用户代码内是否引用(linker)相关模块/变量有关, 默认大小为 512 KB + + +# 参考资料 +https://go-review.googlesource.com/c/go/+/299671 \ No newline at end of file From ccbda406e58e860617ff51c1025ddaba32e9bb1b Mon Sep 17 00:00:00 2001 From: wziww Date: Tue, 27 Apr 2021 16:03:32 +0800 Subject: [PATCH 082/120] =?UTF-8?q?pprof-malloc=20=E9=83=A8=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- readme.md | 1 + 1 file changed, 1 insertion(+) diff --git a/readme.md b/readme.md index 4e982e5..d0c7ab2 100644 --- a/readme.md +++ b/readme.md @@ -30,6 +30,7 @@ 24. [x] [Atomic](atomic.md) 25. [ ] [Generics](generics.md) 26. [x] [IO](io.md) +26. [x] [pprof](pprof.md) # Authors From edd695fdd2fe1759279c8e706a27deb3b253b5a1 Mon Sep 17 00:00:00 2001 From: maiyang Date: Wed, 28 Apr 2021 07:47:19 +0800 Subject: [PATCH 083/120] Update io.md --- io.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/io.md b/io.md index 722a7f8..6e950b6 100644 --- a/io.md +++ b/io.md @@ -290,7 +290,7 @@ func normal(src, dst net.Conn) { ssize_t splice(int fd_in, off64_t *off_in, int fd_out, off64_t *off_out, size_t len, unsigned int flags); ``` -一句话概括就是, `splice` 不需要从内核空间复制这部分数据到用户空间就可以支持将数据从两个文件描述符之间进行转移, 不过两个描述符至少得有一个是 `pipe`, 一下列举如何利用`splice`完成 `socket->socket` 的数据代理 +一句话概括就是, `splice` 不需要从内核空间复制这部分数据到用户空间就可以支持将数据从两个文件描述符之间进行转移, 不过两个描述符至少得有一个是 `pipe`, 以下列举如何利用`splice`完成 `socket->socket` 的数据代理 example: ```go @@ -717,4 +717,4 @@ func Splice(dst, src *FD, remain int64) (written int64, handled bool, sc string, ## 参考资料 - https://go-review.googlesource.com/c/go/+/271537/ -- https://zhuanlan.zhihu.com/p/308054212 \ No newline at end of file +- https://zhuanlan.zhihu.com/p/308054212 From 94d156b9e0b54d801fa922aba1561e2744d151db Mon Sep 17 00:00:00 2001 From: wziww Date: Wed, 28 Apr 2021 15:29:22 +0800 Subject: [PATCH 084/120] =?UTF-8?q?pprof-heap=20=E4=BC=B0=E7=AE=97?= =?UTF-8?q?=E9=83=A8=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pprof.md | 199 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 138 insertions(+), 61 deletions(-) diff --git a/pprof.md b/pprof.md index 0d090a0..5192875 100644 --- a/pprof.md +++ b/pprof.md @@ -22,9 +22,9 @@ profiles.m = map[string]*Profile{ ```go var allocsProfile = &Profile{ - name: "allocs", - count: countHeap, // identical to heap profile - write: writeAlloc, + name: "allocs", + count: countHeap, // identical to heap profile + write: writeAlloc, } ``` - writeAlloc (主要涉及以下几个 api) @@ -40,14 +40,14 @@ var allocsProfile = &Profile{ // collection cycle. func ReadMemStats(m *MemStats) { // STW 操作 - stopTheWorld("read mem stats") + stopTheWorld("read mem stats") // systemstack 切换 - systemstack(func() { + systemstack(func() { // 将 memstats 通过 copy 操作复制给 m - readmemstats_m(m) - }) + readmemstats_m(m) + }) - startTheWorld() + startTheWorld() } ``` @@ -74,12 +74,12 @@ func ReadMemStats(m *MemStats) { // the testing package's -test.memprofile flag instead // of calling MemProfile directly. func MemProfile(p []MemProfileRecord, inuseZero bool) (n int, ok bool) { - lock(&proflock) - // If we're between mProf_NextCycle and mProf_Flush, take care - // of flushing to the active profile so we only have to look - // at the active profile below. - mProf_FlushLocked() - clear := true + lock(&proflock) + // If we're between mProf_NextCycle and mProf_Flush, take care + // of flushing to the active profile so we only have to look + // at the active profile below. + mProf_FlushLocked() + clear := true /* * 记住这个 mbuckets -- memory profile buckets * allocs 的采样都是记录在这个全局变量内, 下面会进行详细分析 @@ -91,46 +91,46 @@ func MemProfile(p []MemProfileRecord, inuseZero bool) (n int, ok bool) { * runtime.bucket *runtime.mbuckets; * (gdb) */ - for b := mbuckets; b != nil; b = b.allnext { - mp := b.mp() - if inuseZero || mp.active.alloc_bytes != mp.active.free_bytes { - n++ - } - if mp.active.allocs != 0 || mp.active.frees != 0 { - clear = false - } - } - if clear { - // Absolutely no data, suggesting that a garbage collection - // has not yet happened. In order to allow profiling when - // garbage collection is disabled from the beginning of execution, - // accumulate all of the cycles, and recount buckets. - n = 0 - for b := mbuckets; b != nil; b = b.allnext { - mp := b.mp() - for c := range mp.future { - mp.active.add(&mp.future[c]) - mp.future[c] = memRecordCycle{} - } - if inuseZero || mp.active.alloc_bytes != mp.active.free_bytes { - n++ - } - } - } - if n <= len(p) { - ok = true - idx := 0 - for b := mbuckets; b != nil; b = b.allnext { - mp := b.mp() - if inuseZero || mp.active.alloc_bytes != mp.active.free_bytes { + for b := mbuckets; b != nil; b = b.allnext { + mp := b.mp() + if inuseZero || mp.active.alloc_bytes != mp.active.free_bytes { + n++ + } + if mp.active.allocs != 0 || mp.active.frees != 0 { + clear = false + } + } + if clear { + // Absolutely no data, suggesting that a garbage collection + // has not yet happened. In order to allow profiling when + // garbage collection is disabled from the beginning of execution, + // accumulate all of the cycles, and recount buckets. + n = 0 + for b := mbuckets; b != nil; b = b.allnext { + mp := b.mp() + for c := range mp.future { + mp.active.add(&mp.future[c]) + mp.future[c] = memRecordCycle{} + } + if inuseZero || mp.active.alloc_bytes != mp.active.free_bytes { + n++ + } + } + } + if n <= len(p) { + ok = true + idx := 0 + for b := mbuckets; b != nil; b = b.allnext { + mp := b.mp() + if inuseZero || mp.active.alloc_bytes != mp.active.free_bytes { // mbuckets 数据拷贝 - record(&p[idx], b) - idx++ - } - } - } - unlock(&proflock) - return + record(&p[idx], b) + idx++ + } + } + } + unlock(&proflock) + return } ``` @@ -143,12 +143,12 @@ func MemProfile(p []MemProfileRecord, inuseZero bool) (n int, ok bool) { ```go var mbuckets *bucket // memory profile buckets type bucket struct { - next *bucket - allnext *bucket - typ bucketType // memBucket or blockBucket (includes mutexProfile) - hash uintptr - size uintptr - nstk uintptr + next *bucket + allnext *bucket + typ bucketType // memBucket or blockBucket (includes mutexProfile) + hash uintptr + size uintptr + nstk uintptr } ``` @@ -164,7 +164,7 @@ type bucket struct { ------------------ | | - | create && insert new bucket into mbuckets + | create_or_get && insert_or_update bucket into mbuckets | | -------------------------------------- @@ -223,10 +223,87 @@ var MemProfileRate int = 512 * 1024 (本文讨论的基于 1.14.3 版本, 如有差异请进行版本确认) -#### pprof/mallocs 总结 +#### pprof/allocs 总结 - 开启后会对 runtime 产生额外压力, 采样时会在 `runtime malloc` 时记录额外信息以供后续分析 - 可以人为选择是否开启, 以及采样频率, 通过设置 `runtime.MemProfileRate` 参数, 不同 go 版本存在差异(是否默认开启), 与用户代码内是否引用(linker)相关模块/变量有关, 默认大小为 512 KB +`allocs` 部分还包含了 `heap` 情况的近似计算, 放在下一节分析 +## heap +>allocs: A sampling of all past memory allocations + +>heap: A sampling of memory allocations of live objects. You can specify the gc GET parameter to run GC before taking the heap sample. +对比下 `allocs` 和 `heap` 官方说明上的区别, 一个是分析所有内存分配的情况, 一个是当前 `heap` 上的分配情况. `heap` 还能使用额外参数运行一次 `GC` 后再进行分析 + +看起来两者差别很大。。。不过实质上在代码层面两者除了一次 `GC` 可以人为调用以及生成的文件类型不同之外 (debug == 0 的时候) 之外没啥区别. + +### heap 采样(伪) +```go +// p 为上文提到过的 MemProfileRecord 采样记录 +for _, r := range p { + hideRuntime := true + for tries := 0; tries < 2; tries++ { + stk := r.Stack() + // For heap profiles, all stack + // addresses are return PCs, which is + // what appendLocsForStack expects. + if hideRuntime { + for i, addr := range stk { + if f := runtime.FuncForPC(addr); f != nil && strings.HasPrefix(f.Name(), "runtime.") { + continue + } + // Found non-runtime. Show any runtime uses above it. + stk = stk[i:] + break + } + } + locs = b.appendLocsForStack(locs[:0], stk) + if len(locs) > 0 { + break + } + hideRuntime = false // try again, and show all frames next time. + } + // rate 即为 runtime.MemProfileRate + values[0], values[1] = scaleHeapSample(r.AllocObjects, r.AllocBytes, rate) + values[2], values[3] = scaleHeapSample(r.InUseObjects(), r.InUseBytes(), rate) + var blockSize int64 + if r.AllocObjects > 0 { + blockSize = r.AllocBytes / r.AllocObjects + } + b.pbSample(values, locs, func() { + if blockSize != 0 { + b.pbLabel(tagSample_Label, "bytes", "", blockSize) + } + }) + } +``` +```go +// scaleHeapSample adjusts the data from a heap Sample to +// account for its probability of appearing in the collected +// data. heap profiles are a sampling of the memory allocations +// requests in a program. We estimate the unsampled value by dividing +// each collected sample by its probability of appearing in the +// profile. heap profiles rely on a poisson process to determine +// which samples to collect, based on the desired average collection +// rate R. The probability of a sample of size S to appear in that +// profile is 1-exp(-S/R). +func scaleHeapSample(count, size, rate int64) (int64, int64) { + if count == 0 || size == 0 { + return 0, 0 + } + + if rate <= 1 { + // if rate==1 all samples were collected so no adjustment is needed. + // if rate<1 treat as unknown and skip scaling. + return count, size + } + + avgSize := float64(size) / float64(count) + scale := 1 / (1 - math.Exp(-avgSize/float64(rate))) + + return int64(float64(count) * scale), int64(float64(size) * scale) +} +``` +为什么要在标题里加个伪? 看上面代码片段也可以注意到, 实质上在 `pprof` 分析的时候并没有扫描所有堆上内存进行分析 (想想也不现实) , 而是通过之前采样出的数据, 进行计算 (现有对象数量, 大小, 采样率等) 来估算出 `heap` 上的情况, 当然给我们参考一般来说是足够了 # 参考资料 https://go-review.googlesource.com/c/go/+/299671 \ No newline at end of file From 7e5582cebc77fc29c1fe825dcc378c4c2716eb8a Mon Sep 17 00:00:00 2001 From: wziww Date: Wed, 28 Apr 2021 18:55:16 +0800 Subject: [PATCH 085/120] goroutine&threadcreate --- pprof.md | 193 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 193 insertions(+) diff --git a/pprof.md b/pprof.md index 5192875..e4a2c97 100644 --- a/pprof.md +++ b/pprof.md @@ -304,6 +304,199 @@ func scaleHeapSample(count, size, rate int64) (int64, int64) { return int64(float64(count) * scale), int64(float64(size) * scale) } ``` + 为什么要在标题里加个伪? 看上面代码片段也可以注意到, 实质上在 `pprof` 分析的时候并没有扫描所有堆上内存进行分析 (想想也不现实) , 而是通过之前采样出的数据, 进行计算 (现有对象数量, 大小, 采样率等) 来估算出 `heap` 上的情况, 当然给我们参考一般来说是足够了 + +## goroutine +- debug >= 2 的情况, 直接进行堆栈输出, 详情可以查看 [stack](runtime_stack.md) 章节 + +```go +// fetch == runtime.GoroutineProfile +func writeRuntimeProfile(w io.Writer, debug int, name string, fetch func([]runtime.StackRecord) (int, bool)) error { + // Find out how many records there are (fetch(nil)), + // allocate that many records, and get the data. + // There's a race—more records might be added between + // the two calls—so allocate a few extra records for safety + // and also try again if we're very unlucky. + // The loop should only execute one iteration in the common case. + var p []runtime.StackRecord + n, ok := fetch(nil) + for { + // Allocate room for a slightly bigger profile, + // in case a few more entries have been added + // since the call to ThreadProfile. + p = make([]runtime.StackRecord, n+10) + n, ok = fetch(p) + if ok { + p = p[0:n] + break + } + // Profile grew; try again. + } + + return printCountProfile(w, debug, name, runtimeProfile(p)) +} +``` + +```go +// GoroutineProfile returns n, the number of records in the active goroutine stack profile. +// If len(p) >= n, GoroutineProfile copies the profile into p and returns n, true. +// If len(p) < n, GoroutineProfile does not change p and returns n, false. +// +// Most clients should use the runtime/pprof package instead +// of calling GoroutineProfile directly. +func GoroutineProfile(p []StackRecord) (n int, ok bool) { + gp := getg() + + isOK := func(gp1 *g) bool { + // Checking isSystemGoroutine here makes GoroutineProfile + // consistent with both NumGoroutine and Stack. + return gp1 != gp && readgstatus(gp1) != _Gdead && !isSystemGoroutine(gp1, false) + } + // 熟悉的味道, STW 又来了 + stopTheWorld("profile") + // 统计有多少 goroutine + n = 1 + for _, gp1 := range allgs { + if isOK(gp1) { + n++ + } + } + // 当传入的 p 非空的时候, 开始获取各个 goroutine 信息, 整体姿势和 stack api 几乎一模一样 + if n <= len(p) { + ok = true + r := p + + // Save current goroutine. + sp := getcallersp() + pc := getcallerpc() + systemstack(func() { + saveg(pc, sp, gp, &r[0]) + }) + r = r[1:] + + // Save other goroutines. + for _, gp1 := range allgs { + if isOK(gp1) { + if len(r) == 0 { + // Should be impossible, but better to return a + // truncated profile than to crash the entire process. + break + } + saveg(^uintptr(0), ^uintptr(0), gp1, &r[0]) + r = r[1:] + } + } + } + + startTheWorld() + + return n, ok +} +``` +总结下 `pprof/goroutine` +- STW 操作, 如果需要观察详情的需要注意这个 API 带来的风险 +- 整体流程基本就是 stackdump 所有协程信息的流程, 差别不大没什么好讲的, 不熟悉的可以去看下 stack 对应章节 + +## pprof/threadcreate +可能会有人想问, 我们通常只关注 `goroutine` 就够了, 为什么还需要对线程的一些情况进行追踪? 例如无法被抢占的阻塞性[系统调用](syscall.md), `cgo` 相关的线程等等, 都可以利用它来进行一个简单的分析, 当然大多数情况考虑的线程问题(诸如泄露等), 一般都是上层的使用问题所导致的(线程泄露等) +```go +// 还是用之前用过的无法被抢占的阻塞性系统调用来进行一个简单的实验 +package main + +import ( + "fmt" + "net/http" + _ "net/http/pprof" + "os" + "syscall" + "unsafe" +) + +const ( + SYS_futex = 202 + _FUTEX_PRIVATE_FLAG = 128 + _FUTEX_WAIT = 0 + _FUTEX_WAKE = 1 + _FUTEX_WAIT_PRIVATE = _FUTEX_WAIT | _FUTEX_PRIVATE_FLAG + _FUTEX_WAKE_PRIVATE = _FUTEX_WAKE | _FUTEX_PRIVATE_FLAG +) + +func main() { + fmt.Println(os.Getpid()) + go func() { + b := make([]byte, 1<<20) + _ = b + }() + for i := 1; i < 13; i++ { + go func() { + var futexVar int = 0 + for { + // Syscall && RawSyscall, 具体差别分析可自行查看 syscall 章节 + fmt.Println(syscall.Syscall6( + SYS_futex, // trap AX 202 + uintptr(unsafe.Pointer(&futexVar)), // a1 DI 1 + uintptr(_FUTEX_WAIT), // a2 SI 0 + 0, // a3 DX + 0, //uintptr(unsafe.Pointer(&ts)), // a4 R10 + 0, // a5 R8 + 0)) + } + }() + } + http.ListenAndServe("0.0.0.0:8899", nil) +} +``` +```shell +# GET /debug/pprof/threadcreate?debug=1 +threadcreate profile: total 18 +17 @ +# 0x0 + +1 @ 0x43b818 0x43bfa3 0x43c272 0x43857d 0x467fb1 +# 0x43b817 runtime.allocm+0x157 /usr/local/go/src/runtime/proc.go:1414 +# 0x43bfa2 runtime.newm+0x42 /usr/local/go/src/runtime/proc.go:1736 +# 0x43c271 runtime.startTemplateThread+0xb1 /usr/local/go/src/runtime/proc.go:1805 +# 0x43857c runtime.main+0x18c /usr/local/go/src/runtime/proc.go:186 +``` +```shell +# 再结合诸如 pstack 的工具 +ps -efT | grep 22298 # pid = 22298 +root 22298 22298 13767 0 16:59 pts/4 00:00:00 ./mstest +root 22298 22299 13767 0 16:59 pts/4 00:00:00 ./mstest +root 22298 22300 13767 0 16:59 pts/4 00:00:00 ./mstest +root 22298 22301 13767 0 16:59 pts/4 00:00:00 ./mstest +root 22298 22302 13767 0 16:59 pts/4 00:00:00 ./mstest +root 22298 22303 13767 0 16:59 pts/4 00:00:00 ./mstest +root 22298 22304 13767 0 16:59 pts/4 00:00:00 ./mstest +root 22298 22305 13767 0 16:59 pts/4 00:00:00 ./mstest +root 22298 22306 13767 0 16:59 pts/4 00:00:00 ./mstest +root 22298 22307 13767 0 16:59 pts/4 00:00:00 ./mstest +root 22298 22308 13767 0 16:59 pts/4 00:00:00 ./mstest +root 22298 22309 13767 0 16:59 pts/4 00:00:00 ./mstest +root 22298 22310 13767 0 16:59 pts/4 00:00:00 ./mstest +root 22298 22311 13767 0 16:59 pts/4 00:00:00 ./mstest +root 22298 22312 13767 0 16:59 pts/4 00:00:00 ./mstest +root 22298 22316 13767 0 16:59 pts/4 00:00:00 ./mstest +root 22298 22317 13767 0 16:59 pts/4 00:00:00 ./mstest + +pstack 22299 +Thread 1 (process 22299): +#0 runtime.futex () at /usr/local/go/src/runtime/sys_linux_amd64.s:568 +#1 0x00000000004326f4 in runtime.futexsleep (addr=0xb2fd78 , val=0, ns=60000000000) at /usr/local/go/src/runtime/os_linux.go:51 +#2 0x000000000040cb3e in runtime.notetsleep_internal (n=0xb2fd78 , ns=60000000000, ~r2=) at /usr/local/go/src/runtime/lock_futex.go:193 +#3 0x000000000040cc11 in runtime.notetsleep (n=0xb2fd78 , ns=60000000000, ~r2=) at /usr/local/go/src/runtime/lock_futex.go:216 +#4 0x00000000004433b2 in runtime.sysmon () at /usr/local/go/src/runtime/proc.go:4558 +#5 0x000000000043af33 in runtime.mstart1 () at /usr/local/go/src/runtime/proc.go:1112 +#6 0x000000000043ae4e in runtime.mstart () at /usr/local/go/src/runtime/proc.go:1077 +#7 0x0000000000401893 in runtime/cgo(.text) () +#8 0x00007fb1e2d53700 in ?? () +#9 0x0000000000000000 in ?? () +``` +其他的线程如果感兴趣也可以仔细查看 + +`pprof/threadcreate` 具体实现和 `pprof/goroutine` 类似, 无非前者遍历的对象是全局 `allm`, 而后者为 `allgs`, 区别在于 `pprof/threadcreate => ThreadCreateProfile` 时不会进行进行 `STW` + + # 参考资料 https://go-review.googlesource.com/c/go/+/299671 \ No newline at end of file From 375f30dd2be716eda45b0bea08b8f598a1b82e61 Mon Sep 17 00:00:00 2001 From: wziww Date: Thu, 29 Apr 2021 15:24:53 +0800 Subject: [PATCH 086/120] pprof - cpu profile --- pprof.md | 355 ++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 314 insertions(+), 41 deletions(-) diff --git a/pprof.md b/pprof.md index e4a2c97..759fc54 100644 --- a/pprof.md +++ b/pprof.md @@ -1,4 +1,5 @@ # pprof +> 本章节没有介绍具体 pprof 以及周边工具的使用, 而是进行了 runtime pprof 实现原理的分析, 旨在提供给读者一个使用方面的参考 在进行深入本章节之前, 让我们来看三个问题, 相信下面这几个问题也是大部分人在使用 pprof 的时候对它最大的困惑, 那么可以带着这三个问题来进行接下去的分析 - 开启 pprof 会对 runtime 产生多大的压力? - 能否选择性在合适阶段对生产环境的应用进行 pprof 的开启 / 关闭操作? @@ -355,14 +356,14 @@ func GoroutineProfile(p []StackRecord) (n int, ok bool) { } // 熟悉的味道, STW 又来了 stopTheWorld("profile") - // 统计有多少 goroutine + // 统计有多少 goroutine n = 1 for _, gp1 := range allgs { if isOK(gp1) { n++ } } - // 当传入的 p 非空的时候, 开始获取各个 goroutine 信息, 整体姿势和 stack api 几乎一模一样 + // 当传入的 p 非空的时候, 开始获取各个 goroutine 信息, 整体姿势和 stack api 几乎一模一样 if n <= len(p) { ok = true r := p @@ -405,59 +406,59 @@ func GoroutineProfile(p []StackRecord) (n int, ok bool) { package main import ( - "fmt" - "net/http" - _ "net/http/pprof" - "os" - "syscall" - "unsafe" + "fmt" + "net/http" + _ "net/http/pprof" + "os" + "syscall" + "unsafe" ) const ( - SYS_futex = 202 - _FUTEX_PRIVATE_FLAG = 128 - _FUTEX_WAIT = 0 - _FUTEX_WAKE = 1 - _FUTEX_WAIT_PRIVATE = _FUTEX_WAIT | _FUTEX_PRIVATE_FLAG - _FUTEX_WAKE_PRIVATE = _FUTEX_WAKE | _FUTEX_PRIVATE_FLAG + SYS_futex = 202 + _FUTEX_PRIVATE_FLAG = 128 + _FUTEX_WAIT = 0 + _FUTEX_WAKE = 1 + _FUTEX_WAIT_PRIVATE = _FUTEX_WAIT | _FUTEX_PRIVATE_FLAG + _FUTEX_WAKE_PRIVATE = _FUTEX_WAKE | _FUTEX_PRIVATE_FLAG ) func main() { - fmt.Println(os.Getpid()) - go func() { - b := make([]byte, 1<<20) - _ = b - }() - for i := 1; i < 13; i++ { - go func() { - var futexVar int = 0 - for { - // Syscall && RawSyscall, 具体差别分析可自行查看 syscall 章节 - fmt.Println(syscall.Syscall6( - SYS_futex, // trap AX 202 - uintptr(unsafe.Pointer(&futexVar)), // a1 DI 1 - uintptr(_FUTEX_WAIT), // a2 SI 0 - 0, // a3 DX - 0, //uintptr(unsafe.Pointer(&ts)), // a4 R10 - 0, // a5 R8 - 0)) - } - }() - } - http.ListenAndServe("0.0.0.0:8899", nil) + fmt.Println(os.Getpid()) + go func() { + b := make([]byte, 1<<20) + _ = b + }() + for i := 1; i < 13; i++ { + go func() { + var futexVar int = 0 + for { + // Syscall && RawSyscall, 具体差别分析可自行查看 syscall 章节 + fmt.Println(syscall.Syscall6( + SYS_futex, // trap AX 202 + uintptr(unsafe.Pointer(&futexVar)), // a1 DI 1 + uintptr(_FUTEX_WAIT), // a2 SI 0 + 0, // a3 DX + 0, //uintptr(unsafe.Pointer(&ts)), // a4 R10 + 0, // a5 R8 + 0)) + } + }() + } + http.ListenAndServe("0.0.0.0:8899", nil) } ``` ```shell # GET /debug/pprof/threadcreate?debug=1 threadcreate profile: total 18 17 @ -# 0x0 +# 0x0 1 @ 0x43b818 0x43bfa3 0x43c272 0x43857d 0x467fb1 -# 0x43b817 runtime.allocm+0x157 /usr/local/go/src/runtime/proc.go:1414 -# 0x43bfa2 runtime.newm+0x42 /usr/local/go/src/runtime/proc.go:1736 -# 0x43c271 runtime.startTemplateThread+0xb1 /usr/local/go/src/runtime/proc.go:1805 -# 0x43857c runtime.main+0x18c /usr/local/go/src/runtime/proc.go:186 +# 0x43b817 runtime.allocm+0x157 /usr/local/go/src/runtime/proc.go:1414 +# 0x43bfa2 runtime.newm+0x42 /usr/local/go/src/runtime/proc.go:1736 +# 0x43c271 runtime.startTemplateThread+0xb1 /usr/local/go/src/runtime/proc.go:1805 +# 0x43857c runtime.main+0x18c /usr/local/go/src/runtime/proc.go:186 ``` ```shell # 再结合诸如 pstack 的工具 @@ -497,6 +498,278 @@ Thread 1 (process 22299): `pprof/threadcreate` 具体实现和 `pprof/goroutine` 类似, 无非前者遍历的对象是全局 `allm`, 而后者为 `allgs`, 区别在于 `pprof/threadcreate => ThreadCreateProfile` 时不会进行进行 `STW` +## pprof/mutex +mutex 默认是关闭采样的, 通过 `runtime.SetMutexProfileFraction(int)` 来进行 `rate` 的配置进行开启或关闭 + +和上文分析过的 `mbuckets` 类似, 这边用以记录采样数据的是 `xbuckets`, `bucket` 记录了锁持有的堆栈, 次数(采样)等信息以供用户查看 +```go +//go:linkname mutexevent sync.event +func mutexevent(cycles int64, skip int) { + if cycles < 0 { + cycles = 0 + } + rate := int64(atomic.Load64(&mutexprofilerate)) + // TODO(pjw): measure impact of always calling fastrand vs using something + // like malloc.go:nextSample() + // 同样根据 rate 来进行采样, 这边用以记录 rate 的是 mutexprofilerate 变量 + if rate > 0 && int64(fastrand())%rate == 0 { + saveblockevent(cycles, skip+1, mutexProfile) + } +} +``` +```shell + --------------- + | user access | + --------------- + | + ------------------ | +| xbuckets list | copy | +| (global) | ------------------------------------- + ------------------ + | + | + | create_or_get && insert_or_update bucket into xbuckets + | + | + -------------------------------------- +| func stkbucket & typ == mutexProfile | + -------------------------------------- + | + ------------------ + | saveblockevent | // 堆栈等信息记录 + ------------------ + | + | + | /* + | * //go:linkname mutexevent sync.event + | * func mutexevent(cycles int64, skip int) { + | * if cycles < 0 { + | * cycles = 0 + | * } + | 采样 * rate := int64(atomic.Load64(&mutexprofilerate)) + | 记录 * // TODO(pjw): measure impact of always calling fastrand vs using something + | * // like malloc.go:nextSample() + | * if rate > 0 && int64(fastrand())%rate == 0 { + | * saveblockevent(cycles, skip+1, mutexProfile) + | * } + | * + | */ + | + ------------ 不采样 + | mutexevent | ----------.... + ------------ + | + | + ------------ + | semrelease1 | + ------------ + | + | + ------------------------ + | runtime_Semrelease | + ------------------------ + | + | + ------------ + | unlockSlow | + ------------ + | + | + ------------ + | Unlock | + ------------ +``` + +## pprof/block +同上, 主要来分析下 `bbuckets` +```shell + --------------- + | user access | + --------------- + | + ------------------ | +| bbuckets list | copy | +| (global) | ------------------------------------- + ------------------ + | + | + | create_or_get && insert_or_update bucket into bbuckets + | + | + -------------------------------------- +| func stkbucket & typ == blockProfile | + -------------------------------------- + | + ------------------ + | saveblockevent | // 堆栈等信息记录 + ------------------ + | + | + | /* + | * func blocksampled(cycles int64) bool { + | * rate := int64(atomic.Load64(&blockprofilerate)) + | * if rate <= 0 || (rate > cycles && int64(fastrand())%rate > cycles) { + | * return false + | 采样 * } + | 记录 * return true + | * } + | */ + | + ------------ 不采样 + | blockevent | ----------.... + ------------ + |---------------------------------------------------------------------------- + | | | + ------------ ----------------------------------------------- ------------ + | semrelease1 | | chansend / chanrecv && mysg.releasetime > 0 | | selectgo | + ------------ ----------------------------------------------- ------------ +``` +相比较 `mutex` 的采样, `block` 的埋点会额外存在于 `chan` 中, 每次 `block` 记录的是前后两个 `cpu 周期` 的差值 (cycles) +需要注意的是 `cputicks` 可能在不同系统上存在一些[问题](https://github.com/golang/go/issues/8976). 暂不放在这边讨论 + +## pprof/profile +上面分析的都属于 `runtime` 在运行的过程中自动采用保存数据后用户进行观察的, `profile` 则是用户选择指定周期内的 `CPU Profiling` + +#总结 +- `pprof` 的确会给 `runtime` 带来额外的压力, 压力的多少取决于用户使用的各个 `*_rate` 配置, 在获取 `pprof` 信息的时候需要按照实际情况酌情使用各个接口, 每个接口产生的额外压力是不一样的. +- 不同版本在是否默认开启上有不同策略, 需要自行根据各自的环境进行确认 +- `pprof` 获取到的数据仅能作为参考, 和设置的采样频率有关, 在计算例如 `heap` 情况时会进行相关的近似预估, 非实质上对 `heap` 进行扫描 + +```shell + ------------------------- +| pprof.StartCPUProfile | + ------------------------- + | + | + | + ------------------------- +| sleep(time.Duration) | + ------------------------- + | + | + | + ------------------------- +| pprof.StopCPUProfile | + ------------------------- +``` +`pprof.StartCPUProfile` 与 `pprof.StopCPUProfile` 核心为 `runtime.SetCPUProfileRate(hz int)` 控制 `cpu profile` 频率, 但是这边的频率设置和前面几个有差异, 不仅仅是设计 rate 的设置, 还涉及全局对象 `cpuprof` log buffer 的分配 +```go +var cpuprof cpuProfile +type cpuProfile struct { + lock mutex + on bool // profiling is on + log *profBuf // profile events written here + + // extra holds extra stacks accumulated in addNonGo + // corresponding to profiling signals arriving on + // non-Go-created threads. Those stacks are written + // to log the next time a normal Go thread gets the + // signal handler. + // Assuming the stacks are 2 words each (we don't get + // a full traceback from those threads), plus one word + // size for framing, 100 Hz profiling would generate + // 300 words per second. + // Hopefully a normal Go thread will get the profiling + // signal at least once every few seconds. + extra [1000]uintptr + numExtra int + lostExtra uint64 // count of frames lost because extra is full + lostAtomic uint64 // count of frames lost because of being in atomic64 on mips/arm; updated racily +} +``` +`log buffer` 的大小每次分配是固定的, 无法进行调节 + +### cpuprof.add +将 `stack trace` 信息写入 `cpuprof` 的 `log buffer` +```go +// add adds the stack trace to the profile. +// It is called from signal handlers and other limited environments +// and cannot allocate memory or acquire locks that might be +// held at the time of the signal, nor can it use substantial amounts +// of stack. +//go:nowritebarrierrec +func (p *cpuProfile) add(gp *g, stk []uintptr) { + // Simple cas-lock to coordinate with setcpuprofilerate. + for !atomic.Cas(&prof.signalLock, 0, 1) { + osyield() + } + + if prof.hz != 0 { // implies cpuprof.log != nil + if p.numExtra > 0 || p.lostExtra > 0 || p.lostAtomic > 0 { + p.addExtra() + } + hdr := [1]uint64{1} + // Note: write "knows" that the argument is &gp.labels, + // because otherwise its write barrier behavior may not + // be correct. See the long comment there before + // changing the argument here. + cpuprof.log.write(&gp.labels, nanotime(), hdr[:], stk) + } + + atomic.Store(&prof.signalLock, 0) +} +``` + +来看下调用 `cpuprof.add` 的流程 +```shell + ------------------------ +| cpu profile start | + ------------------------ + | + | + | start timer (setitimer syscall / ITIMER_PROF) + | 每个一段时间(rate)在向当前 P 所在线程发送一个 SIGPROF 信号量 -- + | | + | | + ------------------------ loop | +| sighandler |---------------------------------------------- + ------------------------ | + | | + | /* | + | * if sig == _SIGPROF { | + | * sigprof(c.sigpc(), c.sigsp(), c.siglr(), gp, _g_.m) + | * return | + | */ } | + | | + ---------------------------- | stop + | sigprof(stack strace) | | + ---------------------------- | + | | + | | + | | + ---------------------- | + | cpuprof.add | | + ---------------------- ---------------------- + | | cpu profile stop | + | ---------------------- + | + ---------------------- + | cpuprof.log buffer | + ---------------------- + | --------------------- --------------- + ----------------------------------------| cpuprof.read |----------------| user access | + --------------------- --------------- +``` +由于 `GMP` 的模型设计, 在绝大多数情况下通过这种 `timer` + `sig` + `current thread` 以及当前支持的抢占式调度, 这种记录方式是能够很好进行整个 `runtime cpu profile` 采样分析的, 但也不能排除一些极端情况是无法被覆盖的, 毕竟也只是基于当前 M 而已. + +# 总结 +#### 可用性: +runtime 自带的 pprof 已经在数据采集的准确性, 覆盖率, 压力等各方面替我们做好了一个比较均衡及全面的考虑 + +在绝大多数场景下使用起来需要考虑的性能点无非就是几个 rate 的设置 + +不同版本的默认开启是有差别的, 几个参数默认值可自行确认, 有时候你觉得没有开启 pprof 但是实际上已经开启了 + +当选择的参数合适的时候, pprof 远远没有想象中那般“重” +#### 局限性: +得到的数据只是采样(根据 rate 决定) 或预估值 + +无法 cover 所有场景, 对于一些特殊的或者极端的情况, 需要各自进行优化来选择合适的手段完善 +#### 安全性: +生产环境可用 pprof, 注意接口不能直接暴露, 毕竟存在诸如 STW 等操作, 存在潜在风险点 +#开源项目 pprof 参考 +[nsq](https://github.com/nsqio/nsq/blob/v1.2.0/nsqd/http.go#L78-L88) +[etcd](https://github.com/etcd-io/etcd/blob/release-3.4/pkg/debugutil/pprof.go#L23) 采用的是[配置式](https://github.com/etcd-io/etcd/blob/release-3.4/etcd.conf.yml.sample#L76)选择是否开启 # 参考资料 https://go-review.googlesource.com/c/go/+/299671 \ No newline at end of file From 4caa0b127bf89622861726b1a3b4b587db701f7f Mon Sep 17 00:00:00 2001 From: wziww Date: Tue, 4 May 2021 00:09:44 +0800 Subject: [PATCH 087/120] fix for #45 --- io.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/io.md b/io.md index 722a7f8..8f40f73 100644 --- a/io.md +++ b/io.md @@ -394,6 +394,8 @@ const ( ) // io.Copy 该场景下内部调用 splice syscall, 感兴趣的自行查看源码 func gosplice(src, dst net.Conn) { + defer src.Close() + defer dst.Close() go func() { io.Copy(src, dst) }() @@ -401,6 +403,8 @@ func gosplice(src, dst net.Conn) { } func normal(src, dst net.Conn) { + defer src.Close() + defer dst.Close() var bts []byte = p.Get().([]byte) var bts2 []byte = p.Get().([]byte) defer p.Put(bts) @@ -475,7 +479,6 @@ func TestMain(m *testing.M) { if err != nil { continue } - defer n.Close() remote, err := net.DialTCP("tcp4", &net.TCPAddr{ IP: net.ParseIP("0.0.0.0"), Port: 0, }, &net.TCPAddr{ @@ -500,7 +503,6 @@ func TestMain(m *testing.M) { if err != nil { continue } - defer n.Close() remote, err := net.DialTCP("tcp4", &net.TCPAddr{ IP: net.ParseIP("0.0.0.0"), Port: 0, }, &net.TCPAddr{ @@ -531,6 +533,7 @@ func BenchmarkNormalReadWrite(b *testing.B) { if err == nil && res.StatusCode == 200 { atomic.AddUint32(&success, 1) } + c.CloseIdleConnections() } b.Logf("test:%s,total: %d,rate: %.2f%%\n", b.Name(), total, float64(success*100/total)) } @@ -552,6 +555,7 @@ func BenchmarkGoSplice(b *testing.B) { if err == nil && res.StatusCode == 200 { atomic.AddUint32(&success, 1) } + c.CloseIdleConnections() } b.Logf("test:%s, total: %d, success rate: %.2f%%\n", b.Name(), total, float64(success*100/total)) } From d5e335c60ea8d3467998fc0da64874e80448d674 Mon Sep 17 00:00:00 2001 From: Xargin Date: Tue, 11 May 2021 17:37:50 +0800 Subject: [PATCH 088/120] update schedule --- scheduler.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scheduler.md b/scheduler.md index ec9ed3e..b1a8317 100644 --- a/scheduler.md +++ b/scheduler.md @@ -1,4 +1,4 @@ -> 注: 在抢占式调度的 go 版本下如果需要对 runtime 进行调试,诸如使用 gdb, lldb, [delve](https://github.com/go-delve/delve) 等工具时,需要注意 GODEBUG=asyncpreemptoff=1 环境变量,该变量会导致 runtime 是否进行抢占式调度,由于 https://github.com/golang/go/issues/36494 ,导致部分系统下该变量会被一些(如 delve)工具配置开启,从而导致超出预期的调试情况,需要读者自行关注 +> 注: 在抢占式调度的 go 版本下如果需要对 runtime 进行调试,诸如使用 gdb, lldb, [delve](https://github.com/go-delve/delve) 等工具时,需要注意 GODEBUG=asyncpreemptoff=1 环境变量,该变量会决定 runtime 是否开启抢占式调度,由于 https://github.com/golang/go/issues/36494 ,导致部分系统下该变量会被一些(如 delve)工具配置开启,从而导致超出预期的调试情况,需要读者自行关注 # 调度 ## 基本数据结构 From f648324765fe2cf07b35127ac4871212d843da49 Mon Sep 17 00:00:00 2001 From: Xargin Date: Wed, 12 May 2021 17:28:50 +0800 Subject: [PATCH 089/120] update sc --- scheduler.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scheduler.md b/scheduler.md index b1a8317..e92925c 100644 --- a/scheduler.md +++ b/scheduler.md @@ -752,6 +752,8 @@ runtime.gcenable --> main.init main.init --> main.main ``` +**主线程也是需要和 p 绑定来运行的**,绑定过程在 procresize 中。 + ### sysmon 线程 sysmon 是在 `runtime.main` 中启动的,不过需要注意的是 sysmon 并不是在 m0 上执行的。因为: From be18673e0fd705c02fe3c97475c11b1a64d62c7c Mon Sep 17 00:00:00 2001 From: Xargin Date: Wed, 12 May 2021 17:30:06 +0800 Subject: [PATCH 090/120] update sc --- scheduler.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scheduler.md b/scheduler.md index e92925c..c28636f 100644 --- a/scheduler.md +++ b/scheduler.md @@ -752,7 +752,7 @@ runtime.gcenable --> main.init main.init --> main.main ``` -**主线程也是需要和 p 绑定来运行的**,绑定过程在 procresize 中。 +**主线程也是需要和 p 绑定来运行的**,绑定过程在 procresize -> acquirep 中。 ### sysmon 线程 From 9c78a7d40cbc3c0949d5e431aebd50aeb43d66bc Mon Sep 17 00:00:00 2001 From: wziww Date: Tue, 25 May 2021 14:27:15 +0800 Subject: [PATCH 091/120] update preempt sc --- 1.14/signal_based_preemption.md | 71 +++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/1.14/signal_based_preemption.md b/1.14/signal_based_preemption.md index e184c05..a7bbf5a 100644 --- a/1.14/signal_based_preemption.md +++ b/1.14/signal_based_preemption.md @@ -380,7 +380,78 @@ asyncPreempt 是汇编实现的,分为三个部分: └─────────────────────────────────────────────────────────────────────┘ └──────────────────────────────────────────────────────────────────┘ ``` +#### 你所想的上下文切换, 不一定等于你想要的上下文切换 + +这边需要注意的是, 抢占式调度中, 是`asyncPremmpt`这个操作保存了所有寄存器的值, 而`go`内部的诸如`systemstack`、`mcall`以及包括`runtime.Gosched()`的使用等, 是不会进行所有寄存器值的保留的, 所以在一些面对一些不常见的使用场景的时候需要注意, 避免寄存器发生非预期的篡改 + +例: +```go +// test.go +package main + +import ( + "fmt" + "os" + "os/signal" + "runtime" + "syscall" + "time" +) + +//go:noescape +func setget(v int64) int64 + +//go:noescape +func set(v int64) + +func gosched() { + runtime.Gosched() +} +func main() { + _ = gosched // avoid warning + c := make(chan os.Signal, 1) + signal.Notify(c, syscall.SIGURG) + go func() { + // for debug + for { + <-c + fmt.Println("SIGURG") + } + }() + go func() { + for { + if v := setget(1); v != 1 { + fmt.Println("not equal:", v) + os.Exit(1) + } else { + fmt.Println("equal") + } + } + }() + go func() { + for { + set(2) + } + }() + time.Sleep(time.Hour) +} +``` +```assembly +//test.s +#include "textflag.h" + +TEXT ·setget(SB),NOSPLIT,$0-16 + MOVL v+0(FP), R13 + //CALL ·gosched(SB) // runtime.Gosched() 没有进行所有寄存器现场的保留 + MOVQ R13, ret+8(FP) + RET +TEXT ·set(SB),NOSPLIT,$0-8 + MOVL v+0(FP), R13 + RET +``` +以上这个简短的程序实现的是内部正常调度(非抢占式)下, 调度前后寄存器异常的问题。如果想要在应用内很好控制自身协程调度的, 那么就要小心这类问题。 ## asyncPreempt2 +接下去进行的就是常规内部上下文切换`mcall` ```go func asyncPreempt2() { From fb73b998fa650018de09589a2c70a4c1d2561ea8 Mon Sep 17 00:00:00 2001 From: wziww Date: Tue, 25 May 2021 14:28:55 +0800 Subject: [PATCH 092/120] =?UTF-8?q?=E4=B9=B1=E7=A0=81=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 1.14/signal_based_preemption.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/1.14/signal_based_preemption.md b/1.14/signal_based_preemption.md index a7bbf5a..6118892 100644 --- a/1.14/signal_based_preemption.md +++ b/1.14/signal_based_preemption.md @@ -124,7 +124,7 @@ sigaction 的三个参数: ### sigaltstack -修改信号执行时所用的函数栈。 +修改信号执行时所用的函数栈。 ## 简单的信号处理函数 From 68bcc5c3ae62cda130ce11268ab118beb4d5a7da Mon Sep 17 00:00:00 2001 From: wziww Date: Tue, 25 May 2021 14:32:17 +0800 Subject: [PATCH 093/120] update --- 1.14/signal_based_preemption.md | 1 + 1 file changed, 1 insertion(+) diff --git a/1.14/signal_based_preemption.md b/1.14/signal_based_preemption.md index 6118892..c43d906 100644 --- a/1.14/signal_based_preemption.md +++ b/1.14/signal_based_preemption.md @@ -448,6 +448,7 @@ TEXT ·setget(SB),NOSPLIT,$0-16 TEXT ·set(SB),NOSPLIT,$0-8 MOVL v+0(FP), R13 RET + ``` 以上这个简短的程序实现的是内部正常调度(非抢占式)下, 调度前后寄存器异常的问题。如果想要在应用内很好控制自身协程调度的, 那么就要小心这类问题。 ## asyncPreempt2 From 293b22faae035286b360b91872adcc9b0be16a2d Mon Sep 17 00:00:00 2001 From: wziww Date: Tue, 25 May 2021 14:48:13 +0800 Subject: [PATCH 094/120] update --- 1.14/signal_based_preemption.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/1.14/signal_based_preemption.md b/1.14/signal_based_preemption.md index c43d906..a573f70 100644 --- a/1.14/signal_based_preemption.md +++ b/1.14/signal_based_preemption.md @@ -441,12 +441,12 @@ func main() { #include "textflag.h" TEXT ·setget(SB),NOSPLIT,$0-16 - MOVL v+0(FP), R13 + MOVQ v+0(FP), R13 //CALL ·gosched(SB) // runtime.Gosched() 没有进行所有寄存器现场的保留 MOVQ R13, ret+8(FP) RET TEXT ·set(SB),NOSPLIT,$0-8 - MOVL v+0(FP), R13 + MOVQ v+0(FP), R13 RET ``` From d5b51b37e4e08accd48c7cf40d92abb32ef5a300 Mon Sep 17 00:00:00 2001 From: Xargin Date: Sat, 12 Jun 2021 20:10:37 +0800 Subject: [PATCH 095/120] add gc write barrier file --- gc_write_barrier.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 gc_write_barrier.md diff --git a/gc_write_barrier.md b/gc_write_barrier.md new file mode 100644 index 0000000..683e9cc --- /dev/null +++ b/gc_write_barrier.md @@ -0,0 +1,4 @@ +# GC write barrier 详解 + +TODO + From 2fbca7d174500dc213f8df98d6af653ffd45525c Mon Sep 17 00:00:00 2001 From: Xargin Date: Mon, 14 Jun 2021 22:21:40 +0800 Subject: [PATCH 096/120] update memory barrier --- memory_barrier.md | 52 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/memory_barrier.md b/memory_barrier.md index 94383c0..98155a1 100644 --- a/memory_barrier.md +++ b/memory_barrier.md @@ -126,7 +126,7 @@ mesi 协议解决了多核环境下,内存多层级带来的问题。使得 ca ## CPU 导致乱序 -使用 litmus 进行形式化验证: +使用 litmus 进行验证: ``` cat sb.litmus @@ -181,6 +181,56 @@ Time SB 0.11 在两个核心上运行汇编指令,意料之外的情况 100w 次中出现了 96 次。虽然很少,但确实是客观存在的情况。 +有文档提到,x86 体系的内存序本身比较严格,除了 store-load 以外不存在其它类型的重排,也可以用下列脚本验证: + +``` +X86 RW +{ x=0; y=0; } + P0 | P1 ; + MOV EAX,[y] | MOV EAX,[x] ; + MOV [x],$1 | MOV [y],$1 ; +locations [x;y;] +exists (0:EAX=1 /\ 1:EAX=1) +``` + +``` +%%%%%%%%%%%%%%%%%%%%%%%%% +% Results for sb.litmus % +%%%%%%%%%%%%%%%%%%%%%%%%% +X86 OOO + +{x=0; y=0;} + + P0 | P1 ; + MOV EAX,[y] | MOV EAX,[x] ; + MOV [x],$1 | MOV [y],$1 ; + +locations [x; y;] +exists (0:EAX=1 /\ 1:EAX=1) +Generated assembler + ##START _litmus_P0 + movl -4(%rsi,%rcx,4), %eax + movl $1, -4(%rbx,%rcx,4) + ##START _litmus_P1 + movl -4(%rbx,%rcx,4), %eax + movl $1, -4(%rsi,%rcx,4) + +Test OOO Allowed +Histogram (2 states) +500000:>0:EAX=1; 1:EAX=0; x=1; y=1; +500000:>0:EAX=0; 1:EAX=1; x=1; y=1; +No + +Witnesses +Positive: 0, Negative: 1000000 +Condition exists (0:EAX=1 /\ 1:EAX=1) is NOT validated +Hash=7cdd62e8647b817c1615cf8eb9d2117b +Observation OOO Never 0 1000000 +Time OOO 0.14 +``` + +无论运行多少次,Positive 应该都是 0。 + ## barrier 从功能上来讲,barrier 有四种: From 7c8d771f3278d150435ef77d247855724301e44f Mon Sep 17 00:00:00 2001 From: Xargin Date: Fri, 18 Jun 2021 01:16:29 +0800 Subject: [PATCH 097/120] add qrcode --- assembly.md | 8 +------- atomic.md | 4 +++- bootstrap.md | 4 +++- channel.md | 3 +++ context.md | 2 ++ defer.md | 3 +++ futex.md | 3 +++ gc.md | 2 ++ gc_write_barrier.md | 1 + generics.md | 2 ++ goroutine.md | 4 +++- interface.md | 2 ++ io.md | 2 ++ lockfree.md | 4 +++- map.md | 2 ++ memory.md | 3 +++ memory_barrier.md | 2 ++ netpoll.md | 2 ++ panic.md | 4 +++- pprof.md | 5 ++++- scheduler.md | 1 + select.md | 2 ++ semaphore.md | 4 +++- signal.md | 3 --- slice.md | 2 ++ sync.md | 2 ++ syscall.md | 4 +++- timer.md | 2 ++ 28 files changed, 64 insertions(+), 18 deletions(-) delete mode 100644 signal.md diff --git a/assembly.md b/assembly.md index a246c16..f084e56 100644 --- a/assembly.md +++ b/assembly.md @@ -999,11 +999,5 @@ go compile -S: 参考资料[4]需要特别注意,在该 slide 中给出的 callee stack frame 中把 caller 的 return address 也包含进去了,个人认为不是很合适。 + - - diff --git a/atomic.md b/atomic.md index 59b12b4..ed3e9c4 100644 --- a/atomic.md +++ b/atomic.md @@ -359,4 +359,6 @@ TEXT runtime∕internal∕atomic·Store(SB), NOSPLIT, $0-12 MOVL val+8(FP), AX XCHGL AX, 0(BX) // 交换指令 RET -``` \ No newline at end of file +``` + + diff --git a/bootstrap.md b/bootstrap.md index f058f98..20337a6 100644 --- a/bootstrap.md +++ b/bootstrap.md @@ -518,4 +518,6 @@ func main() { \ No newline at end of file +--> + + diff --git a/channel.md b/channel.md index dc4af9c..5bad5b1 100644 --- a/channel.md +++ b/channel.md @@ -705,3 +705,6 @@ func closechan(c *hchan) { Q: 如果有多个channel同时唤醒同一个goroutine,这个并发控制是怎么做的? Q: 为什么向 channel 发数据的时候,会直接把数据从一个 goroutine 的栈拷贝到另一个 goroutine 的栈? + + + diff --git a/context.md b/context.md index 4885a38..e767fd9 100644 --- a/context.md +++ b/context.md @@ -566,3 +566,5 @@ func main() { # 总结 ctx 的结构显然是根据代码的执行模型来设计的,虽然设计得比较巧妙,但因为将取消和上下文携带功能混合在一起,在一些情况下还是会给我们埋些比较隐蔽的坑。使用时需要多多注意。 + + diff --git a/defer.md b/defer.md index ae0d8bf..7c6dd02 100644 --- a/defer.md +++ b/defer.md @@ -160,3 +160,6 @@ A: deferproc 和 deferreturn 是成对出现的,对于编译器的实现来说 https://ieevee.com/tech/2017/11/23/go-panic.html + + + diff --git a/futex.md b/futex.md index 812d091..9d51161 100644 --- a/futex.md +++ b/futex.md @@ -235,3 +235,6 @@ http://blog.sina.com.cn/s/blog_e59371cc0102v29b.html https://www.jianshu.com/p/570a61f08e27 https://eli.thegreenplace.net/2018/basics-of-futexes/ + + + diff --git a/gc.md b/gc.md index ff0ec21..b7973ed 100644 --- a/gc.md +++ b/gc.md @@ -742,3 +742,5 @@ gc时间,stw时间和响应延迟之间是什么关系 宏观来看gc划分为多少个阶段 + + diff --git a/gc_write_barrier.md b/gc_write_barrier.md index 683e9cc..52791c0 100644 --- a/gc_write_barrier.md +++ b/gc_write_barrier.md @@ -2,3 +2,4 @@ TODO + diff --git a/generics.md b/generics.md index caf2ba5..4a36142 100644 --- a/generics.md +++ b/generics.md @@ -40,3 +40,5 @@ cat source.go | genny gen "Something=string" 没有官方的泛型支持,社区怎么搞都是邪道。2021 年 1 月,官方的方案已经基本上成型,并释出了 [draft design](https://go.googlesource.com/proposal/+/refs/heads/master/design/go2draft-type-parameters.md)。 + + diff --git a/goroutine.md b/goroutine.md index 0de6ace..38b020b 100644 --- a/goroutine.md +++ b/goroutine.md @@ -7,4 +7,6 @@ > Written with [StackEdit](https://stackedit.io/). \ No newline at end of file +--> + + diff --git a/interface.md b/interface.md index 703507d..a365ff4 100644 --- a/interface.md +++ b/interface.md @@ -543,3 +543,5 @@ go.itab.*os.File,io.Writer SRODATA dupok size=32 // 下面就是正常流程了 ``` + + diff --git a/io.md b/io.md index 4e5e7c3..c15b884 100644 --- a/io.md +++ b/io.md @@ -722,3 +722,5 @@ func Splice(dst, src *FD, remain int64) (written int64, handled bool, sc string, - https://go-review.googlesource.com/c/go/+/271537/ - https://zhuanlan.zhihu.com/p/308054212 + + diff --git a/lockfree.md b/lockfree.md index f9c19d1..261e9cb 100644 --- a/lockfree.md +++ b/lockfree.md @@ -1,4 +1,6 @@ # lock free programming in Go # 参考资料 -https://docs.google.com/presentation/d/1wuNNW-g6v8qizIc_IxAGZTj-49TODKF0TYddTA1VDUo/mobilepresent?slide=id.p \ No newline at end of file +https://docs.google.com/presentation/d/1wuNNW-g6v8qizIc_IxAGZTj-49TODKF0TYddTA1VDUo/mobilepresent?slide=id.p + + diff --git a/map.md b/map.md index 123cfd8..351146d 100644 --- a/map.md +++ b/map.md @@ -1267,3 +1267,5 @@ func (h *hmap) incrnoverflow() { } } ``` + + diff --git a/memory.md b/memory.md index d71141a..b937fcf 100644 --- a/memory.md +++ b/memory.md @@ -1396,3 +1396,6 @@ func (p *notInHeap) add(bytes uintptr) *notInHeap { ### 堆外内存用法 嗯,堆外内存只是 runtime 自己玩的东西,用户态是使用不了的,属于 runtime 专用的 directive。 + + + diff --git a/memory_barrier.md b/memory_barrier.md index 98155a1..cc1a7ce 100644 --- a/memory_barrier.md +++ b/memory_barrier.md @@ -569,3 +569,5 @@ https://stackoverflow.com/questions/29880015/lock-prefix-vs-mesi-protocol https://github.com/torvalds/linux/blob/master/Documentation/memory-barriers.txt http://www.overbyte.com.au/misc/Lesson3/CacheFun.html + + diff --git a/netpoll.md b/netpoll.md index 942adad..7ab59f9 100644 --- a/netpoll.md +++ b/netpoll.md @@ -1355,3 +1355,5 @@ func poll_runtime_pollUnblock(pd *pollDesc) { } } ``` + + diff --git a/panic.md b/panic.md index 7765ec1..e34b3ad 100644 --- a/panic.md +++ b/panic.md @@ -295,4 +295,6 @@ func main() { defer panic(2) panic(1) } -``` \ No newline at end of file +``` + + diff --git a/pprof.md b/pprof.md index 759fc54..f825a1a 100644 --- a/pprof.md +++ b/pprof.md @@ -771,5 +771,8 @@ runtime 自带的 pprof 已经在数据采集的准确性, 覆盖率, 压力等 #开源项目 pprof 参考 [nsq](https://github.com/nsqio/nsq/blob/v1.2.0/nsqd/http.go#L78-L88) [etcd](https://github.com/etcd-io/etcd/blob/release-3.4/pkg/debugutil/pprof.go#L23) 采用的是[配置式](https://github.com/etcd-io/etcd/blob/release-3.4/etcd.conf.yml.sample#L76)选择是否开启 + # 参考资料 -https://go-review.googlesource.com/c/go/+/299671 \ No newline at end of file +https://go-review.googlesource.com/c/go/+/299671 + + diff --git a/scheduler.md b/scheduler.md index c28636f..7d4557b 100644 --- a/scheduler.md +++ b/scheduler.md @@ -2348,3 +2348,4 @@ gcMarkDone --> forEachP 当然,这里 entersyscall 和 entersyscallblock 比较特殊,虽然这俩函数的实现中有设置抢占标记,但实际上这两段逻辑是不会被走到的。因为 syscall 执行时是在 m 的 g0 栈上,如果在执行时被抢占,那么会直接 throw,而无法恢复。 + diff --git a/select.md b/select.md index 24d32d0..ed93053 100644 --- a/select.md +++ b/select.md @@ -826,3 +826,5 @@ sclose: Q: 如果select多个channel,有一个channel触发了,其他channel的waitlist需要不要主动去除?还是一直在那等着? A: waitlist 的出列是由 `func (q *waitq) dequeue() *sudog` 函数控制的,每个 sudog 携带了一个 `selectDone` 标志位,通过 `cas` 操作在每次 `dequeue` 的时候「惰性」去除队列中无效的元素 + + diff --git a/semaphore.md b/semaphore.md index 4fbeddd..ecfac1d 100644 --- a/semaphore.md +++ b/semaphore.md @@ -735,4 +735,6 @@ func notifyListCheck(sz uintptr) { func sync_nanotime() int64 { return nanotime() } -``` \ No newline at end of file +``` + + diff --git a/signal.md b/signal.md deleted file mode 100644 index 09c2668..0000000 --- a/signal.md +++ /dev/null @@ -1,3 +0,0 @@ -# Signal - -Go 1.12 的抢占使用 signal 来实现,所以我们来分析一下 Go runtime 中是怎么处理这些 signal 的。 diff --git a/slice.md b/slice.md index 396491e..344c5ff 100644 --- a/slice.md +++ b/slice.md @@ -338,3 +338,5 @@ func main() { sh.Data = (uintptr)(unsafe.Pointer(&b[1])) sh.Len = bh.Len - 1 ``` + + diff --git a/sync.md b/sync.md index 501fa84..df67417 100644 --- a/sync.md +++ b/sync.md @@ -1027,3 +1027,5 @@ Q: 既然被选中了唤醒的那个G,说明这个G就是一定要退出lock A: + + diff --git a/syscall.md b/syscall.md index a60250c..6c03a92 100644 --- a/syscall.md +++ b/syscall.md @@ -768,4 +768,6 @@ func entersyscall_gcwait() { 3. https://mzh.io/golang-arm64-vdso -4. https://blog.csdn.net/luozhaotian/article/details/79609077 \ No newline at end of file +4. https://blog.csdn.net/luozhaotian/article/details/79609077 + + diff --git a/timer.md b/timer.md index 45d5cac..33c0139 100644 --- a/timer.md +++ b/timer.md @@ -779,3 +779,5 @@ func (t *Timer) Reset(d Duration) bool { ## 最后 本篇内容主要是讲 Go 的定时器实现,工业界的定时器实现并不只有一种。如果你还想知道其它系统,比如 nginx 里是怎么实现定时器的,可以参考[这一篇](https://www.jianshu.com/p/427dfe8ad3c0)。 + + From e396b7cd31269d8434dbf1314d5abb9c9ce89f04 Mon Sep 17 00:00:00 2001 From: Lee ChiChuang Date: Tue, 22 Jun 2021 16:05:08 +0800 Subject: [PATCH 098/120] Update semaphore.md --- semaphore.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/semaphore.md b/semaphore.md index ecfac1d..51d4640 100644 --- a/semaphore.md +++ b/semaphore.md @@ -236,7 +236,7 @@ func semacquire1(addr *uint32, lifo bool, profile semaProfileFlags) { // 高成本的情况: // 增加 waiter count 的值 - // 再尝试调用一次 cansemacquire,成本了就直接返回 + // 再尝试调用一次 cansemacquire,成功了就直接返回 // 没成功就把自己作为一个 waiter 入队 // sleep // (之后 waiter 的 descriptor 被 signaler 用 dequeue 踢出) From 7b5a219b1d05a5a9c925491e97d7c4d26c1627a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B4=E4=BA=B2=E5=BA=93=E9=87=8C?= <36129334+wuqinqiang@users.noreply.github.com> Date: Thu, 26 Aug 2021 11:46:13 +0800 Subject: [PATCH 099/120] Update slice.md change println to Println --- slice.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slice.md b/slice.md index 344c5ff..5445d2a 100644 --- a/slice.md +++ b/slice.md @@ -265,7 +265,7 @@ func main() { func doSomeHappyThings(arr []int) { arr = append(arr, 1) - fmt.println(arr, "after append") + fmt.Println(arr, "after append") } ``` From 56e5fedc03d4bd18289eacd5239ae222b63018df Mon Sep 17 00:00:00 2001 From: wziww Date: Tue, 31 Aug 2021 18:19:20 +0800 Subject: [PATCH 100/120] =?UTF-8?q?go-compiler=20=E8=AF=8D=E6=B3=95?= =?UTF-8?q?=E5=88=86=E6=9E=90=E9=83=A8=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- compilers.md | 244 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 compilers.md diff --git a/compilers.md b/compilers.md new file mode 100644 index 0000000..93f4eb6 --- /dev/null +++ b/compilers.md @@ -0,0 +1,244 @@ +# 编译原理 +> 本文主要介绍编译的几个主要过程及周边工具的使用, 对于工具内部具体实现的算法不做分析, 感兴趣的可自行搜索 + +## 词法分析 + +```mermaid +sequenceDiagram + Source code->>Token stream: Lexical analysis + Note left of Source code: 1 + (2 - 3) * 4 / 5 + Note left of Source code: SELECT * FROM TABLE1 LIMIT 1; + Note left of Source code: {"key1": 1, "key2": "val", "key3": {}} + #------ + Note right of Token stream: 1
+
(
2
-
3
)
*
4
/
5 + Note right of Token stream: SELECT
*
FROM
TABLE1
LIMIT
1
; + Note right of Token stream: {
"key1"
:
1
,
"key2"
:
"val"
,
"key3"
:
{
,
}
,
}
+``` + +第一步将源代码处理成为`token stream`, 这边的源代码可以是一段简单的`go`代码, `DML`, `DSL`, 甚至是`JSON`格式的文件或者其他文本内容等等, `Lexical analysis`的目的就是按照某个定义规则将文本处理成为一连串的`token stream` +> 标记 / Token: 指处理好后的一个字串, 是构成源代码的最小单位, 比如我们可以归类 golang 中的关键字, 例如 var、const、import 等等, 或者一个字符串变量 "str" 或者操作符 :=、>=、== 等等,只要是符合我们定义的语法规则处理后出现的字串, 都可以称为一个 token + +如上图, 左边框内的三条源代码案例, 经过词法分析后, 可能会(具体看自己对`token`的定义处理规则)输出右边的三块`token stream`(每一行代表一个`token`) + +### lex / flex +lex / flex 是常用的词法分析器,支持正则表示某类 token + +flex 文件完整格式: +```c +%{ +Declarations +%} +Definitions +%% +Rules +%% +User subroutines +``` + +例: +test.l +```c + +/* Declarations */ +%{ +void yyerror(const char *msg); +%} + + +/* Definitions */ +WHITESPACE ([ \t\r\a]+) +OPERATOR ([+*-/%=,;!<>(){}]) +INTEGER ([0-9]+) + + +/* Rules */ +%% + +{WHITESPACE} { /* void */ } + +{OPERATOR} { printf("%s\n", yytext); } + +{INTEGER} { printf("%d\n", atoi(yytext)); } + +\n { /* void */ } + +. { printf("analysis error: unknow [%s]\n", yytext); exit(1); } + +%% + +/* User subroutines */ +int main(int argc, char* argv[]) { + FILE *fp = NULL; + if (argc == 2) { + fp = fopen(argv[1], "r"); + if (fp) { + yyin = fp; + } + } + yylex(); + if (fp) { + fclose(fp); + } + return 0; +} + +int yywrap(void) { + return 1; +} + +void yyerror(const char *msg) { + fprintf(stderr, "Error :\n\t%s\n", msg); + exit(-1); +} +``` + + +以上小段词法分析代码定义了三种`token`:`WHITESPACE`, `OPERATOR`, `INTEGER`, 分别用正则定义了他们的规则, 而后在 `Rules` 规则阶段分别对这三种 `token` 进行了各自的处理 +```shell +# 编译 +flex -o test.c test.l +gcc -std=c89 -o flextest test.c +./test.c test.txt +``` +而后用根据我们定义的规则生成的词法分析器`flextest`来处理一个简单的案例 + +```shell +cat test.txt +1 + (2 - 3) * 4 / 5 sss + +./flextest ./test.txt +1 ++ +( +2 +- +3 +) +* +4 +/ +5 +analysis error: unknow [s] +``` +根据输出的`token stream`可以看到, 能通过`token`规则处理的字串会完成输出一个成功处理的`token`, 规则之外的则处理失败 + +经过以上的小案例, 那么如果让我们自己来做一个`golang`的词法分析 `token` 的定义, 难度就不会特别大了 + +这边可以来简单看下`golang`编译器源码内的`token`定义 + +```go +// src/go/token/token.go +var tokens = [...]string{ + ILLEGAL: "ILLEGAL", + + EOF: "EOF", + COMMENT: "COMMENT", + + IDENT: "IDENT", + INT: "INT", + FLOAT: "FLOAT", + IMAG: "IMAG", + CHAR: "CHAR", + STRING: "STRING", + + ADD: "+", + SUB: "-", + MUL: "*", + QUO: "/", + REM: "%", + + AND: "&", + OR: "|", + XOR: "^", + SHL: "<<", + SHR: ">>", + AND_NOT: "&^", + + ADD_ASSIGN: "+=", + SUB_ASSIGN: "-=", + MUL_ASSIGN: "*=", + QUO_ASSIGN: "/=", + REM_ASSIGN: "%=", + + AND_ASSIGN: "&=", + OR_ASSIGN: "|=", + XOR_ASSIGN: "^=", + SHL_ASSIGN: "<<=", + SHR_ASSIGN: ">>=", + AND_NOT_ASSIGN: "&^=", + + LAND: "&&", + LOR: "||", + ARROW: "<-", + INC: "++", + DEC: "--", + + EQL: "==", + LSS: "<", + GTR: ">", + ASSIGN: "=", + NOT: "!", + + NEQ: "!=", + LEQ: "<=", + GEQ: ">=", + DEFINE: ":=", + ELLIPSIS: "...", + + LPAREN: "(", + LBRACK: "[", + LBRACE: "{", + COMMA: ",", + PERIOD: ".", + + RPAREN: ")", + RBRACK: "]", + RBRACE: "}", + SEMICOLON: ";", + COLON: ":", + + BREAK: "break", + CASE: "case", + CHAN: "chan", + CONST: "const", + CONTINUE: "continue", + + DEFAULT: "default", + DEFER: "defer", + ELSE: "else", + FALLTHROUGH: "fallthrough", + FOR: "for", + + FUNC: "func", + GO: "go", + GOTO: "goto", + IF: "if", + IMPORT: "import", + + INTERFACE: "interface", + MAP: "map", + PACKAGE: "package", + RANGE: "range", + RETURN: "return", + + SELECT: "select", + STRUCT: "struct", + SWITCH: "switch", + TYPE: "type", + VAR: "var", +} +``` + +## 语法分析 +根据第一步[词法分析](#词法分析)我们目前已经获取到了自源代码处理好之后的一个`token stream`, 在语法分析阶段主要负责的就是把这一串「看似毫无规则」的标记流进行语法结构上的处理 +例如 +1.判断某个赋值操作是否可以执行, 赋值号两边的变量及数据类型是否匹配 +2.运算规则是否符合语法规则 +3.语句优先级 +…… + 在这个阶段可以直接翻译成目标代码, 或者生成诸如语法树之类的数据结构以便后续语义分析,优化等阶段利用。 +> 上下文无关文法: 文法中所有的产生式左边只有一个非终结符 +> https://www.zhihu.com/question/21833944 + +### bison +### goyacc \ No newline at end of file From e51662b3133cfb526f496bb90369ae0e7c60e45c Mon Sep 17 00:00:00 2001 From: wziww Date: Tue, 31 Aug 2021 18:25:40 +0800 Subject: [PATCH 101/120] update --- compilers.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/compilers.md b/compilers.md index 93f4eb6..80ce19b 100644 --- a/compilers.md +++ b/compilers.md @@ -231,12 +231,15 @@ var tokens = [...]string{ ## 语法分析 根据第一步[词法分析](#词法分析)我们目前已经获取到了自源代码处理好之后的一个`token stream`, 在语法分析阶段主要负责的就是把这一串「看似毫无规则」的标记流进行语法结构上的处理 + 例如 -1.判断某个赋值操作是否可以执行, 赋值号两边的变量及数据类型是否匹配 -2.运算规则是否符合语法规则 -3.语句优先级 +1. 判断某个赋值操作是否可以执行, 赋值号两边的变量及数据类型是否匹配 +2. 运算规则是否符合语法规则 +3. 语句优先级 …… - 在这个阶段可以直接翻译成目标代码, 或者生成诸如语法树之类的数据结构以便后续语义分析,优化等阶段利用。 + +在这个阶段可以直接翻译成目标代码, 或者生成诸如语法树之类的数据结构以便后续语义分析,优化等阶段利用。 + > 上下文无关文法: 文法中所有的产生式左边只有一个非终结符 > https://www.zhihu.com/question/21833944 From 1636cfeb000086dd8c20703c9c7257c8537fa68a Mon Sep 17 00:00:00 2001 From: wziww Date: Tue, 31 Aug 2021 18:27:10 +0800 Subject: [PATCH 102/120] update --- compilers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compilers.md b/compilers.md index 80ce19b..d79f81b 100644 --- a/compilers.md +++ b/compilers.md @@ -236,7 +236,7 @@ var tokens = [...]string{ 1. 判断某个赋值操作是否可以执行, 赋值号两边的变量及数据类型是否匹配 2. 运算规则是否符合语法规则 3. 语句优先级 -…… +4. …… 在这个阶段可以直接翻译成目标代码, 或者生成诸如语法树之类的数据结构以便后续语义分析,优化等阶段利用。 From 297712d6ff3d8c4b85450771e39789f4eadfc7ae Mon Sep 17 00:00:00 2001 From: Xargin Date: Sat, 4 Sep 2021 17:20:12 +0800 Subject: [PATCH 103/120] =?UTF-8?q?=E4=BC=AA=20SP=20=E7=9A=84=E8=AF=B4?= =?UTF-8?q?=E6=B3=95=E5=9C=A8=E5=AE=98=E6=96=B9=20asm=20=E6=96=87=E6=A1=A3?= =?UTF-8?q?=E4=B8=AD=E5=B7=B2=E7=BB=8F=E5=BE=97=E5=88=B0=E4=BA=86=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3=20=E8=BF=99=E9=87=8C=E8=BF=9B=E8=A1=8C=E6=9B=B4?= =?UTF-8?q?=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assembly.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assembly.md b/assembly.md index f084e56..ecbf59d 100644 --- a/assembly.md +++ b/assembly.md @@ -135,7 +135,7 @@ Go 的汇编还引入了 4 个伪寄存器,援引官方文档的描述: >- `FP`: Frame pointer: arguments and locals. >- `PC`: Program counter: jumps and branches. >- `SB`: Static base pointer: global symbols. ->- `SP`: Stack pointer: top of stack. +>- `SP`: Stack pointer: the highest address within the local stack frame. 官方的描述稍微有一些问题,我们对这些说明进行一点扩充: @@ -152,7 +152,7 @@ Go 的汇编还引入了 4 个伪寄存器,援引官方文档的描述: 4. 在 go tool objdump/go tool compile -S 输出的代码中,是没有伪 SP 和 FP 寄存器的,我们上面说的区分伪 SP 和硬件 SP 寄存器的方法,对于上述两个命令的输出结果是没法使用的。在编译和反汇编的结果中,只有真实的 SP 寄存器。 5. FP 和 Go 的官方源代码里的 framepointer 不是一回事,源代码里的 framepointer 指的是 caller BP 寄存器的值,在这里和 caller 的伪 SP 是值是相等的。 -以上说明看不懂也没关系,在熟悉了函数的栈结构之后再反复回来查看应该就可以明白了。个人意见,这些是 Go 官方挖的坑。。 +以上说明看不懂也没关系,在熟悉了函数的栈结构之后再反复回来查看应该就可以明白了。 ## 变量声明 From f4c46b33b21e9d4ef06c5f9833b419682c16f502 Mon Sep 17 00:00:00 2001 From: wziww Date: Wed, 8 Sep 2021 11:26:05 +0800 Subject: [PATCH 104/120] =?UTF-8?q?=E8=AF=AD=E6=B3=95=E5=88=86=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- compilers.md | 775 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 774 insertions(+), 1 deletion(-) diff --git a/compilers.md b/compilers.md index d79f81b..51327c5 100644 --- a/compilers.md +++ b/compilers.md @@ -228,7 +228,83 @@ var tokens = [...]string{ VAR: "var", } ``` +附上一段 `go` 内置实现的词法分析 +```go +package main + +import ( + "fmt" + "go/scanner" + "go/token" +) + +func main() { + // src is the input that we want to tokenize. + src := ` + package main + + func main() { + var num1, num2 int + num1 += num2 + _ = num1 + num1 += "str" + return + } + ` + + // Initialize the scanner. + var s scanner.Scanner + fset := token.NewFileSet() // positions are relative to fset + file := fset.AddFile("", fset.Base(), len(src)) // register input "file" + s.Init(file, []byte(src), nil /* no error handler */, scanner.ScanComments) + + // Repeated calls to Scan yield the token sequence found in the input. + fmt.Printf("%s\t%s\t%s\n", "pos", "token", "literal") + for { + pos, tok, lit := s.Scan() + if tok == token.EOF { + break + } + fmt.Printf("%s\t%s\t%q\n", fset.Position(pos), tok, lit) + } +} +``` +```shell +#output +pos token literal +2:2 package "package" +2:10 IDENT "main" +2:14 ; "\n" +4:2 func "func" +4:7 IDENT "main" +4:11 ( "" +4:12 ) "" +4:14 { "" +5:3 var "var" +5:7 IDENT "num1" +5:11 , "" +5:13 IDENT "num2" +5:18 IDENT "int" +5:21 ; "\n" +6:3 IDENT "num1" +6:8 += "" +6:11 IDENT "num2" +6:15 ; "\n" +7:3 IDENT "_" +7:5 = "" +7:7 IDENT "num1" +7:11 ; "\n" +8:3 IDENT "num1" +8:8 += "" +8:11 STRING "\"str\"" +8:16 ; "\n" +9:3 return "return" +9:9 ; "\n" +10:2 } "" +10:3 ; "\n" +``` +注意, 这边示例代码有意使用了错误的语法, 目的是为了让大家知道`token`提取的词法分析过程中, 是没有必要同步进行语义的分析的, 输出的结果也可以和之前的`token`列表自行对照一下 ## 语法分析 根据第一步[词法分析](#词法分析)我们目前已经获取到了自源代码处理好之后的一个`token stream`, 在语法分析阶段主要负责的就是把这一串「看似毫无规则」的标记流进行语法结构上的处理 @@ -243,5 +319,702 @@ var tokens = [...]string{ > 上下文无关文法: 文法中所有的产生式左边只有一个非终结符 > https://www.zhihu.com/question/21833944 +### token stream 处理过程第一步 +对于`token stream`首先我们得处理的是初步生成语法树而后交由下面的步骤进行处理, 然而这里并不是随意生成一颗语法树的, 它得以某种规则进行初步的约束, 可以试想, 如果生成的语法树有多种可能, 每次生成的结果都不一致, 那么对于这类的语法树进行后续的分析则没有任何意义 + +本案例中我们要实现的是一个简单的`SQL`解析器, 那么接下来看看如何通过`token stream`初步生成我们所需要的语法树 + +回头看下上下文无关语法的简单介绍, 所有的`产生式`右边都是由左侧唯一的`非终结符`产生的, 就例如我们常见的一条 `SQL` +`SELECT * FROM TABLE1;` +用上下文表达式可简单表达为 +```shell +QUERY = [SELECT TOKEN] KEY [FROM TOKEN] TABLE; +KEY = [* TOKEN] | TOKEN | TOKEN , +TABLE = TOKEN +``` +> 这个案例中 `KEY` 定义为三种形式, 对应的 `SQL` 分别为 `SELECT * FROM TABLE;` `SELECT ID FROM TABLE;` `SELECT ID, COLUMN1, COLUMN2 FROPM TABLE;` 用竖线(|)来表示推到的多种可能 + +可以清晰地看到, 最开始看似杂乱无序的一条语句最终就可以通过分治的思想化解为一个个小问题, 以此类推最终对应到我们制定的 `TOKEN` 规则, 如果`TOKEN STREAM`最终匹配不上我们所制定的所有规则, 那么则解析失败 + + +有了此类分析规则的简单概念后, 接下来我们就利用几个简单的工具实现这个解析器的功能 ### bison -### goyacc \ No newline at end of file +[bison](https://www.gnu.org/software/bison/) GNU bison是一个自由软件,用于自动生成语法分析器程序,实际上可用于所有常见的操作系统. 可以结合上文`flex`分析后生成的`token stream`继续进行语法部分的分析 + +> 继上文`flex`工具编辑一个简单的`SQL`解析器, 本案例非完全支持完整语法, 仅支持`SELECT A, B, C FROM TABLE;` `SELECT A, B, C, *;` 形式用作展示 + +test.l +```c +/* Declarations */ +%{ +#define YYSTYPE char * +#include "y.tab.h" +void yyerror(const char *msg); +#define _DUPTEXT { yylval = strdup(yytext); } +%} + + +/* Definitions */ +IDENTIFIER [_a-zA-Z][_a-zA-Z0-9]* +OPERATOR [,;*] +WHITESPACE ([ \t\r\a]+) +/* Rules */ +%% +\n { /* void */ } +SELECT { _DUPTEXT; return T_SELECT; } +FROM { _DUPTEXT; return T_FROM; } +{WHITESPACE} { /* ignore every whitespace */ } +{OPERATOR} { _DUPTEXT; return yytext[0]; } +{IDENTIFIER} { _DUPTEXT; return T_IDENTIFIER; } +. { return 0; } + +%% + +int yywrap(void) { + return 1; +} + +void yyerror(const char *s) { + extern int yylineno; + extern char *yytext; + int len = strlen(yytext); + int i = 0; + char buf[512] = {0}; + for (; i < len; i++) + { + sprintf(buf, "%s%d:%c ", buf, yytext[i], yytext[i]); + } + fprintf(stderr, "ERROR: %s at symbol '%s' on line %d\n", s, buf, yylineno); +} +``` +test.y +```c +%{ +#include +#include +#include +#include "tree.h" +int columns = 0; +%} +/* 定义产生式所产生的数据类型 */ +%union{ + struct ast *_ast; + char** strs; + char* str; +} +/* 定义各个产生式返回的数据类型 */ +%type <_ast> Q +%type K +%type '*' ';' +%token T_SELECT T_FROM T_IDENTIFIER + +%start S +/* + SELECT A, B, C FROM TABLE; + SELECT A, B, C, *; +*/ +%% + +S : { /* void */ } + | Q { ast_print($1); ast_free($1); exit(0); } + ; +/* SQL 产生式定义 */ +Q : T_SELECT K T_FROM T_IDENTIFIER ';' { + struct ast *_ast = new_ast(); + _ast->command = T_SELECT; + ast_add_table(_ast, $4); + ast_add_fields(_ast, $2); + free($4); + int i = 0; + for (; i < _ast->filed_size; i++) { + free($2[i]); + } + free($2); + $$ = _ast; /* 将生产的 struct ast* 作为返回值返返回上一级产生式, 每个产生式的返回值有数据类型的限制, 具体看用户定义 */ + } + + | T_SELECT K ';' { + struct ast *_ast = new_ast(); + _ast->command = T_SELECT; + ast_add_fields(_ast, $2); + int i = 0; + for (; i < _ast->filed_size; i++) { + free($2[i]); + } + free($2); + $$ = _ast; + } + ; + +/* columns 产生式定义 */ +K : '*' { + columns++; + char** filed = malloc(sizeof(char*)); + filed[0] = strdup($1); + $$ = filed; + } + | K ',' T_IDENTIFIER { + columns++; + int len = columns; + char** result = malloc(sizeof(char*) * len); + int i = 0; + for (; i < len-1; i++) { + result[i] = strdup($1[i]); + free($1[i]); + } + free($1); + result[i] = strdup($3); + $$ = result; + } + | T_IDENTIFIER { + columns++; + char** filed = malloc(sizeof(char*)); + filed[0] = strdup($1); + $$ = filed; + } + ; + +%% + +int main() { + return yyparse(); +} +``` +tree.h +```c +struct ast +{ + int command; + char **fileds; + int filed_size; + char *table; +}; +void ast_add_table(struct ast *, char *); + +void ast_add_fields(struct ast *, char **); + +void ast_print(struct ast *); + +void ast_free(struct ast *); + +struct ast *new_ast(); + +extern int columns; +``` +tree.h +```c +#include "tree.h" +#include +#include +#include +#include "y.tab.h" +struct ast *new_ast() +{ + struct ast *_ast = malloc(sizeof(struct ast)); + _ast->table = NULL; + _ast->filed_size = 0; + _ast->fileds = NULL; + return _ast; +} + +void ast_print(struct ast *_ast) +{ + if (!_ast) + return; + printf("command: %d\n", _ast->command); + int i = 0; + for (; i < _ast->filed_size; i++) + { + printf("column%i:%s\n", i, _ast->fileds[i]); + } + printf("tablename: %s\n", _ast->table); + return; +} + +void ast_add_table(struct ast *_ast, char *table) +{ + if (!_ast || _ast->table) + return; + _ast->table = strdup(table); + return; +} + +void ast_add_fields(struct ast *_ast, char **fileds) +{ + if (!_ast || _ast->fileds) + return; + int len = columns; + _ast->filed_size = len; + char **_fileds = malloc(sizeof(char *) * len); + int i = 0; + for (; i < len; i++) + { + _fileds[i] = strdup(fileds[i]); + } + _ast->fileds = _fileds; +} + +void ast_free(struct ast *_ast) +{ + if (_ast->table) + free(_ast->table); + int i = 0; + for (; i < _ast->filed_size; i++) + free(_ast->fileds[i]); + free(_ast->fileds); + free(_ast); +} +``` +output +```shell +flex test.l && bison -vdty test.y && gcc -std=c89 -o test y.tab.c lex.yy.c tree.c +./test +SELECT A FROM B; +command: 258 +column0:A +tablename: B +``` +成功解析这条小`SQL`的各个部分, 当然这边的过程生成的是一个非常简单的数据结构, 仅作对应信息的统计, 通过设计更成熟的语法树而后结合`token stream`及以上的概念就可以初步生成一棵后续步骤所需要的语法树 + +再回过头来看看第一步中那段`go`代码生成的语法树(依旧是用错误代码来生成,提示此处的语法树生成仅仅是初步的阶段, 后续还要进行诸多步骤的处理) +```go +package main + +import ( + "go/ast" + "go/parser" + "go/token" + "log" +) + +func main() { + src := ` + package main + + func main() { + var num1, num2 int + num1 += num2 + _ = num1 + num1 += "str" + return + } + ` + + // Initialize the parser. + fset := token.NewFileSet() // positions are relative to fset + f, err := parser.ParseFile(fset, "", src, 0) + if err != nil { + log.Fatalln(err) + } + ast.Print(fset, f) +} +``` +output: +```shell + 0 *ast.File { + 1 . Package: 2:2 + 2 . Name: *ast.Ident { + 3 . . NamePos: 2:10 + 4 . . Name: "main" + 5 . } + 6 . Decls: []ast.Decl (len = 1) { + 7 . . 0: *ast.FuncDecl { + 8 . . . Name: *ast.Ident { + 9 . . . . NamePos: 4:7 + 10 . . . . Name: "main" + 11 . . . . Obj: *ast.Object { + 12 . . . . . Kind: func + 13 . . . . . Name: "main" + 14 . . . . . Decl: *(obj @ 7) + 15 . . . . } + 16 . . . } + 17 . . . Type: *ast.FuncType { + 18 . . . . Func: 4:2 + 19 . . . . Params: *ast.FieldList { + 20 . . . . . Opening: 4:11 + 21 . . . . . Closing: 4:12 + 22 . . . . } + 23 . . . } + 24 . . . Body: *ast.BlockStmt { + 25 . . . . Lbrace: 4:14 + 26 . . . . List: []ast.Stmt (len = 5) { + 27 . . . . . 0: *ast.DeclStmt { + 28 . . . . . . Decl: *ast.GenDecl { + 29 . . . . . . . TokPos: 5:3 + 30 . . . . . . . Tok: var + 31 . . . . . . . Lparen: - + 32 . . . . . . . Specs: []ast.Spec (len = 1) { + 33 . . . . . . . . 0: *ast.ValueSpec { + 34 . . . . . . . . . Names: []*ast.Ident (len = 2) { + 35 . . . . . . . . . . 0: *ast.Ident { + 36 . . . . . . . . . . . NamePos: 5:7 + 37 . . . . . . . . . . . Name: "num1" + 38 . . . . . . . . . . . Obj: *ast.Object { + 39 . . . . . . . . . . . . Kind: var + 40 . . . . . . . . . . . . Name: "num1" + 41 . . . . . . . . . . . . Decl: *(obj @ 33) + 42 . . . . . . . . . . . . Data: 0 + 43 . . . . . . . . . . . } + 44 . . . . . . . . . . } + 45 . . . . . . . . . . 1: *ast.Ident { + 46 . . . . . . . . . . . NamePos: 5:13 + 47 . . . . . . . . . . . Name: "num2" + 48 . . . . . . . . . . . Obj: *ast.Object { + 49 . . . . . . . . . . . . Kind: var + 50 . . . . . . . . . . . . Name: "num2" + 51 . . . . . . . . . . . . Decl: *(obj @ 33) + 52 . . . . . . . . . . . . Data: 0 + 53 . . . . . . . . . . . } + 54 . . . . . . . . . . } + 55 . . . . . . . . . } + 56 . . . . . . . . . Type: *ast.Ident { + 57 . . . . . . . . . . NamePos: 5:18 + 58 . . . . . . . . . . Name: "int" + 59 . . . . . . . . . } + 60 . . . . . . . . } + 61 . . . . . . . } + 62 . . . . . . . Rparen: - + 63 . . . . . . } + 64 . . . . . } + 65 . . . . . 1: *ast.AssignStmt { + 66 . . . . . . Lhs: []ast.Expr (len = 1) { + 67 . . . . . . . 0: *ast.Ident { + 68 . . . . . . . . NamePos: 6:3 + 69 . . . . . . . . Name: "num1" + 70 . . . . . . . . Obj: *(obj @ 38) + 71 . . . . . . . } + 72 . . . . . . } + 73 . . . . . . TokPos: 6:8 + 74 . . . . . . Tok: += + 75 . . . . . . Rhs: []ast.Expr (len = 1) { + 76 . . . . . . . 0: *ast.Ident { + 77 . . . . . . . . NamePos: 6:11 + 78 . . . . . . . . Name: "num2" + 79 . . . . . . . . Obj: *(obj @ 48) + 80 . . . . . . . } + 81 . . . . . . } + 82 . . . . . } + 83 . . . . . 2: *ast.AssignStmt { + 84 . . . . . . Lhs: []ast.Expr (len = 1) { + 85 . . . . . . . 0: *ast.Ident { + 86 . . . . . . . . NamePos: 7:3 + 87 . . . . . . . . Name: "_" + 88 . . . . . . . } + 89 . . . . . . } + 90 . . . . . . TokPos: 7:5 + 91 . . . . . . Tok: = + 92 . . . . . . Rhs: []ast.Expr (len = 1) { + 93 . . . . . . . 0: *ast.Ident { + 94 . . . . . . . . NamePos: 7:7 + 95 . . . . . . . . Name: "num1" + 96 . . . . . . . . Obj: *(obj @ 38) + 97 . . . . . . . } + 98 . . . . . . } + 99 . . . . . } + 100 . . . . . 3: *ast.AssignStmt { + 101 . . . . . . Lhs: []ast.Expr (len = 1) { + 102 . . . . . . . 0: *ast.Ident { + 103 . . . . . . . . NamePos: 8:3 + 104 . . . . . . . . Name: "num1" + 105 . . . . . . . . Obj: *(obj @ 38) + 106 . . . . . . . } + 107 . . . . . . } + 108 . . . . . . TokPos: 8:8 + 109 . . . . . . Tok: += + 110 . . . . . . Rhs: []ast.Expr (len = 1) { + 111 . . . . . . . 0: *ast.BasicLit { + 112 . . . . . . . . ValuePos: 8:11 + 113 . . . . . . . . Kind: STRING + 114 . . . . . . . . Value: "\"str\"" + 115 . . . . . . . } + 116 . . . . . . } + 117 . . . . . } + 118 . . . . . 4: *ast.ReturnStmt { + 119 . . . . . . Return: 9:3 + 120 . . . . . } + 121 . . . . } + 122 . . . . Rbrace: 10:2 + 123 . . . } + 124 . . } + 125 . } + 126 . Scope: *ast.Scope { + 127 . . Objects: map[string]*ast.Object (len = 1) { + 128 . . . "main": *(obj @ 11) + 129 . . } + 130 . } + 131 . Unresolved: []*ast.Ident (len = 1) { + 132 . . 0: *(obj @ 56) + 133 . } + 134 } +``` +### goyacc +`goyacc` 是一个 `golang` 版的 `yacc` 工具(作用和上文介绍的`flex & bison`)类似, 不同的是没有对应的`lex`工具, 这部分逻辑需要自己实现 +接下来用`goyacc`将上述的`SQL`小解析器实现一遍 + +parser.y +```c +%{ +package sql + +var columns int; + +func setResult(l yyLexer, v *ast) { + l.(*lex).result = v +} + +%} +/* 定义产生式所产生的数据类型 */ +%union{ + _ast * ast + strs []string + str string +} +/* 定义各个产生式返回的数据类型 */ +%type <_ast> Q +%type K +%type '*' ';' +%token T_SELECT T_FROM T_IDENTIFIER + +%start S +/* + SELECT A, B, C FROM TABLE; + SELECT A, B, C, *; +*/ +%% + +S : { /* void */ } + | Q { setResult(yylex, $1) } + ; +/* SQL 产生式定义 */ +Q : T_SELECT K T_FROM T_IDENTIFIER ';' { + _ast := new_ast(); + _ast.command = T_SELECT; + ast_add_table(_ast, $4); + ast_add_fields(_ast, $2); + $$ = _ast; /* 将生产的 struct ast* 作为返回值返返回上一级产生式, 每个产生式的返回值有数据类型的限制, 具体看用户定义 */ + } + + | T_SELECT K ';' { + _ast := new_ast(); + _ast.command = T_SELECT; + ast_add_fields(_ast, $2); + $$ = _ast; + } + ; + +/* columns 产生式定义 */ +K : '*' { + columns++; + $$ = []string{$1}; + } + | K ',' T_IDENTIFIER { + columns++; + $$ = append($1, $3); + } + | T_IDENTIFIER { + columns++; + $$ = []string{$1}; + } + ; + +%% +``` +tree.go +```go +package sql + +import ( + "errors" + "fmt" + "os" + "strings" +) + +//go:generate go run golang.org/x/tools/cmd/goyacc -l -o parser.go parser.y + +type ast struct { + command int + fileds []string + filed_size int + table string +} +type lex struct { + input []byte + pos int + result *ast + err error +} + +func Parse(sql string) (*ast, error) { + l := &lex{ + input: []byte(sql), + } + _ = yyParse(l) // yyParse 入参需要实现 goyacc lex 接口 + return l.result, l.err +} + +func (l *lex) Lex(lval *yySymType) int { // token 解析入口 + str := l.nextString() + switch strings.ToLower(str) { + case "": + return 0 + case "*", ";", ",": + lval.str = str // 对应 flex 中 yylval = strdup(yytext); 赋值操作, 需要和返回的类型相对于, goyacc 会根据返回的类型去对应的字段获取 + return int(byte(str[0])) // 对应 flex 中 return 返回 token 类型 + case "select": + lval.str = str + return T_SELECT + case "from": + lval.str = str + return T_FROM + default: + lval.str = str + return T_IDENTIFIER + } +} +func (l *lex) nextString() string { + /* trim left */ + for { + if l.pos >= len(l.input) { + return "" + } + switch l.input[l.pos] { + case ' ', '\r', '\n', '\t', '\a': + l.pos++ + default: + goto next + } + } +next: + /* get next string */ + var index int = l.pos + for ; index < len(l.input); index++ { + switch l.input[index] { + case ' ', '\r', '\n', '\t', '\a': + goto next_2 + case '*', ';', ',': + if index == l.pos { + index++ + } + goto next_2 + default: + } + } +next_2: + result := l.input[l.pos:index] + l.pos = index + return string(result) +} + +// Error satisfies yyLexer. +func (l *lex) Error(s string) { + l.err = errors.New(s) +} + +func new_ast() *ast { + return &ast{} +} + +func ast_print(_ast *ast) { + if _ast == nil { + return + } + fmt.Printf("command:%s\n", func() string { + switch _ast.command { + case T_SELECT: + return "select" + default: + return "unknow" + } + }()) + for i := range _ast.fileds { + fmt.Printf("column%d:%s\n", i, _ast.fileds[i]) + } + fmt.Printf("tablename:%s\n", _ast.table) +} + +func ast_add_table(_ast *ast, table string) { + if _ast == nil || _ast.table != "" { + return + } + _ast.table = table +} + +func ast_add_fields(_ast *ast, fileds []string) { + if _ast == nil { + return + } + _ast.fileds = fileds + _ast.filed_size = len(fileds) +} + +func ast_free(_ast *ast) { + /* void */ + return +} + +func exit(i int) { + os.Exit(i) +} + +``` +lex_test.go +```go +package sql + +import ( + "fmt" + "log" + "testing" +) + +func TestLex(t *testing.T) { + sqls := []string{ + `SELECT * FROM TABLE1;`, + `SelecT A,B,C FROM TB;`, + `SELECT A,B,C;`, + } + for _, sql := range sqls { + _ast, err := Parse(sql) + if err != nil { + log.Fatalln(err) + } + ast_print(_ast) + fmt.Println() + } +} + +``` +output: +```shell +go run golang.org/x/tools/cmd/goyacc -l -o parser.go parser.y +go test -v . +=== RUN TestLex +command:select +column0:* +tablename:TABLE1 + +command:select +column0:A +column1:B +column2:C +tablename:TB + +command:select +column0:A +column1:B +column2:C +tablename: + +--- PASS: TestLex (0.00s) +PASS +ok json1/sql 0.006s +``` + +对`goyacc`感兴趣的同学可以自行查找, 下面再推荐一个项目, 对入门学习会有比较好的指导 +## 类型检查 +## 中间代码生成 +## 机器码生成 +## 参考资料 +https://pandolia.net/tinyc/index.html +https://github.com/sougou/parser_tutorial \ No newline at end of file From c67d43a6fa8a9e8b2b35b3c8b600db66c7a81881 Mon Sep 17 00:00:00 2001 From: wziww Date: Wed, 8 Sep 2021 11:29:11 +0800 Subject: [PATCH 105/120] update --- compilers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compilers.md b/compilers.md index 51327c5..87f0b11 100644 --- a/compilers.md +++ b/compilers.md @@ -332,7 +332,7 @@ QUERY = [SELECT TOKEN] KEY [FROM TOKEN] TABLE; KEY = [* TOKEN] | TOKEN | TOKEN , TABLE = TOKEN ``` -> 这个案例中 `KEY` 定义为三种形式, 对应的 `SQL` 分别为 `SELECT * FROM TABLE;` `SELECT ID FROM TABLE;` `SELECT ID, COLUMN1, COLUMN2 FROPM TABLE;` 用竖线(|)来表示推到的多种可能 +> 这个案例中 `KEY` 定义为三种形式, 对应的 `SQL` 分别为 `SELECT * FROM TABLE;` `SELECT ID FROM TABLE;` `SELECT ID, COLUMN1, COLUMN2 FROPM TABLE;` 用竖线 | 来表示推到的多种可能 可以清晰地看到, 最开始看似杂乱无序的一条语句最终就可以通过分治的思想化解为一个个小问题, 以此类推最终对应到我们制定的 `TOKEN` 规则, 如果`TOKEN STREAM`最终匹配不上我们所制定的所有规则, 那么则解析失败 From 0e9e5463f87fca2c41a47ba4d59a2927b01d50f1 Mon Sep 17 00:00:00 2001 From: wziww Date: Wed, 8 Sep 2021 11:34:27 +0800 Subject: [PATCH 106/120] update --- compilers.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/compilers.md b/compilers.md index 87f0b11..4c5638a 100644 --- a/compilers.md +++ b/compilers.md @@ -1011,10 +1011,9 @@ PASS ok json1/sql 0.006s ``` -对`goyacc`感兴趣的同学可以自行查找, 下面再推荐一个项目, 对入门学习会有比较好的指导 ## 类型检查 ## 中间代码生成 ## 机器码生成 ## 参考资料 -https://pandolia.net/tinyc/index.html -https://github.com/sougou/parser_tutorial \ No newline at end of file +[flex & bison](https://pandolia.net/tinyc/index.html) +[goyacc 入门案例](https://github.com/sougou/parser_tutorial) \ No newline at end of file From 6710bf558182b3cd1084f4ab5ca9dd509f3b821f Mon Sep 17 00:00:00 2001 From: wziww Date: Wed, 8 Sep 2021 11:40:17 +0800 Subject: [PATCH 107/120] update --- compilers.md | 1 + 1 file changed, 1 insertion(+) diff --git a/compilers.md b/compilers.md index 4c5638a..296a7c7 100644 --- a/compilers.md +++ b/compilers.md @@ -1016,4 +1016,5 @@ ok json1/sql 0.006s ## 机器码生成 ## 参考资料 [flex & bison](https://pandolia.net/tinyc/index.html) + [goyacc 入门案例](https://github.com/sougou/parser_tutorial) \ No newline at end of file From a7ab0ce3f1f63871fa1e64bb9dca9ace77990808 Mon Sep 17 00:00:00 2001 From: wziww Date: Mon, 20 Sep 2021 15:28:36 +0800 Subject: [PATCH 108/120] =?UTF-8?q?=E7=B1=BB=E5=9E=8B=E6=A3=80=E6=9F=A5?= =?UTF-8?q?=E9=83=A8=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- compilers.md | 228 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 227 insertions(+), 1 deletion(-) diff --git a/compilers.md b/compilers.md index 296a7c7..d6739b8 100644 --- a/compilers.md +++ b/compilers.md @@ -609,7 +609,7 @@ func main() { ast.Print(fset, f) } ``` -output: +#### AST_output: ```shell 0 *ast.File { 1 . Package: 2:2 @@ -1012,6 +1012,232 @@ ok json1/sql 0.006s ``` ## 类型检查 +在进行类型检查这一步的时候, 整个文件可以粗略分为`变量声明及作用域`以及`表达式`两块内容, 在检查 AST 的过程中遇到一些定义的变量会在当前作用域中查找是否有对应的声明, 如果没找到则顺着作用域向上去父级作用域寻找 + +### 声明及作用域 +作用域可大致分为几类 +1. 全局 +2. 对应函数体内 +3. 块级表达式内 {...} 单独一堆花括号包裹范围内 +```go +// 作用域结构体定义 +type Scope struct { + parent *Scope + children []*Scope + elems map[string]Object // lazily allocated + pos, end token.Pos // scope extent; may be invalid + comment string // for debugging only + isFunc bool // set if this is a function scope (internal use only) +} +func (s *Scope) LookupParent(name string, pos token.Pos) (*Scope, Object) { + for ; s != nil; s = s.parent { + if obj := s.elems[name]; obj != nil && (!pos.IsValid() || obj.scopePos() <= pos) { + return s, obj + } + } + return nil, nil +} +``` +回顾下 `AST file` 的数据结构 +```go +type File struct { + Doc *CommentGroup // associated documentation; or nil + Package token.Pos // position of "package" keyword + Name *Ident // package name + Decls []Decl // top-level declarations; or nil + Scope *Scope // package scope (this file only) + Imports []*ImportSpec // imports in this file + Unresolved []*Ident // unresolved identifiers in this file + Comments []*CommentGroup // list of all comments in the source file +} +``` +再结合之前输出的 [`AST`](#ast_output), 我们可以看到在生成的过程中, 大部分的 `identifiers token` 是能够确认对应类型的, 例如函数声明之类的, 那么对应函数名的 `token` 就可以被成功解析为对应类型的语法树中的一个节点 + +但是依旧存在一些在`AST`初步生成阶段无法被成功解析的, 那么会被存放在`Unresolved`字段中, 就比如上面输出的`int`, 那么这时候就通过向上从父级中依次查找, 如果最终能够找到对应定义, 那么检查成功, 否则则抛出未定义异常 +例: +```go +package main + +import ( + "go/ast" + "go/parser" + "go/token" + "go/types" + "log" +) + +func main() { + src := ` + + package main + + func main() { + var num1, num2 int + num1 += num2 + _ = num1 + testval++ + return + } + ` + + // Initialize the parser. + fset := token.NewFileSet() // positions are relative to fset + f, err := parser.ParseFile(fset, "", src, parser.AllErrors|parser.ParseComments) + if err != nil { + log.Fatalln(err) + } + pkg, err := new(types.Config).Check("test.go", fset, []*ast.File{f}, nil) + if err != nil { + log.Fatal(err) + } + + _ = pkg +} + +``` +output: +```shell +2021/09/20 15:19:01 9:3: undeclared name: testval +``` +### 表达式检查 +截取之前生成的`AST`中的一小段 +`num1 += num2` +```shell +65 . . . . . 1: *ast.AssignStmt { +66 . . . . . . Lhs: []ast.Expr (len = 1) { +67 . . . . . . . 0: *ast.Ident { +68 . . . . . . . . NamePos: 6:3 +69 . . . . . . . . Name: "num1" +70 . . . . . . . . Obj: *(obj @ 38) +71 . . . . . . . } +72 . . . . . . } +73 . . . . . . TokPos: 6:8 +74 . . . . . . Tok: += +75 . . . . . . Rhs: []ast.Expr (len = 1) { +76 . . . . . . . 0: *ast.Ident { +77 . . . . . . . . NamePos: 6:11 +78 . . . . . . . . Name: "num2" +79 . . . . . . . . Obj: *(obj @ 48) +80 . . . . . . . } +81 . . . . . . } +82 . . . . . } +``` +先看下这个简单的赋值表达式生成的树形结构 +```mermaid +graph TB + +A((op: +=)) +B((exprL: num1)) +C((exprR: num2)) +A-->B +A-->C +``` +对于当前这部分表达式检查, 需要进行的步骤为 +1. 确认当前操作符(+=) +2. 左子树表达式递归, 并确认表达式最终类型 +3. 右子树表达式递归, 并确认表达式最终类型 +4. 左右 expr 类型校验, 如符合当前操作符规则, 成功, 反之失败 + +```go +// The binary expression e may be nil. It's passed in for better error messages only. +func (check *Checker) binary(x *operand, e *ast.BinaryExpr, lhs, rhs ast.Expr, op token.Token) { + var y operand + + check.expr(x, lhs) // 左子树表达式递归 + check.expr(&y, rhs) // 右子树表达式递归 + /* 先判断下特殊的操作类型 */ + if x.mode == invalid { + return + } + if y.mode == invalid { + x.mode = invalid + x.expr = y.expr + return + } + + if isShift(op) { + check.shift(x, &y, e, op) + return + } + + check.convertUntyped(x, y.typ) + if x.mode == invalid { + return + } + check.convertUntyped(&y, x.typ) + if y.mode == invalid { + x.mode = invalid + return + } + + if isComparison(op) { + check.comparison(x, &y, op) + return + } + /* 默认要求 x y 类型一致 */ + if !check.identical(x.typ, y.typ) { // 类型校验 + // only report an error if we have valid types + // (otherwise we had an error reported elsewhere already) + if x.typ != Typ[Invalid] && y.typ != Typ[Invalid] { + check.invalidOp(x.pos(), "mismatched types %s and %s", x.typ, y.typ) + } + x.mode = invalid + return + } + + if !check.op(binaryOpPredicates, x, op) { + x.mode = invalid + return + } + + if op == token.QUO || op == token.REM { + // check for zero divisor + if (x.mode == constant_ || isInteger(x.typ)) && y.mode == constant_ && constant.Sign(y.val) == 0 { + check.invalidOp(y.pos(), "division by zero") + x.mode = invalid + return + } + + // check for divisor underflow in complex division (see issue 20227) + if x.mode == constant_ && y.mode == constant_ && isComplex(x.typ) { + re, im := constant.Real(y.val), constant.Imag(y.val) + re2, im2 := constant.BinaryOp(re, token.MUL, re), constant.BinaryOp(im, token.MUL, im) + if constant.Sign(re2) == 0 && constant.Sign(im2) == 0 { + check.invalidOp(y.pos(), "division by zero") + x.mode = invalid + return + } + } + } + + if x.mode == constant_ && y.mode == constant_ { + xval := x.val + yval := y.val + typ := x.typ.Underlying().(*Basic) + // force integer division of integer operands + if op == token.QUO && isInteger(typ) { + op = token.QUO_ASSIGN + } + x.val = constant.BinaryOp(xval, op, yval) + // Typed constants must be representable in + // their type after each constant operation. + if isTyped(typ) { + if e != nil { + x.expr = e // for better error message + } + check.representable(x, typ) + } + return + } + + x.mode = value + // x.typ is unchanged +} +``` +> 这边以 types 标准库的类型检查作为案例, 编译器整体流程大同小异 + +以上, 通过`TOKEN`声明以及对应作用域的维护及查找, 再结合各操作符下表达式的递归分析过程, 对于一棵语法树的类型检查就可以进行了 + ## 中间代码生成 ## 机器码生成 ## 参考资料 From 468ac43e54833b00d4021064eec896d652d924d9 Mon Sep 17 00:00:00 2001 From: wziww Date: Mon, 20 Sep 2021 15:29:52 +0800 Subject: [PATCH 109/120] update --- compilers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compilers.md b/compilers.md index d6739b8..1055155 100644 --- a/compilers.md +++ b/compilers.md @@ -1018,7 +1018,7 @@ ok json1/sql 0.006s 作用域可大致分为几类 1. 全局 2. 对应函数体内 -3. 块级表达式内 {...} 单独一堆花括号包裹范围内 +3. 块级表达式内 {...} // 单独一对花括号包裹范围内 ```go // 作用域结构体定义 type Scope struct { From 83f9b5aab4777fe3256efe8cd5eeb8359a262609 Mon Sep 17 00:00:00 2001 From: wziww Date: Mon, 20 Sep 2021 15:30:45 +0800 Subject: [PATCH 110/120] update --- compilers.md | 1 + 1 file changed, 1 insertion(+) diff --git a/compilers.md b/compilers.md index 1055155..63c2a4d 100644 --- a/compilers.md +++ b/compilers.md @@ -1054,6 +1054,7 @@ type File struct { 再结合之前输出的 [`AST`](#ast_output), 我们可以看到在生成的过程中, 大部分的 `identifiers token` 是能够确认对应类型的, 例如函数声明之类的, 那么对应函数名的 `token` 就可以被成功解析为对应类型的语法树中的一个节点 但是依旧存在一些在`AST`初步生成阶段无法被成功解析的, 那么会被存放在`Unresolved`字段中, 就比如上面输出的`int`, 那么这时候就通过向上从父级中依次查找, 如果最终能够找到对应定义, 那么检查成功, 否则则抛出未定义异常 + 例: ```go package main From 6c789c68aabd938e46194a41c5443ab5506a7ead Mon Sep 17 00:00:00 2001 From: wziww Date: Mon, 20 Sep 2021 15:31:59 +0800 Subject: [PATCH 111/120] update --- compilers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compilers.md b/compilers.md index 63c2a4d..f9ab2f0 100644 --- a/compilers.md +++ b/compilers.md @@ -1235,7 +1235,7 @@ func (check *Checker) binary(x *operand, e *ast.BinaryExpr, lhs, rhs ast.Expr, o // x.typ is unchanged } ``` -> 这边以 types 标准库的类型检查作为案例, 编译器整体流程大同小异 +> 这边以 `go/types` 标准库的类型检查作为案例, 编译器整体流程大同小异 以上, 通过`TOKEN`声明以及对应作用域的维护及查找, 再结合各操作符下表达式的递归分析过程, 对于一棵语法树的类型检查就可以进行了 From 73bd93d8d834417bedac6635c176e99fbcbe9d77 Mon Sep 17 00:00:00 2001 From: Zhaoyang Date: Wed, 15 Dec 2021 08:39:51 +0800 Subject: [PATCH 112/120] Update channel.md --- channel.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/channel.md b/channel.md index 5bad5b1..a31d9e3 100644 --- a/channel.md +++ b/channel.md @@ -305,7 +305,7 @@ func chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool { } // qcount 是 buffer 中已塞进的元素数量 - // dataqsize 是 buffer 的总大小 + // dataqsiz 是 buffer 的总大小 // 说明还有余量 if c.qcount < c.dataqsiz { // Space is available in the channel buffer. Enqueue the element to send. From 5ed02dfad907583cb35353c785d9c3cee2079433 Mon Sep 17 00:00:00 2001 From: Xargin Date: Wed, 15 Dec 2021 13:57:46 +0800 Subject: [PATCH 113/120] add sites --- site/README.md | 3 + site/archetypes/default.md | 6 + site/config.toml | 6 + site/content/_index.md | 13 + site/content/docs/_index.md | 0 site/content/docs/api_programming/_index.md | 6 + site/content/docs/api_programming/fasthttp.md | 2 + .../docs/api_programming/httprouter.md | 1 + site/content/docs/api_programming/mysql.md | 1 + site/content/docs/api_programming/orm.md | 1 + .../content/docs/api_programming/validator.md | 1 + site/content/docs/api_programming/viper.md | 1 + site/content/docs/assembly/_index.md | 6 + site/content/docs/assembly/assembly.md | 1006 ++++++++++++++ site/content/docs/assembly/exercises.md | 7 + site/content/docs/bootstrap/_index.md | 7 + site/content/docs/bootstrap/boot.md | 7 + site/content/docs/bootstrap/elf.md | 1 + site/content/docs/bootstrap/exercises.md | 2 + .../docs/compiler_and_linker/_index.md | 6 + .../compiler_and_linker/calling_convention.md | 25 + .../docs/compiler_and_linker/compiler.md | 6 + .../docs/compiler_and_linker/linker.md | 6 + site/content/docs/data_structure/_index.md | 5 + site/content/docs/data_structure/channel.md | 10 + site/content/docs/data_structure/context.md | 7 + site/content/docs/data_structure/interface.md | 7 + site/content/docs/data_structure/map.md | 27 + site/content/docs/data_structure/semaphore.md | 9 + site/content/docs/data_structure/string.md | 7 + site/content/docs/data_structure/timer.md | 6 + site/content/docs/ddd/_index.md | 6 + site/content/docs/debug/_index.md | 6 + site/content/docs/debug/dlv_tutorial.md | 1 + site/content/docs/debug/ssa_debug.md | 1 + site/content/docs/design_patterns/_index.md | 6 + .../content/docs/design_patterns/decorator.md | 1 + site/content/docs/optimization/_index.md | 6 + site/content/docs/optimization/benchmark.md | 1 + .../docs/optimization/continuous_profiling.md | 1 + .../docs/optimization/optimizations.md | 1 + .../docs/optimization/pprof_tutorial.md | 1 + site/content/docs/quality/_index.md | 5 + site/content/docs/quality/auto_review.md | 9 + site/content/docs/quality/custom_linter.md | 5 + site/content/docs/quality/refactoring.md | 5 + site/content/docs/quality/static_analysis.md | 277 ++++ site/content/docs/runtime/_index.md | 5 + .../docs/runtime/memory_management/_index.md | 4 + .../memory_management/escape_analysis.md | 7 + .../runtime/memory_management/finalizer.md | 7 + .../memory_management/garbage_collection.md | 171 +++ .../memory_management/memory_alloctor.md | 32 + site/content/docs/runtime/netpoll/_index.md | 5 + site/content/docs/runtime/netpoll/basics.md | 127 ++ site/content/docs/runtime/netpoll/netpoll.md | 20 + site/content/docs/runtime/scheduler/_index.md | 5 + site/content/docs/runtime/scheduler/gmp.md | 7 + .../runtime/scheduler/handling_blocking.md | 28 + .../docs/runtime/scheduler/preemption.md | 622 +++++++++ .../docs/runtime/scheduler/sched_loop.md | 34 + site/content/docs/std_library/_index.md | 6 + site/content/docs/sync/_index.md | 5 + site/content/docs/sync/lock_free.md | 8 + site/content/docs/sync/memory_barrier.md | 7 + site/content/docs/sync/patterns.md | 8 + site/content/docs/sync/theory.md | 8 + site/content/docs/sync/tools.md | 8 + site/content/docs/sync/tools/_index.md | 6 + site/content/docs/sync/tools/syncPool.md | 7 + site/content/docs/syntax_sugar/_index.md | 6 + site/content/docs/syntax_sugar/defer.md | 1 + .../content/docs/system_programming/_index.md | 6 + .../docs/system_programming/syscall.md | 1 + site/content/docs/system_programming/vdso.md | 1 + site/content/docs/third_party/_index.md | 6 + site/content/docs/third_party/parser.md | 3 + site/content/docs/time/_index.md | 6 + site/content/docs/time/monotonic.md | 1 + site/layouts/shortcodes/rawhtml.html | 2 + ...s_50fc8c04e12a2f59027287995557ceff.content | 1 + ...scss_50fc8c04e12a2f59027287995557ceff.json | 1 + site/static/images/index/banner.jpg | Bin 0 -> 144283 bytes .../images/runtime/block_on_channel_send.jpg | Bin 0 -> 56439 bytes .../static/images/runtime/data_struct/map.png | Bin 0 -> 113842 bytes .../data_struct/map_func_translate2.png | Bin 0 -> 99475 bytes .../data_struct/map_function_translate.png | Bin 0 -> 61707 bytes .../runtime/data_struct/map_tophash.png | Bin 0 -> 57073 bytes .../static/images/runtime/memory/gcphases.jpg | Bin 0 -> 282265 bytes .../runtime/memory/write_barrier_demo.jpg | Bin 0 -> 102325 bytes .../images/runtime/schedule/sche_big.png | Bin 0 -> 168431 bytes site/static/images/sync/syncPool.png | Bin 0 -> 100619 bytes site/themes/book/.github/workflows/main.yml | 24 + site/themes/book/.gitignore | 3 + site/themes/book/LICENSE | 20 + site/themes/book/README.md | 327 +++++ site/themes/book/archetypes/docs.md | 10 + site/themes/book/archetypes/posts.md | 6 + site/themes/book/assets/_custom.scss | 3 + site/themes/book/assets/_defaults.scss | 66 + site/themes/book/assets/_fonts.scss | 39 + site/themes/book/assets/_main.scss | 410 ++++++ site/themes/book/assets/_markdown.scss | 191 +++ site/themes/book/assets/_print.scss | 17 + site/themes/book/assets/_shortcodes.scss | 104 ++ site/themes/book/assets/_utils.scss | 92 ++ site/themes/book/assets/_variables.scss | 3 + site/themes/book/assets/book.scss | 15 + site/themes/book/assets/clipboard.js | 21 + site/themes/book/assets/manifest.json | 15 + site/themes/book/assets/menu-reset.js | 7 + site/themes/book/assets/mermaid.json | 6 + site/themes/book/assets/normalize.css | 349 +++++ .../themes/book/assets/plugins/_numbered.scss | 36 + .../book/assets/plugins/_scrollbars.scss | 26 + site/themes/book/assets/search-data.json | 15 + site/themes/book/assets/search.js | 100 ++ site/themes/book/assets/sw-register.js | 7 + site/themes/book/assets/sw.js | 55 + site/themes/book/assets/themes/_auto.scss | 9 + site/themes/book/assets/themes/_dark.scss | 3 + site/themes/book/assets/themes/_light.scss | 3 + .../book/exampleSite/assets/_custom.scss | 4 + .../book/exampleSite/assets/_variables.scss | 1 + site/themes/book/exampleSite/config.toml | 118 ++ site/themes/book/exampleSite/config.yaml | 114 ++ .../book/exampleSite/content.bn/_index.md | 79 ++ .../book/exampleSite/content.ru/_index.md | 79 ++ .../book/exampleSite/content.zh/_index.md | 79 ++ .../themes/book/exampleSite/content/_index.md | 41 + .../content/docs/example/_index.md | 71 + .../example/collapsed/3rd-level/4th-level.md | 12 + .../example/collapsed/3rd-level/_index.md | 26 + .../content/docs/example/collapsed/_index.md | 4 + .../content/docs/example/hidden.md | 52 + .../docs/example/table-of-contents/_index.md | 85 ++ .../example/table-of-contents/with-toc.md | 64 + .../example/table-of-contents/without-toc.md | 59 + .../content/docs/shortcodes/_index.md | 3 + .../content/docs/shortcodes/buttons.md | 13 + .../content/docs/shortcodes/columns.md | 45 + .../content/docs/shortcodes/details.md | 22 + .../content/docs/shortcodes/expand.md | 35 + .../content/docs/shortcodes/hints.md | 32 + .../content/docs/shortcodes/katex.md | 28 + .../content/docs/shortcodes/mermaid.md | 41 + .../content/docs/shortcodes/section/_index.md | 15 + .../docs/shortcodes/section/first-page.md | 6 + .../docs/shortcodes/section/second-page.md | 6 + .../content/docs/shortcodes/tabs.md | 50 + .../book/exampleSite/content/menu/index.md | 22 + .../book/exampleSite/content/posts/_index.md | 7 + .../content/posts/creating-a-new-theme.md | 1150 +++++++++++++++++ .../content/posts/goisforlovers.md | 344 +++++ .../content/posts/hugoisforlovers.md | 89 ++ .../content/posts/migrate-from-jekyll.md | 156 +++ ...s_50fc8c04e12a2f59027287995557ceff.content | 1 + ...scss_50fc8c04e12a2f59027287995557ceff.json | 1 + site/themes/book/i18n/bn.yaml | 14 + site/themes/book/i18n/cn.yaml | 21 + site/themes/book/i18n/cs.yaml | 14 + site/themes/book/i18n/de.yaml | 14 + site/themes/book/i18n/en.yaml | 14 + site/themes/book/i18n/es.yaml | 14 + site/themes/book/i18n/fa.yaml | 20 + site/themes/book/i18n/fr.yaml | 14 + site/themes/book/i18n/it.yaml | 14 + site/themes/book/i18n/ja.yaml | 20 + site/themes/book/i18n/jp.yaml | 21 + site/themes/book/i18n/ko.yaml | 20 + site/themes/book/i18n/nb.yaml | 14 + site/themes/book/i18n/pt.yaml | 14 + site/themes/book/i18n/ru.yaml | 14 + site/themes/book/i18n/sv.yaml | 14 + site/themes/book/i18n/tr.yaml | 14 + site/themes/book/i18n/uk.yaml | 14 + site/themes/book/i18n/zh-TW.yaml | 20 + site/themes/book/i18n/zh.yaml | 20 + site/themes/book/images/screenshot.png | Bin 0 -> 189080 bytes site/themes/book/images/tn.png | Bin 0 -> 195683 bytes site/themes/book/layouts/404.html | 35 + .../_default/_markup/render-heading.html | 4 + .../_default/_markup/render-image.html | 19 + .../layouts/_default/_markup/render-link.html | 28 + site/themes/book/layouts/_default/baseof.html | 83 ++ site/themes/book/layouts/_default/list.html | 1 + site/themes/book/layouts/_default/single.html | 1 + .../book/layouts/partials/docs/brand.html | 8 + .../book/layouts/partials/docs/comments.html | 11 + .../book/layouts/partials/docs/date.html | 6 + .../book/layouts/partials/docs/footer.html | 30 + .../book/layouts/partials/docs/header.html | 13 + .../book/layouts/partials/docs/html-head.html | 50 + .../layouts/partials/docs/inject/body.html | 0 .../partials/docs/inject/content-after.html | 0 .../partials/docs/inject/content-before.html | 0 .../layouts/partials/docs/inject/footer.html | 0 .../layouts/partials/docs/inject/head.html | 3 + .../partials/docs/inject/menu-after.html | 0 .../partials/docs/inject/menu-before.html | 0 .../partials/docs/inject/toc-after.html | 0 .../partials/docs/inject/toc-before.html | 0 .../book/layouts/partials/docs/languages.html | 31 + .../layouts/partials/docs/menu-bundle.html | 5 + .../layouts/partials/docs/menu-filetree.html | 45 + .../book/layouts/partials/docs/menu-hugo.html | 28 + .../book/layouts/partials/docs/menu.html | 22 + .../book/layouts/partials/docs/post-meta.html | 23 + .../book/layouts/partials/docs/search.html | 7 + .../book/layouts/partials/docs/taxonomy.html | 19 + .../book/layouts/partials/docs/title.html | 15 + .../book/layouts/partials/docs/toc.html | 3 + site/themes/book/layouts/posts/list.html | 22 + site/themes/book/layouts/posts/single.html | 13 + .../book/layouts/shortcodes/button.html | 12 + .../book/layouts/shortcodes/columns.html | 7 + .../book/layouts/shortcodes/details.html | 6 + .../book/layouts/shortcodes/expand.html | 13 + site/themes/book/layouts/shortcodes/hint.html | 3 + .../themes/book/layouts/shortcodes/katex.html | 13 + .../book/layouts/shortcodes/mermaid.html | 12 + .../book/layouts/shortcodes/section.html | 10 + site/themes/book/layouts/shortcodes/tab.html | 12 + site/themes/book/layouts/shortcodes/tabs.html | 15 + site/themes/book/layouts/taxonomy/list.html | 13 + .../book/layouts/taxonomy/taxonomy.html | 22 + site/themes/book/static/favicon.png | Bin 0 -> 109 bytes site/themes/book/static/favicon.svg | 1 + site/themes/book/static/flexsearch.min.js | 42 + .../fonts/roboto-mono-v13-latin-regular.woff | Bin 0 -> 15160 bytes .../fonts/roboto-mono-v13-latin-regular.woff2 | Bin 0 -> 12312 bytes .../static/fonts/roboto-v27-latin-700.woff | Bin 0 -> 20396 bytes .../static/fonts/roboto-v27-latin-700.woff2 | Bin 0 -> 15828 bytes .../fonts/roboto-v27-latin-regular.woff | Bin 0 -> 20332 bytes .../fonts/roboto-v27-latin-regular.woff2 | Bin 0 -> 15688 bytes .../book/static/katex/auto-render.min.js | 1 + .../static/katex/fonts/KaTeX_AMS-Regular.ttf | Bin 0 -> 70972 bytes .../static/katex/fonts/KaTeX_AMS-Regular.woff | Bin 0 -> 38868 bytes .../katex/fonts/KaTeX_AMS-Regular.woff2 | Bin 0 -> 32944 bytes .../katex/fonts/KaTeX_Caligraphic-Bold.ttf | Bin 0 -> 19316 bytes .../katex/fonts/KaTeX_Caligraphic-Bold.woff | Bin 0 -> 11696 bytes .../katex/fonts/KaTeX_Caligraphic-Bold.woff2 | Bin 0 -> 10448 bytes .../katex/fonts/KaTeX_Caligraphic-Regular.ttf | Bin 0 -> 18684 bytes .../fonts/KaTeX_Caligraphic-Regular.woff | Bin 0 -> 11460 bytes .../fonts/KaTeX_Caligraphic-Regular.woff2 | Bin 0 -> 10240 bytes .../static/katex/fonts/KaTeX_Fraktur-Bold.ttf | Bin 0 -> 35660 bytes .../katex/fonts/KaTeX_Fraktur-Bold.woff | Bin 0 -> 22632 bytes .../katex/fonts/KaTeX_Fraktur-Bold.woff2 | Bin 0 -> 20360 bytes .../katex/fonts/KaTeX_Fraktur-Regular.ttf | Bin 0 -> 34352 bytes .../katex/fonts/KaTeX_Fraktur-Regular.woff | Bin 0 -> 22088 bytes .../katex/fonts/KaTeX_Fraktur-Regular.woff2 | Bin 0 -> 19784 bytes .../static/katex/fonts/KaTeX_Main-Bold.ttf | Bin 0 -> 60784 bytes .../static/katex/fonts/KaTeX_Main-Bold.woff | Bin 0 -> 35464 bytes .../static/katex/fonts/KaTeX_Main-Bold.woff2 | Bin 0 -> 30244 bytes .../katex/fonts/KaTeX_Main-BoldItalic.ttf | Bin 0 -> 44496 bytes .../katex/fonts/KaTeX_Main-BoldItalic.woff | Bin 0 -> 25352 bytes .../katex/fonts/KaTeX_Main-BoldItalic.woff2 | Bin 0 -> 21944 bytes .../static/katex/fonts/KaTeX_Main-Italic.ttf | Bin 0 -> 47640 bytes .../static/katex/fonts/KaTeX_Main-Italic.woff | Bin 0 -> 26228 bytes .../katex/fonts/KaTeX_Main-Italic.woff2 | Bin 0 -> 22748 bytes .../static/katex/fonts/KaTeX_Main-Regular.ttf | Bin 0 -> 69520 bytes .../katex/fonts/KaTeX_Main-Regular.woff | Bin 0 -> 38112 bytes .../katex/fonts/KaTeX_Main-Regular.woff2 | Bin 0 -> 32464 bytes .../katex/fonts/KaTeX_Math-BoldItalic.ttf | Bin 0 -> 39308 bytes .../katex/fonts/KaTeX_Math-BoldItalic.woff | Bin 0 -> 22324 bytes .../katex/fonts/KaTeX_Math-BoldItalic.woff2 | Bin 0 -> 19720 bytes .../static/katex/fonts/KaTeX_Math-Italic.ttf | Bin 0 -> 40992 bytes .../static/katex/fonts/KaTeX_Math-Italic.woff | Bin 0 -> 22844 bytes .../katex/fonts/KaTeX_Math-Italic.woff2 | Bin 0 -> 20096 bytes .../katex/fonts/KaTeX_SansSerif-Bold.ttf | Bin 0 -> 33688 bytes .../katex/fonts/KaTeX_SansSerif-Bold.woff | Bin 0 -> 18516 bytes .../katex/fonts/KaTeX_SansSerif-Bold.woff2 | Bin 0 -> 15732 bytes .../katex/fonts/KaTeX_SansSerif-Italic.ttf | Bin 0 -> 30960 bytes .../katex/fonts/KaTeX_SansSerif-Italic.woff | Bin 0 -> 17572 bytes .../katex/fonts/KaTeX_SansSerif-Italic.woff2 | Bin 0 -> 15024 bytes .../katex/fonts/KaTeX_SansSerif-Regular.ttf | Bin 0 -> 29812 bytes .../katex/fonts/KaTeX_SansSerif-Regular.woff | Bin 0 -> 16228 bytes .../katex/fonts/KaTeX_SansSerif-Regular.woff2 | Bin 0 -> 13708 bytes .../katex/fonts/KaTeX_Script-Regular.ttf | Bin 0 -> 24620 bytes .../katex/fonts/KaTeX_Script-Regular.woff | Bin 0 -> 13428 bytes .../katex/fonts/KaTeX_Script-Regular.woff2 | Bin 0 -> 12064 bytes .../katex/fonts/KaTeX_Size1-Regular.ttf | Bin 0 -> 12916 bytes .../katex/fonts/KaTeX_Size1-Regular.woff | Bin 0 -> 6696 bytes .../katex/fonts/KaTeX_Size1-Regular.woff2 | Bin 0 -> 5592 bytes .../katex/fonts/KaTeX_Size2-Regular.ttf | Bin 0 -> 12172 bytes .../katex/fonts/KaTeX_Size2-Regular.woff | Bin 0 -> 6436 bytes .../katex/fonts/KaTeX_Size2-Regular.woff2 | Bin 0 -> 5392 bytes .../katex/fonts/KaTeX_Size3-Regular.ttf | Bin 0 -> 8120 bytes .../katex/fonts/KaTeX_Size3-Regular.woff | Bin 0 -> 4568 bytes .../katex/fonts/KaTeX_Size3-Regular.woff2 | Bin 0 -> 3728 bytes .../katex/fonts/KaTeX_Size4-Regular.ttf | Bin 0 -> 11016 bytes .../katex/fonts/KaTeX_Size4-Regular.woff | Bin 0 -> 6184 bytes .../katex/fonts/KaTeX_Size4-Regular.woff2 | Bin 0 -> 5028 bytes .../katex/fonts/KaTeX_Typewriter-Regular.ttf | Bin 0 -> 35924 bytes .../katex/fonts/KaTeX_Typewriter-Regular.woff | Bin 0 -> 20260 bytes .../fonts/KaTeX_Typewriter-Regular.woff2 | Bin 0 -> 17272 bytes site/themes/book/static/katex/katex.min.css | 1 + site/themes/book/static/katex/katex.min.js | 1 + site/themes/book/static/mermaid.min.js | 32 + site/themes/book/static/svg/calendar.svg | 1 + site/themes/book/static/svg/edit.svg | 1 + site/themes/book/static/svg/menu.svg | 1 + site/themes/book/static/svg/toc.svg | 1 + site/themes/book/static/svg/translate.svg | 1 + site/themes/book/theme.toml | 15 + 305 files changed, 8723 insertions(+) create mode 100644 site/README.md create mode 100644 site/archetypes/default.md create mode 100644 site/config.toml create mode 100644 site/content/_index.md create mode 100644 site/content/docs/_index.md create mode 100644 site/content/docs/api_programming/_index.md create mode 100644 site/content/docs/api_programming/fasthttp.md create mode 100644 site/content/docs/api_programming/httprouter.md create mode 100644 site/content/docs/api_programming/mysql.md create mode 100644 site/content/docs/api_programming/orm.md create mode 100644 site/content/docs/api_programming/validator.md create mode 100644 site/content/docs/api_programming/viper.md create mode 100644 site/content/docs/assembly/_index.md create mode 100644 site/content/docs/assembly/assembly.md create mode 100644 site/content/docs/assembly/exercises.md create mode 100644 site/content/docs/bootstrap/_index.md create mode 100644 site/content/docs/bootstrap/boot.md create mode 100644 site/content/docs/bootstrap/elf.md create mode 100644 site/content/docs/bootstrap/exercises.md create mode 100644 site/content/docs/compiler_and_linker/_index.md create mode 100644 site/content/docs/compiler_and_linker/calling_convention.md create mode 100644 site/content/docs/compiler_and_linker/compiler.md create mode 100644 site/content/docs/compiler_and_linker/linker.md create mode 100644 site/content/docs/data_structure/_index.md create mode 100644 site/content/docs/data_structure/channel.md create mode 100644 site/content/docs/data_structure/context.md create mode 100644 site/content/docs/data_structure/interface.md create mode 100644 site/content/docs/data_structure/map.md create mode 100644 site/content/docs/data_structure/semaphore.md create mode 100644 site/content/docs/data_structure/string.md create mode 100644 site/content/docs/data_structure/timer.md create mode 100644 site/content/docs/ddd/_index.md create mode 100644 site/content/docs/debug/_index.md create mode 100644 site/content/docs/debug/dlv_tutorial.md create mode 100644 site/content/docs/debug/ssa_debug.md create mode 100644 site/content/docs/design_patterns/_index.md create mode 100644 site/content/docs/design_patterns/decorator.md create mode 100644 site/content/docs/optimization/_index.md create mode 100644 site/content/docs/optimization/benchmark.md create mode 100644 site/content/docs/optimization/continuous_profiling.md create mode 100644 site/content/docs/optimization/optimizations.md create mode 100644 site/content/docs/optimization/pprof_tutorial.md create mode 100644 site/content/docs/quality/_index.md create mode 100644 site/content/docs/quality/auto_review.md create mode 100644 site/content/docs/quality/custom_linter.md create mode 100644 site/content/docs/quality/refactoring.md create mode 100644 site/content/docs/quality/static_analysis.md create mode 100644 site/content/docs/runtime/_index.md create mode 100644 site/content/docs/runtime/memory_management/_index.md create mode 100644 site/content/docs/runtime/memory_management/escape_analysis.md create mode 100644 site/content/docs/runtime/memory_management/finalizer.md create mode 100644 site/content/docs/runtime/memory_management/garbage_collection.md create mode 100644 site/content/docs/runtime/memory_management/memory_alloctor.md create mode 100644 site/content/docs/runtime/netpoll/_index.md create mode 100644 site/content/docs/runtime/netpoll/basics.md create mode 100644 site/content/docs/runtime/netpoll/netpoll.md create mode 100644 site/content/docs/runtime/scheduler/_index.md create mode 100644 site/content/docs/runtime/scheduler/gmp.md create mode 100644 site/content/docs/runtime/scheduler/handling_blocking.md create mode 100644 site/content/docs/runtime/scheduler/preemption.md create mode 100644 site/content/docs/runtime/scheduler/sched_loop.md create mode 100644 site/content/docs/std_library/_index.md create mode 100644 site/content/docs/sync/_index.md create mode 100644 site/content/docs/sync/lock_free.md create mode 100644 site/content/docs/sync/memory_barrier.md create mode 100644 site/content/docs/sync/patterns.md create mode 100644 site/content/docs/sync/theory.md create mode 100644 site/content/docs/sync/tools.md create mode 100644 site/content/docs/sync/tools/_index.md create mode 100644 site/content/docs/sync/tools/syncPool.md create mode 100644 site/content/docs/syntax_sugar/_index.md create mode 100644 site/content/docs/syntax_sugar/defer.md create mode 100644 site/content/docs/system_programming/_index.md create mode 100644 site/content/docs/system_programming/syscall.md create mode 100644 site/content/docs/system_programming/vdso.md create mode 100644 site/content/docs/third_party/_index.md create mode 100644 site/content/docs/third_party/parser.md create mode 100644 site/content/docs/time/_index.md create mode 100644 site/content/docs/time/monotonic.md create mode 100644 site/layouts/shortcodes/rawhtml.html create mode 100644 site/resources/_gen/assets/scss/book.scss_50fc8c04e12a2f59027287995557ceff.content create mode 100644 site/resources/_gen/assets/scss/book.scss_50fc8c04e12a2f59027287995557ceff.json create mode 100644 site/static/images/index/banner.jpg create mode 100644 site/static/images/runtime/block_on_channel_send.jpg create mode 100644 site/static/images/runtime/data_struct/map.png create mode 100644 site/static/images/runtime/data_struct/map_func_translate2.png create mode 100644 site/static/images/runtime/data_struct/map_function_translate.png create mode 100644 site/static/images/runtime/data_struct/map_tophash.png create mode 100644 site/static/images/runtime/memory/gcphases.jpg create mode 100644 site/static/images/runtime/memory/write_barrier_demo.jpg create mode 100644 site/static/images/runtime/schedule/sche_big.png create mode 100644 site/static/images/sync/syncPool.png create mode 100644 site/themes/book/.github/workflows/main.yml create mode 100644 site/themes/book/.gitignore create mode 100644 site/themes/book/LICENSE create mode 100644 site/themes/book/README.md create mode 100644 site/themes/book/archetypes/docs.md create mode 100644 site/themes/book/archetypes/posts.md create mode 100644 site/themes/book/assets/_custom.scss create mode 100644 site/themes/book/assets/_defaults.scss create mode 100644 site/themes/book/assets/_fonts.scss create mode 100644 site/themes/book/assets/_main.scss create mode 100644 site/themes/book/assets/_markdown.scss create mode 100644 site/themes/book/assets/_print.scss create mode 100644 site/themes/book/assets/_shortcodes.scss create mode 100644 site/themes/book/assets/_utils.scss create mode 100644 site/themes/book/assets/_variables.scss create mode 100644 site/themes/book/assets/book.scss create mode 100644 site/themes/book/assets/clipboard.js create mode 100644 site/themes/book/assets/manifest.json create mode 100644 site/themes/book/assets/menu-reset.js create mode 100644 site/themes/book/assets/mermaid.json create mode 100644 site/themes/book/assets/normalize.css create mode 100644 site/themes/book/assets/plugins/_numbered.scss create mode 100644 site/themes/book/assets/plugins/_scrollbars.scss create mode 100644 site/themes/book/assets/search-data.json create mode 100644 site/themes/book/assets/search.js create mode 100644 site/themes/book/assets/sw-register.js create mode 100644 site/themes/book/assets/sw.js create mode 100644 site/themes/book/assets/themes/_auto.scss create mode 100644 site/themes/book/assets/themes/_dark.scss create mode 100644 site/themes/book/assets/themes/_light.scss create mode 100644 site/themes/book/exampleSite/assets/_custom.scss create mode 100644 site/themes/book/exampleSite/assets/_variables.scss create mode 100644 site/themes/book/exampleSite/config.toml create mode 100644 site/themes/book/exampleSite/config.yaml create mode 100644 site/themes/book/exampleSite/content.bn/_index.md create mode 100644 site/themes/book/exampleSite/content.ru/_index.md create mode 100644 site/themes/book/exampleSite/content.zh/_index.md create mode 100644 site/themes/book/exampleSite/content/_index.md create mode 100644 site/themes/book/exampleSite/content/docs/example/_index.md create mode 100644 site/themes/book/exampleSite/content/docs/example/collapsed/3rd-level/4th-level.md create mode 100644 site/themes/book/exampleSite/content/docs/example/collapsed/3rd-level/_index.md create mode 100644 site/themes/book/exampleSite/content/docs/example/collapsed/_index.md create mode 100644 site/themes/book/exampleSite/content/docs/example/hidden.md create mode 100644 site/themes/book/exampleSite/content/docs/example/table-of-contents/_index.md create mode 100644 site/themes/book/exampleSite/content/docs/example/table-of-contents/with-toc.md create mode 100644 site/themes/book/exampleSite/content/docs/example/table-of-contents/without-toc.md create mode 100644 site/themes/book/exampleSite/content/docs/shortcodes/_index.md create mode 100644 site/themes/book/exampleSite/content/docs/shortcodes/buttons.md create mode 100644 site/themes/book/exampleSite/content/docs/shortcodes/columns.md create mode 100644 site/themes/book/exampleSite/content/docs/shortcodes/details.md create mode 100644 site/themes/book/exampleSite/content/docs/shortcodes/expand.md create mode 100644 site/themes/book/exampleSite/content/docs/shortcodes/hints.md create mode 100644 site/themes/book/exampleSite/content/docs/shortcodes/katex.md create mode 100644 site/themes/book/exampleSite/content/docs/shortcodes/mermaid.md create mode 100644 site/themes/book/exampleSite/content/docs/shortcodes/section/_index.md create mode 100644 site/themes/book/exampleSite/content/docs/shortcodes/section/first-page.md create mode 100644 site/themes/book/exampleSite/content/docs/shortcodes/section/second-page.md create mode 100644 site/themes/book/exampleSite/content/docs/shortcodes/tabs.md create mode 100644 site/themes/book/exampleSite/content/menu/index.md create mode 100644 site/themes/book/exampleSite/content/posts/_index.md create mode 100644 site/themes/book/exampleSite/content/posts/creating-a-new-theme.md create mode 100644 site/themes/book/exampleSite/content/posts/goisforlovers.md create mode 100644 site/themes/book/exampleSite/content/posts/hugoisforlovers.md create mode 100644 site/themes/book/exampleSite/content/posts/migrate-from-jekyll.md create mode 100644 site/themes/book/exampleSite/resources/_gen/assets/scss/book.scss_50fc8c04e12a2f59027287995557ceff.content create mode 100644 site/themes/book/exampleSite/resources/_gen/assets/scss/book.scss_50fc8c04e12a2f59027287995557ceff.json create mode 100644 site/themes/book/i18n/bn.yaml create mode 100644 site/themes/book/i18n/cn.yaml create mode 100644 site/themes/book/i18n/cs.yaml create mode 100644 site/themes/book/i18n/de.yaml create mode 100644 site/themes/book/i18n/en.yaml create mode 100644 site/themes/book/i18n/es.yaml create mode 100644 site/themes/book/i18n/fa.yaml create mode 100644 site/themes/book/i18n/fr.yaml create mode 100644 site/themes/book/i18n/it.yaml create mode 100644 site/themes/book/i18n/ja.yaml create mode 100644 site/themes/book/i18n/jp.yaml create mode 100644 site/themes/book/i18n/ko.yaml create mode 100644 site/themes/book/i18n/nb.yaml create mode 100644 site/themes/book/i18n/pt.yaml create mode 100644 site/themes/book/i18n/ru.yaml create mode 100644 site/themes/book/i18n/sv.yaml create mode 100644 site/themes/book/i18n/tr.yaml create mode 100644 site/themes/book/i18n/uk.yaml create mode 100644 site/themes/book/i18n/zh-TW.yaml create mode 100644 site/themes/book/i18n/zh.yaml create mode 100644 site/themes/book/images/screenshot.png create mode 100644 site/themes/book/images/tn.png create mode 100644 site/themes/book/layouts/404.html create mode 100644 site/themes/book/layouts/_default/_markup/render-heading.html create mode 100644 site/themes/book/layouts/_default/_markup/render-image.html create mode 100644 site/themes/book/layouts/_default/_markup/render-link.html create mode 100644 site/themes/book/layouts/_default/baseof.html create mode 100644 site/themes/book/layouts/_default/list.html create mode 100644 site/themes/book/layouts/_default/single.html create mode 100644 site/themes/book/layouts/partials/docs/brand.html create mode 100644 site/themes/book/layouts/partials/docs/comments.html create mode 100644 site/themes/book/layouts/partials/docs/date.html create mode 100644 site/themes/book/layouts/partials/docs/footer.html create mode 100644 site/themes/book/layouts/partials/docs/header.html create mode 100644 site/themes/book/layouts/partials/docs/html-head.html create mode 100644 site/themes/book/layouts/partials/docs/inject/body.html create mode 100644 site/themes/book/layouts/partials/docs/inject/content-after.html create mode 100644 site/themes/book/layouts/partials/docs/inject/content-before.html create mode 100644 site/themes/book/layouts/partials/docs/inject/footer.html create mode 100644 site/themes/book/layouts/partials/docs/inject/head.html create mode 100644 site/themes/book/layouts/partials/docs/inject/menu-after.html create mode 100644 site/themes/book/layouts/partials/docs/inject/menu-before.html create mode 100644 site/themes/book/layouts/partials/docs/inject/toc-after.html create mode 100644 site/themes/book/layouts/partials/docs/inject/toc-before.html create mode 100644 site/themes/book/layouts/partials/docs/languages.html create mode 100644 site/themes/book/layouts/partials/docs/menu-bundle.html create mode 100644 site/themes/book/layouts/partials/docs/menu-filetree.html create mode 100644 site/themes/book/layouts/partials/docs/menu-hugo.html create mode 100644 site/themes/book/layouts/partials/docs/menu.html create mode 100644 site/themes/book/layouts/partials/docs/post-meta.html create mode 100644 site/themes/book/layouts/partials/docs/search.html create mode 100644 site/themes/book/layouts/partials/docs/taxonomy.html create mode 100644 site/themes/book/layouts/partials/docs/title.html create mode 100644 site/themes/book/layouts/partials/docs/toc.html create mode 100644 site/themes/book/layouts/posts/list.html create mode 100644 site/themes/book/layouts/posts/single.html create mode 100644 site/themes/book/layouts/shortcodes/button.html create mode 100644 site/themes/book/layouts/shortcodes/columns.html create mode 100644 site/themes/book/layouts/shortcodes/details.html create mode 100644 site/themes/book/layouts/shortcodes/expand.html create mode 100644 site/themes/book/layouts/shortcodes/hint.html create mode 100644 site/themes/book/layouts/shortcodes/katex.html create mode 100644 site/themes/book/layouts/shortcodes/mermaid.html create mode 100644 site/themes/book/layouts/shortcodes/section.html create mode 100644 site/themes/book/layouts/shortcodes/tab.html create mode 100644 site/themes/book/layouts/shortcodes/tabs.html create mode 100644 site/themes/book/layouts/taxonomy/list.html create mode 100644 site/themes/book/layouts/taxonomy/taxonomy.html create mode 100644 site/themes/book/static/favicon.png create mode 100644 site/themes/book/static/favicon.svg create mode 100644 site/themes/book/static/flexsearch.min.js create mode 100644 site/themes/book/static/fonts/roboto-mono-v13-latin-regular.woff create mode 100644 site/themes/book/static/fonts/roboto-mono-v13-latin-regular.woff2 create mode 100644 site/themes/book/static/fonts/roboto-v27-latin-700.woff create mode 100644 site/themes/book/static/fonts/roboto-v27-latin-700.woff2 create mode 100644 site/themes/book/static/fonts/roboto-v27-latin-regular.woff create mode 100644 site/themes/book/static/fonts/roboto-v27-latin-regular.woff2 create mode 100644 site/themes/book/static/katex/auto-render.min.js create mode 100644 site/themes/book/static/katex/fonts/KaTeX_AMS-Regular.ttf create mode 100644 site/themes/book/static/katex/fonts/KaTeX_AMS-Regular.woff create mode 100644 site/themes/book/static/katex/fonts/KaTeX_AMS-Regular.woff2 create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Caligraphic-Bold.ttf create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Caligraphic-Bold.woff create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Caligraphic-Bold.woff2 create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Caligraphic-Regular.ttf create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Caligraphic-Regular.woff create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Caligraphic-Regular.woff2 create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Fraktur-Bold.ttf create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Fraktur-Bold.woff create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Fraktur-Bold.woff2 create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Fraktur-Regular.ttf create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Fraktur-Regular.woff create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Fraktur-Regular.woff2 create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Main-Bold.ttf create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Main-Bold.woff create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Main-Bold.woff2 create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Main-BoldItalic.ttf create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Main-BoldItalic.woff create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Main-BoldItalic.woff2 create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Main-Italic.ttf create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Main-Italic.woff create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Main-Italic.woff2 create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Main-Regular.ttf create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Main-Regular.woff create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Main-Regular.woff2 create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Math-BoldItalic.ttf create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Math-BoldItalic.woff create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Math-BoldItalic.woff2 create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Math-Italic.ttf create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Math-Italic.woff create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Math-Italic.woff2 create mode 100644 site/themes/book/static/katex/fonts/KaTeX_SansSerif-Bold.ttf create mode 100644 site/themes/book/static/katex/fonts/KaTeX_SansSerif-Bold.woff create mode 100644 site/themes/book/static/katex/fonts/KaTeX_SansSerif-Bold.woff2 create mode 100644 site/themes/book/static/katex/fonts/KaTeX_SansSerif-Italic.ttf create mode 100644 site/themes/book/static/katex/fonts/KaTeX_SansSerif-Italic.woff create mode 100644 site/themes/book/static/katex/fonts/KaTeX_SansSerif-Italic.woff2 create mode 100644 site/themes/book/static/katex/fonts/KaTeX_SansSerif-Regular.ttf create mode 100644 site/themes/book/static/katex/fonts/KaTeX_SansSerif-Regular.woff create mode 100644 site/themes/book/static/katex/fonts/KaTeX_SansSerif-Regular.woff2 create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Script-Regular.ttf create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Script-Regular.woff create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Script-Regular.woff2 create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Size1-Regular.ttf create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Size1-Regular.woff create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Size1-Regular.woff2 create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Size2-Regular.ttf create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Size2-Regular.woff create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Size2-Regular.woff2 create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Size3-Regular.ttf create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Size3-Regular.woff create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Size3-Regular.woff2 create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Size4-Regular.ttf create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Size4-Regular.woff create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Size4-Regular.woff2 create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Typewriter-Regular.ttf create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Typewriter-Regular.woff create mode 100644 site/themes/book/static/katex/fonts/KaTeX_Typewriter-Regular.woff2 create mode 100644 site/themes/book/static/katex/katex.min.css create mode 100644 site/themes/book/static/katex/katex.min.js create mode 100644 site/themes/book/static/mermaid.min.js create mode 100644 site/themes/book/static/svg/calendar.svg create mode 100644 site/themes/book/static/svg/edit.svg create mode 100644 site/themes/book/static/svg/menu.svg create mode 100644 site/themes/book/static/svg/toc.svg create mode 100644 site/themes/book/static/svg/translate.svg create mode 100644 site/themes/book/theme.toml diff --git a/site/README.md b/site/README.md new file mode 100644 index 0000000..f6a4c5e --- /dev/null +++ b/site/README.md @@ -0,0 +1,3 @@ +# golang + +source of go.xargin.com diff --git a/site/archetypes/default.md b/site/archetypes/default.md new file mode 100644 index 0000000..00e77bd --- /dev/null +++ b/site/archetypes/default.md @@ -0,0 +1,6 @@ +--- +title: "{{ replace .Name "-" " " | title }}" +date: {{ .Date }} +draft: true +--- + diff --git a/site/config.toml b/site/config.toml new file mode 100644 index 0000000..80f33af --- /dev/null +++ b/site/config.toml @@ -0,0 +1,6 @@ +baseURL = "http://go.xargin.com/" +languageCode = "en-us" +title = "Go 语言笔记" +theme = "book" +googleAnalytics = "G-KLR638LKEQ" + diff --git a/site/content/_index.md b/site/content/_index.md new file mode 100644 index 0000000..e5fb159 --- /dev/null +++ b/site/content/_index.md @@ -0,0 +1,13 @@ +--- +title: Go 语言笔记 +type: docs +--- + +# Go 语言笔记 + +## 为什么会有这本书 + +之前关于 Go 的输出主要散落在 blog 和 github 的 golang-notes 以及公众号中,内容比较分散。不方便阅读,到了这个时间点,本人已经使用 Go 已有超过 6 个年头,可以将之前的输出集合起来,进行系统化的输出了。 + +![nobody knows more about Go than me](/images/index/banner.jpg) + diff --git a/site/content/docs/_index.md b/site/content/docs/_index.md new file mode 100644 index 0000000..e69de29 diff --git a/site/content/docs/api_programming/_index.md b/site/content/docs/api_programming/_index.md new file mode 100644 index 0000000..7718841 --- /dev/null +++ b/site/content/docs/api_programming/_index.md @@ -0,0 +1,6 @@ +--- +title: API 开发 +weight: 5 +bookCollapseSection: true +draft: true +--- diff --git a/site/content/docs/api_programming/fasthttp.md b/site/content/docs/api_programming/fasthttp.md new file mode 100644 index 0000000..a886942 --- /dev/null +++ b/site/content/docs/api_programming/fasthttp.md @@ -0,0 +1,2 @@ +# FastHTTP + diff --git a/site/content/docs/api_programming/httprouter.md b/site/content/docs/api_programming/httprouter.md new file mode 100644 index 0000000..bc5ea15 --- /dev/null +++ b/site/content/docs/api_programming/httprouter.md @@ -0,0 +1 @@ +# http router 的实现 diff --git a/site/content/docs/api_programming/mysql.md b/site/content/docs/api_programming/mysql.md new file mode 100644 index 0000000..c64321e --- /dev/null +++ b/site/content/docs/api_programming/mysql.md @@ -0,0 +1 @@ +# mysql diff --git a/site/content/docs/api_programming/orm.md b/site/content/docs/api_programming/orm.md new file mode 100644 index 0000000..c69e1ca --- /dev/null +++ b/site/content/docs/api_programming/orm.md @@ -0,0 +1 @@ +# orm && sql builder diff --git a/site/content/docs/api_programming/validator.md b/site/content/docs/api_programming/validator.md new file mode 100644 index 0000000..76a423c --- /dev/null +++ b/site/content/docs/api_programming/validator.md @@ -0,0 +1 @@ +# validator diff --git a/site/content/docs/api_programming/viper.md b/site/content/docs/api_programming/viper.md new file mode 100644 index 0000000..63d973f --- /dev/null +++ b/site/content/docs/api_programming/viper.md @@ -0,0 +1 @@ +# viper diff --git a/site/content/docs/assembly/_index.md b/site/content/docs/assembly/_index.md new file mode 100644 index 0000000..18a2318 --- /dev/null +++ b/site/content/docs/assembly/_index.md @@ -0,0 +1,6 @@ +--- +title: 汇编基础 +weight: 1 +bookCollapseSection: true +--- + diff --git a/site/content/docs/assembly/assembly.md b/site/content/docs/assembly/assembly.md new file mode 100644 index 0000000..22fef9c --- /dev/null +++ b/site/content/docs/assembly/assembly.md @@ -0,0 +1,1006 @@ +--- +title: Plan9 汇编解析 +weight: 10 +--- + +# plan9 assembly 完全解析 + +众所周知,Go 使用了 Unix 老古董(误 们发明的 plan9 汇编。就算你对 x86 汇编有所了解,在 plan9 里还是有些许区别。说不定你在看代码的时候,偶然发现代码里的 SP 看起来是 SP,但它实际上不是 SP 的时候就抓狂了哈哈哈。 + +本文将对 plan9 汇编进行全面的介绍,同时解答你在接触 plan9 汇编时可能遇到的大部分问题。 + +本文所使用的平台是 linux amd64,因为不同的平台指令集和寄存器都不一样,所以没有办法共同探讨。这也是由汇编本身的性质决定的。 + +## 基本指令 + +### 栈调整 + +intel 或 AT&T 汇编提供了 push 和 pop 指令族,~~plan9 中没有 push 和 pop~~,plan9 中虽然有 push 和 pop 指令,但一般生成的代码中是没有的,我们看到的栈的调整大多是通过对硬件 SP 寄存器进行运算来实现的,例如: + +```go +SUBQ $0x18, SP // 对 SP 做减法,为函数分配函数栈帧 +... // 省略无用代码 +ADDQ $0x18, SP // 对 SP 做加法,清除函数栈帧 +``` + +通用的指令和 X64 平台差不多,下面分节详述。 + +### 数据搬运 + +常数在 plan9 汇编用 $num 表示,可以为负数,默认情况下为十进制。可以用 $0x123 的形式来表示十六进制数。 + +```go +MOVB $1, DI // 1 byte +MOVW $0x10, BX // 2 bytes +MOVD $1, DX // 4 bytes +MOVQ $-10, AX // 8 bytes +``` + +可以看到,搬运的长度是由 MOV 的后缀决定的,这一点与 intel 汇编稍有不同,看看类似的 X64 汇编: + +```asm +mov rax, 0x1 // 8 bytes +mov eax, 0x100 // 4 bytes +mov ax, 0x22 // 2 bytes +mov ah, 0x33 // 1 byte +mov al, 0x44 // 1 byte +``` + +plan9 的汇编的操作数的方向是和 intel 汇编相反的,与 AT&T 类似。 + +```go +MOVQ $0x10, AX ===== mov rax, 0x10 + | |------------| | + |------------------------| +``` + +不过凡事总有例外,如果想了解这种意外,可以参见参考资料中的 [1]。 + +### 常见计算指令 + +```go +ADDQ AX, BX // BX += AX +SUBQ AX, BX // BX -= AX +IMULQ AX, BX // BX *= AX +``` + +类似数据搬运指令,同样可以通过修改指令的后缀来对应不同长度的操作数。例如 ADDQ/ADDW/ADDL/ADDB。 + +### 条件跳转/无条件跳转 + +```go +// 无条件跳转 +JMP addr // 跳转到地址,地址可为代码中的地址,不过实际上手写不会出现这种东西 +JMP label // 跳转到标签,可以跳转到同一函数内的标签位置 +JMP 2(PC) // 以当前指令为基础,向前/后跳转 x 行 +JMP -2(PC) // 同上 + +// 有条件跳转 +JZ target // 如果 zero flag 被 set 过,则跳转 + +``` + + +### 指令集 + +可以参考源代码的 [arch](https://github.com/golang/arch/blob/master/x86/x86.csv) 部分。 + +额外提一句,Go 1.10 添加了大量的 SIMD 指令支持,所以在该版本以上的话,不像之前写那样痛苦了,也就是不用人肉填 byte 了。 + +## 寄存器 + +### 通用寄存器 + +amd64 的通用寄存器: + +```gdb +(lldb) reg read +General Purpose Registers: + rax = 0x0000000000000005 + rbx = 0x000000c420088000 + rcx = 0x0000000000000000 + rdx = 0x0000000000000000 + rdi = 0x000000c420088008 + rsi = 0x0000000000000000 + rbp = 0x000000c420047f78 + rsp = 0x000000c420047ed8 + r8 = 0x0000000000000004 + r9 = 0x0000000000000000 + r10 = 0x000000c420020001 + r11 = 0x0000000000000202 + r12 = 0x0000000000000000 + r13 = 0x00000000000000f1 + r14 = 0x0000000000000011 + r15 = 0x0000000000000001 + rip = 0x000000000108ef85 int`main.main + 213 at int.go:19 + rflags = 0x0000000000000212 + cs = 0x000000000000002b + fs = 0x0000000000000000 + gs = 0x0000000000000000 +``` + +在 plan9 汇编里都是可以使用的,应用代码层面会用到的通用寄存器主要是: rax, rbx, rcx, rdx, rdi, rsi, r8~r15 这 14 个寄存器,虽然 rbp 和 rsp 也可以用,不过 bp 和 sp 会被用来管理栈顶和栈底,最好不要拿来进行运算。 + +plan9 中使用寄存器不需要带 r 或 e 的前缀,例如 rax,只要写 AX 即可: + +```go +MOVQ $101, AX = mov rax, 101 +``` + +下面是通用通用寄存器的名字在 X64 和 plan9 中的对应关系: + +| X64 | rax | rbx| rcx | rdx | rdi | rsi | rbp | rsp | r8 | r9 | r10 | r11 | r12 | r13 | r14 | rip| +|--|--|--|--| --| --|--| --|--|--|--|--|--|--|--|--|--| +| Plan9 | AX | BX | CX | DX | DI | SI | BP | SP | R8 | R9 | R10 | R11 | R12 | R13 | R14 | PC | + +### 伪寄存器 + +Go 的汇编还引入了 4 个伪寄存器,援引官方文档的描述: + +>- `FP`: Frame pointer: arguments and locals. +>- `PC`: Program counter: jumps and branches. +>- `SB`: Static base pointer: global symbols. +>- `SP`: Stack pointer: the highest address within the local stack frame. + +官方的描述稍微有一些问题,我们对这些说明进行一点扩充: + +- FP: 使用形如 `symbol+offset(FP)` 的方式,引用函数的输入参数。例如 `arg0+0(FP)`,`arg1+8(FP)`,使用 FP 不加 symbol 时,无法通过编译,在汇编层面来讲,symbol 并没有什么用,加 symbol 主要是为了提升代码可读性。另外,官方文档虽然将伪寄存器 FP 称之为 frame pointer,实际上它根本不是 frame pointer,按照传统的 x86 的习惯来讲,frame pointer 是指向整个 stack frame 底部的 BP 寄存器。假如当前的 callee 函数是 add,在 add 的代码中引用 FP,该 FP 指向的位置不在 callee 的 stack frame 之内,而是在 caller 的 stack frame 上。具体可参见之后的 **栈结构** 一章。 +- PC: 实际上就是在体系结构的知识中常见的 pc 寄存器,在 x86 平台下对应 ip 寄存器,amd64 上则是 rip。除了个别跳转之外,手写 plan9 代码与 PC 寄存器打交道的情况较少。 +- SB: 全局静态基指针,一般用来声明函数或全局变量,在之后的函数知识和示例部分会看到具体用法。 +- SP: plan9 的这个 SP 寄存器指向当前栈帧的局部变量的开始位置,使用形如 `symbol+offset(SP)` 的方式,引用函数的局部变量。offset 的合法取值是 [-framesize, 0),注意是个左闭右开的区间。假如局部变量都是 8 字节,那么第一个局部变量就可以用 `localvar0-8(SP)` 来表示。这也是一个词不表意的寄存器。与硬件寄存器 SP 是两个不同的东西,在栈帧 size 为 0 的情况下,伪寄存器 SP 和硬件寄存器 SP 指向同一位置。手写汇编代码时,如果是 `symbol+offset(SP)` 形式,则表示伪寄存器 SP。如果是 `offset(SP)` 则表示硬件寄存器 SP。务必注意。对于编译输出(go tool compile -S / go tool objdump)的代码来讲,目前所有的 SP 都是硬件寄存器 SP,无论是否带 symbol。 + +我们这里对容易混淆的几点简单进行说明: + +1. 伪 SP 和硬件 SP 不是一回事,在手写代码时,伪 SP 和硬件 SP 的区分方法是看该 SP 前是否有 symbol。如果有 symbol,那么即为伪寄存器,如果没有,那么说明是硬件 SP 寄存器。 +2. SP 和 FP 的相对位置是会变的,所以不应该尝试用伪 SP 寄存器去找那些用 FP + offset 来引用的值,例如函数的入参和返回值。 +3. 官方文档中说的伪 SP 指向 stack 的 top,是有问题的。其指向的局部变量位置实际上是整个栈的栈底(除 caller BP 之外),所以说 bottom 更合适一些。 +4. 在 go tool objdump/go tool compile -S 输出的代码中,是没有伪 SP 和 FP 寄存器的,我们上面说的区分伪 SP 和硬件 SP 寄存器的方法,对于上述两个命令的输出结果是没法使用的。在编译和反汇编的结果中,只有真实的 SP 寄存器。 +5. FP 和 Go 的官方源代码里的 framepointer 不是一回事,源代码里的 framepointer 指的是 caller BP 寄存器的值,在这里和 caller 的伪 SP 是值是相等的。 + +以上说明看不懂也没关系,在熟悉了函数的栈结构之后再反复回来查看应该就可以明白了。 + +## 变量声明 + +在汇编里所谓的变量,一般是存储在 .rodata 或者 .data 段中的只读值。对应到应用层的话,就是已初始化过的全局的 const、var、static 变量/常量。 + +使用 DATA 结合 GLOBL 来定义一个变量。DATA 的用法为: + +```go +DATA symbol+offset(SB)/width, value +``` + +大多数参数都是字面意思,不过这个 offset 需要稍微注意。其含义是该值相对于符号 symbol 的偏移,而不是相对于全局某个地址的偏移。 + +使用 GLOBL 指令将变量声明为 global,额外接收两个参数,一个是 flag,另一个是变量的总大小。 + +```go +GLOBL divtab(SB), RODATA, $64 +``` + +GLOBL 必须跟在 DATA 指令之后,下面是一个定义了多个 readonly 的全局变量的完整例子: + +```go +DATA age+0x00(SB)/4, $18 // forever 18 +GLOBL age(SB), RODATA, $4 + +DATA pi+0(SB)/8, $3.1415926 +GLOBL pi(SB), RODATA, $8 + +DATA birthYear+0(SB)/4, $1988 +GLOBL birthYear(SB), RODATA, $4 +``` + +正如之前所说,所有符号在声明时,其 offset 一般都是 0。 + +有时也可能会想在全局变量中定义数组,或字符串,这时候就需要用上非 0 的 offset 了,例如: + +```go +DATA bio<>+0(SB)/8, $"oh yes i" +DATA bio<>+8(SB)/8, $"am here " +GLOBL bio<>(SB), RODATA, $16 +``` + +大部分都比较好理解,不过这里我们又引入了新的标记 `<>`,这个跟在符号名之后,表示该全局变量只在当前文件中生效,类似于 C 语言中的 static。如果在另外文件中引用该变量的话,会报 `relocation target not found` 的错误。 + +本小节中提到的 flag,还可以有其它的取值: +>- `NOPROF` = 1 + (For `TEXT` items.) Don't profile the marked function. This flag is deprecated. +>- `DUPOK` = 2 + It is legal to have multiple instances of this symbol in a single binary. The linker will choose one of the duplicates to use. +>- `NOSPLIT` = 4 + (For `TEXT` items.) Don't insert the preamble to check if the stack must be split. The frame for the routine, plus anything it calls, must fit in the spare space at the top of the stack segment. Used to protect routines such as the stack splitting code itself. +>- `RODATA` = 8 + (For `DATA` and `GLOBL` items.) Put this data in a read-only section. +>- `NOPTR` = 16 + (For `DATA` and `GLOBL` items.) This data contains no pointers and therefore does not need to be scanned by the garbage collector. +>- `WRAPPER` = 32 + (For `TEXT` items.) This is a wrapper function and should not count as disabling `recover`. +>- `NEEDCTXT` = 64 + (For `TEXT` items.) This function is a closure so it uses its incoming context register. + +当使用这些 flag 的字面量时,需要在汇编文件中 `#include "textflag.h"`。 + +## .s 和 .go 文件的全局变量互通 + +在 `.s` 文件中是可以直接使用 `.go` 中定义的全局变量的,看看下面这个简单的例子: + +refer.go: + +```go +package main + +var a = 999 +func get() int + +func main() { + println(get()) +} + +``` + +refer.s: + +```go +#include "textflag.h" + +TEXT ·get(SB), NOSPLIT, $0-8 + MOVQ ·a(SB), AX + MOVQ AX, ret+0(FP) + RET +``` + +·a(SB),表示该符号需要链接器来帮我们进行重定向(relocation),如果找不到该符号,会输出 `relocation target not found` 的错误。 + +例子比较简单,大家可以自行尝试。 + +## 函数声明 + +我们来看看一个典型的 plan9 的汇编函数的定义: + +```go +// func add(a, b int) int +// => 该声明定义在同一个 package 下的任意 .go 文件中 +// => 只有函数头,没有实现 +TEXT pkgname·add(SB), NOSPLIT, $0-8 + MOVQ a+0(FP), AX + MOVQ a+8(FP), BX + ADDQ AX, BX + MOVQ BX, ret+16(FP) + RET +``` + +为什么要叫 TEXT ?如果对程序数据在文件中和内存中的分段稍有了解的同学应该知道,我们的代码在二进制文件中,是存储在 .text 段中的,这里也就是一种约定俗成的起名方式。实际上在 plan9 中 TEXT 是一个指令,用来定义一个函数。除了 TEXT 之外还有前面变量声明说到的 DATA/GLOBL。 + +定义中的 pkgname 部分是可以省略的,非想写也可以写上。不过写上 pkgname 的话,在重命名 package 之后还需要改代码,所以推荐最好还是不要写。 + +中点 `·` 比较特殊,是一个 unicode 的中点,该点在 mac 下的输入方法是 `option+shift+9`。在程序被链接之后,所有的中点`·` 都会被替换为句号`.`,比如你的方法是 `runtime·main`,在编译之后的程序里的符号则是 `runtime.main`。嗯,看起来很变态。简单总结一下: + +```go + + 参数及返回值大小 + | + TEXT pkgname·add(SB),NOSPLIT,$32-32 + | | | + 包名 函数名 栈帧大小(局部变量+可能需要的额外调用函数的参数空间的总大小,但不包括调用其它函数时的 ret address 的大小) + +``` + +## 栈结构 + +下面是一个典型的函数的栈结构图: + +``` + + ----------------- + current func arg0 + ----------------- <----------- FP(pseudo FP) + caller ret addr + +---------------+ + | caller BP(*) | + ----------------- <----------- SP(pseudo SP,实际上是当前栈帧的 BP 位置) + | Local Var0 | + ----------------- + | Local Var1 | + ----------------- + | Local Var2 | + ----------------- - + | ........ | + ----------------- + | Local VarN | + ----------------- + | | + | | + | temporarily | + | unused space | + | | + | | + ----------------- + | call retn | + ----------------- + | call ret(n-1)| + ----------------- + | .......... | + ----------------- + | call ret1 | + ----------------- + | call argn | + ----------------- + | ..... | + ----------------- + | call arg3 | + ----------------- + | call arg2 | + |---------------| + | call arg1 | + ----------------- <------------ hardware SP 位置 + return addr + +---------------+ + + +``` + +从原理上来讲,如果当前函数调用了其它函数,那么 return addr 也是在 caller 的栈上的,不过往栈上插 return addr 的过程是由 CALL 指令完成的,在 RET 时,SP 又会恢复到图上位置。我们在计算 SP 和参数相对位置时,可以认为硬件 SP 指向的就是图上的位置。 + +图上的 caller BP,指的是 caller 的 BP 寄存器值,有些人把 caller BP 叫作 caller 的 frame pointer,实际上这个习惯是从 x86 架构沿袭来的。Go 的 asm 文档中把伪寄存器 FP 也称为 frame pointer,但是这两个 frame pointer 根本不是一回事。 + +此外需要注意的是,caller BP 是在编译期由编译器插入的,用户手写代码时,计算 frame size 时是不包括这个 caller BP 部分的。是否插入 caller BP 的主要判断依据是: + +1. 函数的栈帧大小大于 0 +2. 下述函数返回 true + +```go +func Framepointer_enabled(goos, goarch string) bool { + return framepointer_enabled != 0 && goarch == "amd64" && goos != "nacl" +} +``` + +如果编译器在最终的汇编结果中没有插入 caller BP(源代码中所称的 frame pointer)的情况下,伪 SP 和伪 FP 之间只有 8 个字节的 caller 的 return address,而插入了 BP 的话,就会多出额外的 8 字节。也就说伪 SP 和伪 FP 的相对位置是不固定的,有可能是间隔 8 个字节,也有可能间隔 16 个字节。并且判断依据会根据平台和 Go 的版本有所不同。 + +图上可以看到,FP 伪寄存器指向函数的传入参数的开始位置,因为栈是朝低地址方向增长,为了通过寄存器引用参数时方便,所以参数的摆放方向和栈的增长方向是相反的,即: + +```shell + FP +high ----------------------> low +argN, ... arg3, arg2, arg1, arg0 +``` + +假设所有参数均为 8 字节,这样我们就可以用 symname+0(FP) 访问第一个 参数,symname+8(FP) 访问第二个参数,以此类推。用伪 SP 来引用局部变量,原理上来讲差不多,不过因为伪 SP 指向的是局部变量的底部,所以 symname-8(SP) 表示的是第一个局部变量,symname-16(SP)表示第二个,以此类推。当然,这里假设局部变量都占用 8 个字节。 + +图的最上部的 caller return address 和 current func arg0 都是由 caller 来分配空间的。不算在当前的栈帧内。 + +因为官方文档本身较模糊,我们来一个函数调用的全景图,来看一下这些真假 SP/FP/BP 到底是个什么关系: + +``` + + caller + +------------------+ + | | + +----------------------> -------------------- + | | | + | | caller parent BP | + | BP(pseudo SP) -------------------- + | | | + | | Local Var0 | + | -------------------- + | | | + | | ....... | + | -------------------- + | | | + | | Local VarN | + -------------------- + caller stack frame | | + | callee arg2 | + | |------------------| + | | | + | | callee arg1 | + | |------------------| + | | | + | | callee arg0 | + | ----------------------------------------------+ FP(virtual register) + | | | | + | | return addr | parent return address | + +----------------------> +------------------+--------------------------- <-------------------------------+ + | caller BP | | + | (caller frame pointer) | | + BP(pseudo SP) ---------------------------- | + | | | + | Local Var0 | | + ---------------------------- | + | | + | Local Var1 | + ---------------------------- callee stack frame + | | + | ..... | + ---------------------------- | + | | | + | Local VarN | | + SP(Real Register) ---------------------------- | + | | | + | | | + | | | + | | | + | | | + +--------------------------+ <-------------------------------+ + + callee +``` + +## argsize 和 framesize 计算规则 + +### argsize + +在函数声明中: + +```go + TEXT pkgname·add(SB),NOSPLIT,$16-32 +``` + +前面已经说过 $16-32 表示 $framesize-argsize。Go 在函数调用时,参数和返回值都需要由 caller 在其栈帧上备好空间。callee 在声明时仍然需要知道这个 argsize。argsize 的计算方法是,参数大小求和+返回值大小求和,例如入参是 3 个 int64 类型,返回值是 1 个 int64 类型,那么这里的 argsize = sizeof(int64) * 4。 + +不过真实世界永远没有我们假设的这么美好,函数参数往往混合了多种类型,还需要考虑内存对齐问题。 + +如果不确定自己的函数签名需要多大的 argsize,可以通过简单实现一个相同签名的空函数,然后 go tool objdump 来逆向查找应该分配多少空间。 + +### framesize + +函数的 framesize 就稍微复杂一些了,手写代码的 framesize 不需要考虑由编译器插入的 caller BP,要考虑: + +1. 局部变量,及其每个变量的 size。 +2. 在函数中是否有对其它函数调用时,如果有的话,调用时需要将 callee 的参数、返回值考虑在内。虽然 return address(rip)的值也是存储在 caller 的 stack frame 上的,但是这个过程是由 CALL 指令和 RET 指令完成 PC 寄存器的保存和恢复的,在手写汇编时,同样也是不需要考虑这个 PC 寄存器在栈上所需占用的 8 个字节的。 +3. 原则上来说,调用函数时只要不把局部变量覆盖掉就可以了。稍微多分配几个字节的 framesize 也不会死。 +4. 在确保逻辑没有问题的前提下,你愿意覆盖局部变量也没有问题。只要保证进入和退出汇编函数时的 caller 和 callee 能正确拿到返回值就可以。 + +## 地址运算 + +地址运算也是用 lea 指令,英文原意为 `Load Effective Address`,amd64 平台地址都是 8 个字节,所以直接就用 LEAQ 就好: + +```go +LEAQ (BX)(AX*8), CX +// 上面代码中的 8 代表 scale +// scale 只能是 0、2、4、8 +// 如果写成其它值: +// LEAQ (BX)(AX*3), CX +// ./a.s:6: bad scale: 3 + +// 用 LEAQ 的话,即使是两个寄存器值直接相加,也必须提供 scale +// 下面这样是不行的 +// LEAQ (BX)(AX), CX +// asm: asmidx: bad address 0/2064/2067 +// 正确的写法是 +LEAQ (BX)(AX*1), CX + + +// 在寄存器运算的基础上,可以加上额外的 offset +LEAQ 16(BX)(AX*1), CX + +// 三个寄存器做运算,还是别想了 +// LEAQ DX(BX)(AX*8), CX +// ./a.s:13: expected end of operand, found ( +``` + +使用 LEAQ 的好处也比较明显,可以节省指令数。如果用基本算术指令来实现 LEAQ 的功能,需要两~三条以上的计算指令才能实现 LEAQ 的完整功能。 + +## 示例 + +### add/sub/mul + +math.go: + +```go +package main + +import "fmt" + +func add(a, b int) int // 汇编函数声明 + +func sub(a, b int) int // 汇编函数声明 + +func mul(a, b int) int // 汇编函数声明 + +func main() { + fmt.Println(add(10, 11)) + fmt.Println(sub(99, 15)) + fmt.Println(mul(11, 12)) +} +``` + +math.s: + +```go +#include "textflag.h" // 因为我们声明函数用到了 NOSPLIT 这样的 flag,所以需要将 textflag.h 包含进来 + +// func add(a, b int) int +TEXT ·add(SB), NOSPLIT, $0-24 + MOVQ a+0(FP), AX // 参数 a + MOVQ b+8(FP), BX // 参数 b + ADDQ BX, AX // AX += BX + MOVQ AX, ret+16(FP) // 返回 + RET + +// func sub(a, b int) int +TEXT ·sub(SB), NOSPLIT, $0-24 + MOVQ a+0(FP), AX + MOVQ b+8(FP), BX + SUBQ BX, AX // AX -= BX + MOVQ AX, ret+16(FP) + RET + +// func mul(a, b int) int +TEXT ·mul(SB), NOSPLIT, $0-24 + MOVQ a+0(FP), AX + MOVQ b+8(FP), BX + IMULQ BX, AX // AX *= BX + MOVQ AX, ret+16(FP) + RET + // 最后一行的空行是必须的,否则可能报 unexpected EOF +``` + +把这两个文件放在任意目录下,执行 `go build` 并运行就可以看到效果了。 + +### 伪寄存器 SP 、伪寄存器 FP 和硬件寄存器 SP + +来写一段简单的代码证明伪 SP、伪 FP 和硬件 SP 的位置关系。 +spspfp.s: + +```go +#include "textflag.h" + +// func output(int) (int, int, int) +TEXT ·output(SB), $8-48 + MOVQ 24(SP), DX // 不带 symbol,这里的 SP 是硬件寄存器 SP + MOVQ DX, ret3+24(FP) // 第三个返回值 + MOVQ perhapsArg1+16(SP), BX // 当前函数栈大小 > 0,所以 FP 在 SP 的上方 16 字节处 + MOVQ BX, ret2+16(FP) // 第二个返回值 + MOVQ arg1+0(FP), AX + MOVQ AX, ret1+8(FP) // 第一个返回值 + RET + +``` + +spspfp.go: + +```go +package main + +import ( + "fmt" +) + +func output(int) (int, int, int) // 汇编函数声明 + +func main() { + a, b, c := output(987654321) + fmt.Println(a, b, c) +} +``` + +执行上面的代码,可以得到输出: + +```shell +987654321 987654321 987654321 +``` + +和代码结合思考,可以知道我们当前的栈结构是这样的: + +```shell +------ +ret2 (8 bytes) +------ +ret1 (8 bytes) +------ +ret0 (8 bytes) +------ +arg0 (8 bytes) +------ FP +ret addr (8 bytes) +------ +caller BP (8 bytes) +------ pseudo SP +frame content (8 bytes) +------ hardware SP +``` + +本小节例子的 framesize 是大于 0 的,读者可以尝试修改 framesize 为 0,然后调整代码中引用伪 SP 和硬件 SP 时的 offset,来研究 framesize 为 0 时,伪 FP,伪 SP 和硬件 SP 三者之间的相对位置。 + +本小节的例子是为了告诉大家,伪 SP 和伪 FP 的相对位置是会变化的,手写时不应该用伪 SP 和 >0 的 offset 来引用数据,否则结果可能会出乎你的预料。 + +### 汇编调用非汇编函数 + +output.s: + +```go +#include "textflag.h" + +// func output(a,b int) int +TEXT ·output(SB), NOSPLIT, $24-24 + MOVQ a+0(FP), DX // arg a + MOVQ DX, 0(SP) // arg x + MOVQ b+8(FP), CX // arg b + MOVQ CX, 8(SP) // arg y + CALL ·add(SB) // 在调用 add 之前,已经把参数都通过物理寄存器 SP 搬到了函数的栈顶 + MOVQ 16(SP), AX // add 函数会把返回值放在这个位置 + MOVQ AX, ret+16(FP) // return result + RET + +``` + +output.go: + +```go +package main + +import "fmt" + +func add(x, y int) int { + return x + y +} + +func output(a, b int) int + +func main() { + s := output(10, 13) + fmt.Println(s) +} + +``` + +### 汇编中的循环 + +通过 DECQ 和 JZ 结合,可以实现高级语言里的循环逻辑: + +sum.s: + +```go +#include "textflag.h" + +// func sum(sl []int64) int64 +TEXT ·sum(SB), NOSPLIT, $0-32 + MOVQ $0, SI + MOVQ sl+0(FP), BX // &sl[0], addr of the first elem + MOVQ sl+8(FP), CX // len(sl) + INCQ CX // CX++, 因为要循环 len 次 + +start: + DECQ CX // CX-- + JZ done + ADDQ (BX), SI // SI += *BX + ADDQ $8, BX // 指针移动 + JMP start + +done: + // 返回地址是 24 是怎么得来的呢? + // 可以通过 go tool compile -S math.go 得知 + // 在调用 sum 函数时,会传入三个值,分别为: + // slice 的首地址、slice 的 len, slice 的 cap + // 不过我们这里的求和只需要 len,但 cap 依然会占用参数的空间 + // 就是 16(FP) + MOVQ SI, ret+24(FP) + RET +``` + +sum.go: + +```go +package main + +func sum([]int64) int64 + +func main() { + println(sum([]int64{1, 2, 3, 4, 5})) +} +``` + +## 扩展话题 + +### 标准库中的一些数据结构 + +#### 数值类型 + +标准库中的数值类型很多: + +1. int/int8/int16/int32/int64 +2. uint/uint8/uint16/uint32/uint64 +3. float32/float64 +4. byte/rune +5. uintptr + +这些类型在汇编中就是一段存储着数据的连续内存,只是内存长度不一样,操作的时候看好数据长度就行。 + +#### slice + +前面的例子已经说过了,slice 在传递给函数的时候,实际上会展开成三个参数: + +1. 首元素地址 +2. slice 的 len +3. slice 的 cap + +在汇编中处理时,只要知道这个原则那就很好办了,按顺序还是按索引操作随你开心。 + +#### string + +```go +package main + +//go:noinline +func stringParam(s string) {} + +func main() { + var x = "abcc" + stringParam(x) +} +``` + +用 `go tool compile -S` 输出其汇编: + +```go +0x001d 00029 (stringParam.go:11) LEAQ go.string."abcc"(SB), AX // 获取 RODATA 段中的字符串地址 +0x0024 00036 (stringParam.go:11) MOVQ AX, (SP) // 将获取到的地址放在栈顶,作为第一个参数 +0x0028 00040 (stringParam.go:11) MOVQ $4, 8(SP) // 字符串长度作为第二个参数 +0x0031 00049 (stringParam.go:11) PCDATA $0, $0 // gc 相关 +0x0031 00049 (stringParam.go:11) CALL "".stringParam(SB) // 调用 stringParam 函数 +``` + +在汇编层面 string 就是地址 + 字符串长度。 + +#### struct + +struct 在汇编层面实际上就是一段连续内存,在作为参数传给函数时,会将其展开在 caller 的栈上传给对应的 callee: + +struct.go + +```go +package main + +type address struct { + lng int + lat int +} + +type person struct { + age int + height int + addr address +} + +func readStruct(p person) (int, int, int, int) + +func main() { + var p = person{ + age: 99, + height: 88, + addr: address{ + lng: 77, + lat: 66, + }, + } + a, b, c, d := readStruct(p) + println(a, b, c, d) +} +``` + +struct.s + +```go +#include "textflag.h" + +TEXT ·readStruct(SB), NOSPLIT, $0-64 + MOVQ arg0+0(FP), AX + MOVQ AX, ret0+32(FP) + MOVQ arg1+8(FP), AX + MOVQ AX, ret1+40(FP) + MOVQ arg2+16(FP), AX + MOVQ AX, ret2+48(FP) + MOVQ arg3+24(FP), AX + MOVQ AX, ret3+56(FP) + RET +``` + +上述的程序会输出 99, 88, 77, 66,这表明即使是内嵌结构体,在内存分布上依然是连续的。 + +#### map + +通过对下述文件进行汇编(go tool compile -S),我们可以得到一个 map 在对某个 key 赋值时所需要做的操作: + +m.go: + +```go +package main + +func main() { + var m = map[int]int{} + m[43] = 1 + var n = map[string]int{} + n["abc"] = 1 + println(m, n) +} +``` + +看一看第七行的输出: + +```go +0x0085 00133 (m.go:7) LEAQ type.map[int]int(SB), AX +0x008c 00140 (m.go:7) MOVQ AX, (SP) +0x0090 00144 (m.go:7) LEAQ ""..autotmp_2+232(SP), AX +0x0098 00152 (m.go:7) MOVQ AX, 8(SP) +0x009d 00157 (m.go:7) MOVQ $43, 16(SP) +0x00a6 00166 (m.go:7) PCDATA $0, $1 +0x00a6 00166 (m.go:7) CALL runtime.mapassign_fast64(SB) +0x00ab 00171 (m.go:7) MOVQ 24(SP), AX +0x00b0 00176 (m.go:7) MOVQ $1, (AX) +``` + +前面我们已经分析过调用函数的过程,这里前几行都是在准备 runtime.mapassign_fast64(SB) 的参数。去 runtime 里看看这个函数的签名: + +```go +func mapassign_fast64(t *maptype, h *hmap, key uint64) unsafe.Pointer { +``` + +不用看函数的实现我们也大概能推测出函数输入参数和输出参数的关系了,把入参和汇编指令对应的话: + +```go +t *maptype +=> +LEAQ type.map[int]int(SB), AX +MOVQ AX, (SP) + +h *hmap +=> +LEAQ ""..autotmp_2+232(SP), AX +MOVQ AX, 8(SP) + +key uint64 +=> +MOVQ $43, 16(SP) +``` + +返回参数就是 key 对应的可以写值的内存地址,拿到该地址后我们把想要写的值写进去就可以了: + +```go +MOVQ 24(SP), AX +MOVQ $1, (AX) +``` + +整个过程还挺复杂的,我们手抄一遍倒也可以实现。不过还要考虑,不同类型的 map,实际上需要执行的 runtime 中的 assign 函数是不同的,感兴趣的同学可以汇编本节的示例自行尝试。 + +整体来讲,用汇编来操作 map 并不是一个明智的选择。 + +#### channel + +channel 在 runtime 也是比较复杂的数据结构,如果在汇编层面操作,实际上也是调用 runtime 中 chan.go 中的函数,和 map 比较类似,这里就不展开说了。 + +### 获取 goroutine id + +Go 的 goroutine 是一个叫 g 的结构体,内部有自己的唯一 id,不过 runtime 没有把这个 id 暴露出来,但不知道为什么有很多人就是想把这个 id 得到。于是就有了各种或其 goroutine id 的库。 + +在 struct 一小节我们已经提到,结构体本身就是一段连续的内存,我们知道起始地址和字段的偏移量的话,很容易就可以把这段数据搬运出来: + +go_tls.h: + +```go +#ifdef GOARCH_arm +#define LR R14 +#endif + +#ifdef GOARCH_amd64 +#define get_tls(r) MOVQ TLS, r +#define g(r) 0(r)(TLS*1) +#endif + +#ifdef GOARCH_amd64p32 +#define get_tls(r) MOVL TLS, r +#define g(r) 0(r)(TLS*1) +#endif + +#ifdef GOARCH_386 +#define get_tls(r) MOVL TLS, r +#define g(r) 0(r)(TLS*1) +#endif +``` + +goid.go: + +```go +package goroutineid +import "runtime" +var offsetDict = map[string]int64{ + // ... 省略一些行 + "go1.7": 192, + "go1.7.1": 192, + "go1.7.2": 192, + "go1.7.3": 192, + "go1.7.4": 192, + "go1.7.5": 192, + "go1.7.6": 192, + // ... 省略一些行 +} + +var offset = offsetDict[runtime.Version()] + +// GetGoID returns the goroutine id +func GetGoID() int64 { + return getGoID(offset) +} + +func getGoID(off int64) int64 +``` + +goid.s: + +```go +#include "textflag.h" +#include "go_tls.h" + +// func getGoID() int64 +TEXT ·getGoID(SB), NOSPLIT, $0-16 + get_tls(CX) + MOVQ g(CX), AX + MOVQ offset(FP), BX + LEAQ 0(AX)(BX*1), DX + MOVQ (DX), AX + MOVQ AX, ret+8(FP) + RET +``` + +这样就实现了一个简单的获取 struct g 中的 goid 字段的小 library,作为玩具放在这里: +>https://github.com/cch123/goroutineid + +### SIMD + +[SIMD](https://cch123.gitbooks.io/duplicate/content/part3/performance/simd-instruction-class.html) 是 Single Instruction, Multiple Data 的缩写,在 Intel 平台上的 SIMD 指令集先后为 SSE,AVX,AVX2,AVX512,这些指令集引入了标准以外的指令,和宽度更大的寄存器,例如: + +- 128 位的 XMM0~XMM31 寄存器。 +- 256 位的 YMM0~YMM31 寄存器。 +- 512 位的 ZMM0~ZMM31 寄存器。 + +这些寄存器的关系,类似 RAX,EAX,AX 之间的关系。指令方面可以同时对多组数据进行移动或者计算,例如: + +- movups : 把4个不对准的单精度值传送到xmm寄存器或者内存 +- movaps : 把4个对准的单精度值传送到xmm寄存器或者内存 + +上述指令,当我们将数组作为函数的入参时有很大概率会看到,例如: + +arr_par.go: + +```go +package main + +import "fmt" + +func pr(input [3]int) { + fmt.Println(input) +} + +func main() { + pr([3]int{1, 2, 3}) +} +``` + +go compile -S: + +```go +0x001d 00029 (arr_par.go:10) MOVQ "".statictmp_0(SB), AX +0x0024 00036 (arr_par.go:10) MOVQ AX, (SP) +0x0028 00040 (arr_par.go:10) MOVUPS "".statictmp_0+8(SB), X0 +0x002f 00047 (arr_par.go:10) MOVUPS X0, 8(SP) +0x0034 00052 (arr_par.go:10) CALL "".pr(SB) +``` + +可见,编译器在某些情况下已经考虑到了性能问题,帮助我们使用 SIMD 指令集来对数据搬运进行了优化。 + +因为 SIMD 这个话题本身比较广,这里就不展开细说了。 + +## 特别感谢 + +研究过程基本碰到不太明白的都去骚扰卓巨巨了,就是这位 https://mzh.io/ 大大。特别感谢他,给了不少线索和提示。 + +## 参考资料 + +1. https://quasilyte.github.io/blog/post/go-asm-complementary-reference/#external-resources +2. http://davidwong.fr/goasm +3. https://www.doxsey.net/blog/go-and-assembly +4. https://github.com/golang/go/files/447163/GoFunctionsInAssembly.pdf +5. https://golang.org/doc/asm + +参考资料[4]需要特别注意,在该 slide 中给出的 callee stack frame 中把 caller 的 return address 也包含进去了,个人认为不是很合适。 + + diff --git a/site/content/docs/assembly/exercises.md b/site/content/docs/assembly/exercises.md new file mode 100644 index 0000000..3c16b05 --- /dev/null +++ b/site/content/docs/assembly/exercises.md @@ -0,0 +1,7 @@ +--- +title: 习题 +weight: 2 +draft: true +--- + +# 习题 diff --git a/site/content/docs/bootstrap/_index.md b/site/content/docs/bootstrap/_index.md new file mode 100644 index 0000000..3832726 --- /dev/null +++ b/site/content/docs/bootstrap/_index.md @@ -0,0 +1,7 @@ +--- +title: Go 进程的启动流程 +weight: 2 +bookCollapseSection: true +draft: true +--- + diff --git a/site/content/docs/bootstrap/boot.md b/site/content/docs/bootstrap/boot.md new file mode 100644 index 0000000..a8fe587 --- /dev/null +++ b/site/content/docs/bootstrap/boot.md @@ -0,0 +1,7 @@ +--- +title: 启动流程 +weight: 2 +--- +# 启动流程 + + diff --git a/site/content/docs/bootstrap/elf.md b/site/content/docs/bootstrap/elf.md new file mode 100644 index 0000000..15a78a7 --- /dev/null +++ b/site/content/docs/bootstrap/elf.md @@ -0,0 +1 @@ +# elf 文件简介 diff --git a/site/content/docs/bootstrap/exercises.md b/site/content/docs/bootstrap/exercises.md new file mode 100644 index 0000000..a49ba48 --- /dev/null +++ b/site/content/docs/bootstrap/exercises.md @@ -0,0 +1,2 @@ +--- +--- \ No newline at end of file diff --git a/site/content/docs/compiler_and_linker/_index.md b/site/content/docs/compiler_and_linker/_index.md new file mode 100644 index 0000000..ce63fe2 --- /dev/null +++ b/site/content/docs/compiler_and_linker/_index.md @@ -0,0 +1,6 @@ +--- +title: 编译器与链接器 +weight: 3 +bookCollapseSection: true +draft: true +--- diff --git a/site/content/docs/compiler_and_linker/calling_convention.md b/site/content/docs/compiler_and_linker/calling_convention.md new file mode 100644 index 0000000..e0609f0 --- /dev/null +++ b/site/content/docs/compiler_and_linker/calling_convention.md @@ -0,0 +1,25 @@ +--- +title: 调用规约 +weight: 3 +draft: true +--- + +# 调用规约 + +早期版本的 Go 在向函数传递参数时,是使用栈的。 + +从 1.17 开始,调用函数时会尽量使用寄存器传参。 + +"".add STEXT size=121 args=0x10 locals=0x18 funcid=0x0 + 0x0000 00000 (add.go:8) TEXT "".add(SB), ABIInternal, $24-16 + 0x0000 00000 (add.go:8) CMPQ SP, 16(R14) + 0x0004 00004 (add.go:8) JLS 94 + 0x0006 00006 (add.go:8) SUBQ $24, SP + 0x005d 00093 (add.go:11) RET + 0x005e 00094 (add.go:11) NOP + 0x005e 00094 (add.go:8) MOVQ AX, 8(SP) + 0x0063 00099 (add.go:8) MOVQ BX, 16(SP) + 0x0068 00104 (add.go:8) CALL runtime.morestack_noctxt(SB) + 0x006d 00109 (add.go:8) MOVQ 8(SP), AX + 0x0072 00114 (add.go:8) MOVQ 16(SP), BX + 0x0077 00119 (add.go:8) JMP 0 diff --git a/site/content/docs/compiler_and_linker/compiler.md b/site/content/docs/compiler_and_linker/compiler.md new file mode 100644 index 0000000..6592c8e --- /dev/null +++ b/site/content/docs/compiler_and_linker/compiler.md @@ -0,0 +1,6 @@ +--- +title: 编译器 +weight: 3 +draft: true +--- +# 编译器 diff --git a/site/content/docs/compiler_and_linker/linker.md b/site/content/docs/compiler_and_linker/linker.md new file mode 100644 index 0000000..ea2d801 --- /dev/null +++ b/site/content/docs/compiler_and_linker/linker.md @@ -0,0 +1,6 @@ +--- +title: 链接器 +weight: 3 +draft: true +--- +# linker diff --git a/site/content/docs/data_structure/_index.md b/site/content/docs/data_structure/_index.md new file mode 100644 index 0000000..08b7aa5 --- /dev/null +++ b/site/content/docs/data_structure/_index.md @@ -0,0 +1,5 @@ +--- +title: 内置数据结构 +weight: 10 +bookCollapseSection: true +--- diff --git a/site/content/docs/data_structure/channel.md b/site/content/docs/data_structure/channel.md new file mode 100644 index 0000000..da9c943 --- /dev/null +++ b/site/content/docs/data_structure/channel.md @@ -0,0 +1,10 @@ +--- +title: channel +weight: 10 +bookCollapseSection: false +--- +# channel 实现 + +{{}} + +{{}} diff --git a/site/content/docs/data_structure/context.md b/site/content/docs/data_structure/context.md new file mode 100644 index 0000000..ed1e832 --- /dev/null +++ b/site/content/docs/data_structure/context.md @@ -0,0 +1,7 @@ +--- +title: 内置数据结构 +weight: 10 +bookCollapseSection: true +draft: true +--- +# context 的实现 diff --git a/site/content/docs/data_structure/interface.md b/site/content/docs/data_structure/interface.md new file mode 100644 index 0000000..be9c333 --- /dev/null +++ b/site/content/docs/data_structure/interface.md @@ -0,0 +1,7 @@ +--- +title: 内置数据结构 +weight: 10 +bookCollapseSection: true +draft: true +--- +# interface 实现 diff --git a/site/content/docs/data_structure/map.md b/site/content/docs/data_structure/map.md new file mode 100644 index 0000000..61f68ea --- /dev/null +++ b/site/content/docs/data_structure/map.md @@ -0,0 +1,27 @@ +--- +title: map +weight: 10 +bookCollapseSection: false +--- + +# map 实现 + +![map struct](/images/runtime/data_struct/map.png) + +## map 元素定位过程 + +![top hash](/images/runtime/data_struct/map_tophash.png) + +## 特权函数 + +![func translate](/images/runtime/data_struct/map_function_translate.png) + +## 函数翻译 + +![func translate](/images/runtime/data_struct/map_func_translate2.png) + +## map 遍历过程 + +{{}} + +{{}} diff --git a/site/content/docs/data_structure/semaphore.md b/site/content/docs/data_structure/semaphore.md new file mode 100644 index 0000000..73af24b --- /dev/null +++ b/site/content/docs/data_structure/semaphore.md @@ -0,0 +1,9 @@ +--- +title: 内置数据结构 +weight: 10 +bookCollapseSection: true +draft: true +--- + +# 信号量的实现 + diff --git a/site/content/docs/data_structure/string.md b/site/content/docs/data_structure/string.md new file mode 100644 index 0000000..49eee42 --- /dev/null +++ b/site/content/docs/data_structure/string.md @@ -0,0 +1,7 @@ +--- +title: 内置数据结构 +weight: 10 +bookCollapseSection: true +draft: true +--- +# string 不可变字符串 diff --git a/site/content/docs/data_structure/timer.md b/site/content/docs/data_structure/timer.md new file mode 100644 index 0000000..907fa4e --- /dev/null +++ b/site/content/docs/data_structure/timer.md @@ -0,0 +1,6 @@ +--- +title: 内置数据结构 +weight: 10 +bookCollapseSection: true +draft: true +--- \ No newline at end of file diff --git a/site/content/docs/ddd/_index.md b/site/content/docs/ddd/_index.md new file mode 100644 index 0000000..e28e493 --- /dev/null +++ b/site/content/docs/ddd/_index.md @@ -0,0 +1,6 @@ +--- +title: 领域驱动设计 +weight: 2 +bookCollapseSection: true +draft: true +--- diff --git a/site/content/docs/debug/_index.md b/site/content/docs/debug/_index.md new file mode 100644 index 0000000..a3feaba --- /dev/null +++ b/site/content/docs/debug/_index.md @@ -0,0 +1,6 @@ +--- +title: 调试工具 +weight: 5 +bookCollapseSection: true +draft: true +--- diff --git a/site/content/docs/debug/dlv_tutorial.md b/site/content/docs/debug/dlv_tutorial.md new file mode 100644 index 0000000..ae66d9d --- /dev/null +++ b/site/content/docs/debug/dlv_tutorial.md @@ -0,0 +1 @@ +# dlv tutorial diff --git a/site/content/docs/debug/ssa_debug.md b/site/content/docs/debug/ssa_debug.md new file mode 100644 index 0000000..3e5bba3 --- /dev/null +++ b/site/content/docs/debug/ssa_debug.md @@ -0,0 +1 @@ +# debug ssa func diff --git a/site/content/docs/design_patterns/_index.md b/site/content/docs/design_patterns/_index.md new file mode 100644 index 0000000..b45eae5 --- /dev/null +++ b/site/content/docs/design_patterns/_index.md @@ -0,0 +1,6 @@ +--- +title: 设计模式 +weight: 6 +bookCollapseSection: true +draft: true +--- diff --git a/site/content/docs/design_patterns/decorator.md b/site/content/docs/design_patterns/decorator.md new file mode 100644 index 0000000..7fad81a --- /dev/null +++ b/site/content/docs/design_patterns/decorator.md @@ -0,0 +1 @@ +# 装饰器模式 diff --git a/site/content/docs/optimization/_index.md b/site/content/docs/optimization/_index.md new file mode 100644 index 0000000..ff520c9 --- /dev/null +++ b/site/content/docs/optimization/_index.md @@ -0,0 +1,6 @@ +--- +title: 性能优化 +weight: 8 +bookCollapseSection: true +draft: true +--- diff --git a/site/content/docs/optimization/benchmark.md b/site/content/docs/optimization/benchmark.md new file mode 100644 index 0000000..58abd58 --- /dev/null +++ b/site/content/docs/optimization/benchmark.md @@ -0,0 +1 @@ +# benchmark 入门 diff --git a/site/content/docs/optimization/continuous_profiling.md b/site/content/docs/optimization/continuous_profiling.md new file mode 100644 index 0000000..ef9e56f --- /dev/null +++ b/site/content/docs/optimization/continuous_profiling.md @@ -0,0 +1 @@ +# continuous profiling diff --git a/site/content/docs/optimization/optimizations.md b/site/content/docs/optimization/optimizations.md new file mode 100644 index 0000000..c6ae860 --- /dev/null +++ b/site/content/docs/optimization/optimizations.md @@ -0,0 +1 @@ +# 性能优化 diff --git a/site/content/docs/optimization/pprof_tutorial.md b/site/content/docs/optimization/pprof_tutorial.md new file mode 100644 index 0000000..43a898b --- /dev/null +++ b/site/content/docs/optimization/pprof_tutorial.md @@ -0,0 +1 @@ +# pprof 指南 diff --git a/site/content/docs/quality/_index.md b/site/content/docs/quality/_index.md new file mode 100644 index 0000000..d6f49c9 --- /dev/null +++ b/site/content/docs/quality/_index.md @@ -0,0 +1,5 @@ +--- +title: 工程质量管控 +weight: 20 +bookCollapseSection: true +--- diff --git a/site/content/docs/quality/auto_review.md b/site/content/docs/quality/auto_review.md new file mode 100644 index 0000000..83193f6 --- /dev/null +++ b/site/content/docs/quality/auto_review.md @@ -0,0 +1,9 @@ +--- +title: 自动化 Code Review +weight: 1 +draft: true +--- + +# 自动化 Code Review + +## reviewdog diff --git a/site/content/docs/quality/custom_linter.md b/site/content/docs/quality/custom_linter.md new file mode 100644 index 0000000..7bd48f9 --- /dev/null +++ b/site/content/docs/quality/custom_linter.md @@ -0,0 +1,5 @@ +--- +title: 定制 linter +weight: 1 +draft: true +--- \ No newline at end of file diff --git a/site/content/docs/quality/refactoring.md b/site/content/docs/quality/refactoring.md new file mode 100644 index 0000000..d556488 --- /dev/null +++ b/site/content/docs/quality/refactoring.md @@ -0,0 +1,5 @@ +--- +title: 整洁代码 +weight: 1 +draft: true +--- diff --git a/site/content/docs/quality/static_analysis.md b/site/content/docs/quality/static_analysis.md new file mode 100644 index 0000000..21c2184 --- /dev/null +++ b/site/content/docs/quality/static_analysis.md @@ -0,0 +1,277 @@ +--- +title: 静态分析 +weight: 1 +--- + +# 静态分析 + +静态分析是通过扫描并解析用户代码,寻找代码中的潜在 bug 的一种手段。 + +静态分析一般会集成在项目上线的 CI 流程中,如果分析过程找到了 bug,会直接阻断上线,避免有问题的代码被部署到线上系统。从而在部署早期发现并修正潜在的问题。 + +## 社区常见 linter + +时至今日,社区已经有了丰富的 linter 资源供我们使用,本文会挑出一些常见 linter 进行说明。 + +### go lint + +go lint 是官方出的 linter,是 Go 语言最早期的 linter 了,其可以检查: + +* 导出函数是否有注释 +* 变量、函数、包命名不符合 Go 规范,有下划线 +* receiver 命名是否不符合规范 + +但这几年社区的 linter 蓬勃发展,所以这个项目也被官方 deprecated 掉了。其主要功能被另外一个 linter:revive[^1] 完全继承了。 + +### go vet + +go vet 也是官方提供的静态分析工具,其内置了锁拷贝检查、循环变量捕获问题、printf 参数不匹配等工具。 + +比如新手老手都很容易犯的 loop capture 错误: + +```go +package main + +func main() { + var a = map[int]int {1 : 1, 2: 3} + var b = map[int]*int{} + for k, r := range a { + go func() { + b[k] = &r + }() + } +} +``` + +go vet 会直接把你骂醒: + +```shell +~/test git:master ❯❯❯ go vet ./clo.go +# command-line-arguments +./clo.go:8:6: loop variable k captured by func literal +./clo.go:8:12: loop variable r captured by func literal +``` + +执行 go tool vet help 可以看到 go vet 已经内置的一些 linter。 + +```shell +~ ❯❯❯ go tool vet help +vet is a tool for static analysis of Go programs. + +vet examines Go source code and reports suspicious constructs, +such as Printf calls whose arguments do not align with the format +string. It uses heuristics that do not guarantee all reports are +genuine problems, but it can find errors not caught by the compilers. + +Registered analyzers: + + asmdecl report mismatches between assembly files and Go declarations + assign check for useless assignments + atomic check for common mistakes using the sync/atomic package + bools check for common mistakes involving boolean operators + buildtag check that +build tags are well-formed and correctly located + cgocall detect some violations of the cgo pointer passing rules + composites check for unkeyed composite literals + copylocks check for locks erroneously passed by value + errorsas report passing non-pointer or non-error values to errors.As + httpresponse check for mistakes using HTTP responses + loopclosure check references to loop variables from within nested functions + lostcancel check cancel func returned by context.WithCancel is called + nilfunc check for useless comparisons between functions and nil + printf check consistency of Printf format strings and arguments + shift check for shifts that equal or exceed the width of the integer + stdmethods check signature of methods of well-known interfaces + structtag check that struct field tags conform to reflect.StructTag.Get + tests check for common mistaken usages of tests and examples + unmarshal report passing non-pointer or non-interface values to unmarshal + unreachable check for unreachable code + unsafeptr check for invalid conversions of uintptr to unsafe.Pointer + unusedresult check for unused results of calls to some functions +``` + +默认情况下这些 linter 都是会跑的,当前很多 IDE 在代码修改时会自动执行 go vet,所以我们在写代码的时候一般就能发现这些错了。 + +但 `go vet` 还是应该集成到线上流程中,因为有些程序员的下限实在太低。 + +### errcheck + +Go 语言中的大多数函数返回字段中都是有 error 的: + +```go +func sayhello(wr http.ResponseWriter, r *http.Request) { + io.WriteString(wr, "hello") +} + +func main() { + http.HandleFunc("/", sayhello) + http.ListenAndServe(":1314", nil) // 这里返回的 err 没有处理 +} +``` + +这个例子中,我们没有处理 `http.ListenAndServe` 函数返回的 error 信息,这会导致我们的程序在启动时发生静默失败。 + +程序员往往会基于过往经验,对当前的场景产生过度自信,从而忽略掉一些常见函数的返回错误,这样的编程习惯经常为我们带来意外的线上事故。例如,规矩的写法是下面这样的: + +```go +data, err := getDataFromRPC() +if err != nil { + return nil, err +} + +// do business logic +age := data.age +``` + +而自信的程序员可能会写成这样: + +```go +data, _ := getDataFromRPC() + +// do business logic +age := data.age +``` + +如果底层 RPC 逻辑出错,上层的 data 是个空指针也是很正常的,如果底层函数返回的 err 非空时,我们不应该对其它字段做任何的假设。这里 data 完全有可能是个空指针,造成用户程序 panic。 + +errcheck 会强制我们在代码中检查并处理 err。 + +### gocyclo + +gocyclo 主要用来检查函数的圈复杂度。圈复杂度可以参考下面的定义: + +> 圈复杂度(Cyclomatic complexity)是一种代码复杂度的衡量标准,在 1976 年由 Thomas J. McCabe, Sr. 提出。在软件测试的概念里,圈复杂度用来衡量一个模块判定结构的复杂程度,数量上表现为线性无关的路径条数,即合理的预防错误所需测试的最少路径条数。圈复杂度大说明程序代码可能质量低且难于测试和维护,根据经验,程序的可能错误和高的圈复杂度有着很大关系。 + +看定义较为复杂但计算还是比较简单的,我们可以认为: + +* 一个 if,那么函数的圈复杂度要 + 1 +* 一个 switch 的 case,函数的圈复杂度要 + 1 +* 一个 for 循环,圈复杂度 + 1 +* 一个 && 或 ||,圈复杂度 + 1 + +在大多数语言中,若函数的圈复杂度超过了 10,那么我们就认为该函数较为复杂,需要做拆解或重构。部分场景可以使用表驱动的方式进行重构。 + +由于在 Go 语言中,我们使用 `if err != nil` 来处理错误,所以在一个函数中出现多个 `if err != nil` 是比较正常的,因此 Go 中函数复杂度的阈值可以稍微调高一些,15 是较为合适的值。 + +下面是在个人项目 elasticsql 中执行 gocyclo 的结果,输出 top 10 复杂的函数: + +```shell +~/g/s/g/c/elasticsql git:master ❯❯❯ gocyclo -top 10 ./ +23 elasticsql handleSelectWhere select_handler.go:289:1 +16 elasticsql handleSelectWhereComparisonExpr select_handler.go:220:1 +16 elasticsql handleSelect select_handler.go:11:1 +9 elasticsql handleGroupByFuncExprDateHisto select_agg_handler.go:82:1 +9 elasticsql handleGroupByFuncExprDateRange select_agg_handler.go:154:1 +8 elasticsql buildComparisonExprRightStr select_handler.go:188:1 +7 elasticsql TestSupported select_test.go:80:1 +7 elasticsql Convert main.go:28:1 +7 elasticsql handleGroupByFuncExpr select_agg_handler.go:215:1 +6 elasticsql handleSelectWhereOrExpr select_handler.go:157:1 +``` + +### bodyclose + +使用 bodyclose[^2] 可以帮我们检查在使用 HTTP 标准库时忘记关闭 http body 导致连接一直被占用的问题。 + +```go +resp, err := http.Get("http://example.com/") // Wrong case +if err != nil { + // handle error +} +body, err := ioutil.ReadAll(resp.Body) +``` + +像上面这样的例子是不对的,使用标准库很容易犯这样的错。bodyclose 可以直接检查出这个问题: + +```shell +# command-line-arguments +./httpclient.go:10:23: response body must be closed +``` + +所以必须要把 Body 关闭: + +```go +resp, err := http.Get("http://example.com/") +if err != nil { + // handle error +} +defer resp.Body.Close() // OK +body, err := ioutil.ReadAll(resp.Body) +``` + +HTTP 标准库的 API 设计的不太好,这个问题更好的避免方法是公司内部将 HTTP client 封装为 SDK,防止用户写出这样不 Close HTTP body 的代码。 + +### sqlrows + +与 HTTP 库设计类似,我们在面向数据库编程时,也会碰到 sql.Rows 忘记关闭的问题,导致连接大量被占用。sqlrows[^3] 这个 linter 能帮我们避免这个问题,先来看看错误的写法: + +```go +rows, err := db.QueryContext(ctx, "SELECT * FROM users") +if err != nil { + return nil, err +} + +for rows.Next() { + err = rows.Scan(...) + if err != nil { + return nil, err // NG: this return will not release a connection. + } +} +``` + +正确的写法需要在使用完后关闭 sql.Rows: + +```go +rows, err := db.QueryContext(ctx, "SELECT * FROM users") +if err != nil { + return nil, err +} +defer rows.Close() +``` + +与 HTTP 同理,公司内也应该将 DB 查询封装为合理的 SDK,不要让业务使用标准库中的 API,避免上述错误发生。 + +### funlen + +funlen[^4] 和 gocyclo 类似,但是这两个 linter 对代码复杂度的视角不太相同,gocyclo 更多关注函数中的逻辑分支,而 funlen 则重点关注函数的长度。默认函数超过 60 行和 40 条语句时,该 linter 即会报警。 + +## linter 集成工具 + +一个一个去社区里找 linter 来拼搭效率太低,当前社区里已经有了较好的集成工具,早期是 gometalinter,后来性能更好,功能更全的 golangci-lint 逐渐取而代之。目前 golangci-lint 是 Go 社区的绝对主流 linter。 + +### golangci-lint + +golangci-lint[^5] 能够通过配置来 enable 很多 linter,基本主流的都包含在内了。 + +在本节开头讲到的所有 linter 都可以在 golangci-lint 中进行配置, + +使用也较为简单,只要在项目目录执行 golangci-lint run . 即可。 + +```shell +~/g/s/g/c/elasticsql git:master ❯❯❯ golangci-lint run . +main.go:36:9: S1034: assigning the result of this type assertion to a variable (switch stmt := stmt.(type)) could eliminate type assertions in switch cases (gosimple) + switch stmt.(type) { + ^ +main.go:38:34: S1034(related information): could eliminate this type assertion (gosimple) + dsl, table, err = handleSelect(stmt.(*sqlparser.Select)) + ^ +main.go:40:23: S1034(related information): could eliminate this type assertion (gosimple) + return handleUpdate(stmt.(*sqlparser.Update)) + ^ +main.go:42:23: S1034(related information): could eliminate this type assertion (gosimple) + return handleInsert(stmt.(*sqlparser.Insert)) + ^ +select_handler.go:192:9: S1034: assigning the result of this type assertion to a variable (switch expr := expr.(type)) could eliminate type assertions in switch cases (gosimple) + switch expr.(type) { +``` + +## 参考资料 + +[^1]:https://revive.run/ + +[^2]:https://github.com/timakin/bodyclose + +[^3]:https://github.com/gostaticanalysis/sqlrows + +[^4]:https://github.com/ultraware/funlen + +[^5]:https://github.com/golangci/golangci-lint diff --git a/site/content/docs/runtime/_index.md b/site/content/docs/runtime/_index.md new file mode 100644 index 0000000..308963f --- /dev/null +++ b/site/content/docs/runtime/_index.md @@ -0,0 +1,5 @@ +--- +title: 运行时 +weight: 2 +bookCollapseSection: true +--- diff --git a/site/content/docs/runtime/memory_management/_index.md b/site/content/docs/runtime/memory_management/_index.md new file mode 100644 index 0000000..0b705e3 --- /dev/null +++ b/site/content/docs/runtime/memory_management/_index.md @@ -0,0 +1,4 @@ +--- +title: 内存管理 +weight: 10 +--- diff --git a/site/content/docs/runtime/memory_management/escape_analysis.md b/site/content/docs/runtime/memory_management/escape_analysis.md new file mode 100644 index 0000000..009a738 --- /dev/null +++ b/site/content/docs/runtime/memory_management/escape_analysis.md @@ -0,0 +1,7 @@ +--- +title: 逃逸分析 +weight: 10 +draft: true +--- + +# 逃逸分析 diff --git a/site/content/docs/runtime/memory_management/finalizer.md b/site/content/docs/runtime/memory_management/finalizer.md new file mode 100644 index 0000000..42b6c5e --- /dev/null +++ b/site/content/docs/runtime/memory_management/finalizer.md @@ -0,0 +1,7 @@ +--- +title: finalizer +weight: 10 +draft: true +--- + +# finalizer diff --git a/site/content/docs/runtime/memory_management/garbage_collection.md b/site/content/docs/runtime/memory_management/garbage_collection.md new file mode 100644 index 0000000..dbf5dfb --- /dev/null +++ b/site/content/docs/runtime/memory_management/garbage_collection.md @@ -0,0 +1,171 @@ +--- +title: 垃圾回收 +weight: 10 +--- + +# 垃圾回收(WIP) + +基于 Go 1.17。 + +![GC 的多个阶段](/images/runtime/memory/gcphases.jpg) + +## 三色抽象 + +在 Go 的代码中并无直接提示对象颜色的代码,对象的颜色主要由: + +* 对象对应的 gcmarkbit 位是否为 1 +* 对象的子对象是否已入队完成,若已完成,对象本身应该已经在队列外了 + +这两个状态来决定,三种颜色分别为: + +* 黑色:对象的 gcmarkbit 为 1,且对象已从队列中弹出 +* 灰色:对象的 gcmarkbit 为 1,其子对象未被处理完成,对象本身还在队列中 +* 白色:对象的 gcmarkbit 为 0,还未被标记流程所处理 + +## GC 触发 + +当前 GC 有三个触发点: + +* runtime.GC +* forcegchelper +* heap trigger + +## 并发标记流程 + + +{{}} + +{{}} + +### 关键组件及启动流程 + +worker 的三种模式 + +* 全职模式:gcMarkWorkerDedicatedMode +* 比例模式:gcMarkWorkerFractionalMode +* 兼职模式:gcMarkWorkerIdleMode + + +### gc roots + +垃圾回收的标记流程是将存活对象对应的 bit 位置为 1,堆上存活对象在内存中会形成森林结构,标记开始之前需要先将所有的根确定下来。 + +根对象包括四个来源: + +* bss 段 +* data 段 +* goroutine 栈 +* finalizer 关联的 special 类对象 + +### gcDrain + +gcDrain 是标记的核心流程 + +#### markroot + +根标记的流程很简单,就是根据 gcMarkrootPrepare 中计算出的索引值,遍历使用的根,执行 scanblock。 + +这些全局变量、goroutine 栈变量被扫描后,会被推到 gcw 队列中,成为灰色对象。 + +#### 标记过程中的队列 gcw && wbBuf && work.full + +{{}} + +{{}} + +#### 排空本地 gcw 和全局 work.full + +#### 标记终止流程 + +## mutator 与 marker 并发执行时的问题 + +### 对象丢失问题 + +GC 标记过程与 mutator 是并发执行的,所以在标记过程中,堆上对象的引用关系也会被动态修改,这时候可能有下面这种情况: + +{{}} + +{{}} + +丢失的对象会被认为是垃圾而被回收掉,这样在 mutator 后续访问该对象时便会发生内存错误。为了解决这个问题,mutator 在 GC 标记阶段需要打开 write barrier。所谓的 write barrier,就是在堆上指针发生修改前,插入一小段代码: + +![write barrier demo](/images/runtime/memory/write_barrier_demo.jpg) + +每次修改堆上指针都会判断 runtime.writeBarrier.enabled 是否为 true,如果为 true,那么在修改指针前需要调用 runtime.gcWriteBarrier。 + +Go 语言使用的 gc write barrier 是插入和删除的混合屏障,我们先来看看插入和删除屏障是什么。 + +#### dijistra 插入屏障 + +{{}} + +{{}} + +#### yuasa 删除屏障 + +{{}} + +{{}} + +#### Go 语言使用的混合屏障 + +runtime.gcWriteBarrier 是汇编函数,可以看到会将指针在修改前指向的值,和修改后指向的值都 push 到 wbBuf 中。 + +如果 wbBuf 满,那么就会将其 push 到 gcw 中,gcw 满了会 push 到全局的 work.full 中。 + +```go +TEXT runtime·gcWriteBarrier(SB),NOSPLIT,$112 + ...... + MOVQ (p_wbBuf+wbBuf_next)(R13), R12 + // Increment wbBuf.next position. + LEAQ 16(R12), R12 + MOVQ R12, (p_wbBuf+wbBuf_next)(R13) + CMPQ R12, (p_wbBuf+wbBuf_end)(R13) + // Record the write. + MOVQ AX, -16(R12) // Record value + MOVQ (DI), R13 + MOVQ R13, -8(R12) // Record *slot + // Is the buffer full? (flags set in CMPQ above) + JEQ flush +ret: + MOVQ 96(SP), R12 + MOVQ 104(SP), R13 + // Do the write. + MOVQ AX, (DI) + RET + +flush: + ...... + CALL runtime·wbBufFlush(SB) + ...... + JMP ret + ...... +``` + +## 清扫流程 sweep + +标记完成后,在 gcMarkTermination 中调用 gcSweep 会唤醒后台清扫 goroutine。该 goroutine 循环遍历所有 mspan,sweepone -> sweep 的主要操作为: + +* mspan.allocBits = mspan.gcMarkBits +* mspan.gcMarkBits clear + +清扫完成后会有三种情况: +* 该 mspan 全空了,那么调用 freeSpan 释放该 mspan 使其回归 arena,等待 scavenge 最终将这些 page 归还给操作系统 +* 尽管清扫了,但该 mspan 还是满的,那么将该 mspan 从 full 的 Unswept 链表移动到 full 的 Swept 部分 +* 清扫后 mspan 中出现了空槽,那么将该 mspan 从 full/partial 的 Unswept 链表移动到 partial 的 Swept 部分 + +### 协助清扫 + +TODO + +## 归还内存流程 scavenge + +bgscavenge -> pageAlloc.scavenge -> pageAlloc.scavengeOne -> pageAlloc.scavengeRangeLocked -> sysUnused -> madvise + + +### GOGC 及 GC 调步算法 + + +## debug.FreeOsMemory + +TODO \ No newline at end of file diff --git a/site/content/docs/runtime/memory_management/memory_alloctor.md b/site/content/docs/runtime/memory_management/memory_alloctor.md new file mode 100644 index 0000000..f149226 --- /dev/null +++ b/site/content/docs/runtime/memory_management/memory_alloctor.md @@ -0,0 +1,32 @@ +--- +title: 内存分配 +weight: 10 +--- + +# 内存分配 + +## bump/sequential allocator + +{{}} + + +{{}} + +## freelist allocator + +{{}} + +{{}} + +## dangling pointer + +{{}} + +{{}} + + +## tiny alloc + +{{}} + +{{}} \ No newline at end of file diff --git a/site/content/docs/runtime/netpoll/_index.md b/site/content/docs/runtime/netpoll/_index.md new file mode 100644 index 0000000..515c93b --- /dev/null +++ b/site/content/docs/runtime/netpoll/_index.md @@ -0,0 +1,5 @@ +--- +title: netpoll +weight: 10 +draft: true +--- diff --git a/site/content/docs/runtime/netpoll/basics.md b/site/content/docs/runtime/netpoll/basics.md new file mode 100644 index 0000000..8ec08a2 --- /dev/null +++ b/site/content/docs/runtime/netpoll/basics.md @@ -0,0 +1,127 @@ +--- +title: 网络编程基础 +weight: 1 +draft: true +--- + +# 网络编程基础 + +## 阻塞与非阻塞 + +在 linux 中,一切外部资源都被抽象为文件。网络连接也不例外,当我们在执行网络读、写操作时,可以使用 [fcntl](https://man7.org/linux/man-pages/man2/fcntl.2.html) 这个 syscall 来调整网络 fd 的阻塞模式: + +```c +int flag = fcntl(fd, F_GETFL, 0); +fcntl(fd, F_SETFL, flag|O_NONBLOCK); +``` + +fd 默认都是阻塞模式的,使用 fcntl 调整之后即为非阻塞模式。非阻塞与阻塞的区别可以用这张图来理解: + + + +## C 语言 epoll 示例 + +```c +#include +#include +#include +#include +#include +#include +#include + +#define BUF_SIZE 1024 +#define EPOLL_SIZE 50 + +void error_handling( char * msg ); + + +int main( int argc, char * argv[] ) +{ + int sock_fd, conn_fd; + struct sockaddr_in serv_addr, client_addr; + socklen_t addr_size; + int str_len, i; + char buf[BUF_SIZE]; + struct epoll_event * ep_events; + struct epoll_event event; + + int epfd, event_cnt; + + if ( argc != 2 ) + { + printf( "Usage : %s \n", argv[0] ); + exit( 1 ); + } + + sock_fd = socket( PF_INET, SOCK_STREAM, 0 ); + memset( &serv_addr, 0, sizeof(serv_addr) ); + serv_addr.sin_family = AF_INET; + serv_addr.sin_addr.s_addr = htonl( INADDR_ANY ); + serv_addr.sin_port = htons( atoi( argv[1] ) ); + + if ( bind( sock_fd, (struct sockaddr *) &serv_addr, sizeof(serv_addr) ) == -1 ) + { + error_handling( "bind error" ); + } + + if ( listen( sock_fd, 5 ) == -1 ) + { + error_handling( "listen error" ); + } + + epfd = epoll_create( EPOLL_SIZE ); + ep_events = malloc( sizeof(struct epoll_event) * EPOLL_SIZE ); + + event.events = EPOLLIN; + event.data.fd = sock_fd; + epoll_ctl( epfd, EPOLL_CTL_ADD, sock_fd, &event ); + + while ( 1 ) + { + event_cnt = epoll_wait( epfd, ep_events, EPOLL_SIZE, -1 ); + if ( event_cnt == -1 ) + { + puts( "epoll_wait error" ); + break; + } + + for ( i = 0; i < event_cnt; i++ ) + { + if ( ep_events[i].data.fd == sock_fd ) + { + addr_size = sizeof(client_addr); + conn_fd = accept( sock_fd, (struct sockaddr *) &client_addr, &addr_size ); + event.events = EPOLLIN; + event.data.fd = conn_fd; + epoll_ctl( epfd, EPOLL_CTL_ADD, conn_fd, &event ); + printf( "connected client : %d\n", conn_fd ); + } else { + str_len = read( ep_events[i].data.fd, buf, BUF_SIZE ); + if ( str_len == 0 ) /* close request EOF? */ + { + epoll_ctl( epfd, EPOLL_CTL_DEL, ep_events[i].data.fd, NULL ); + close( ep_events[i].data.fd ); /* close(conn_fd); */ + printf( "closed client: %d\n", ep_events[i].data.fd ); + } else { + write( ep_events[i].data.fd, buf, str_len ); /* echo result! */ + } + } + } + } + close( sock_fd ); + close( epfd ); + return(0); +} + + +void error_handling( char * msg ) +{ + fputs( msg, stderr ); + fputc( '\n', stderr ); +} +``` + +## 参考资料 + +https://chromium.googlesource.com/chromiumos/docs/+/master/constants/syscalls.md diff --git a/site/content/docs/runtime/netpoll/netpoll.md b/site/content/docs/runtime/netpoll/netpoll.md new file mode 100644 index 0000000..6996b89 --- /dev/null +++ b/site/content/docs/runtime/netpoll/netpoll.md @@ -0,0 +1,20 @@ +--- +title: Go 语言的网络抽象 +weight: 2 +--- + +# netpoller + +从网络编程基础篇我们知道,网络编程中涉及到的主要系统调用是下面这些: + +* socket +* bind +* listen +* accept +* epoll_create +* epoll_wait +* epoll_ctl +* read +* write + +因此在学习 Go 对网络层的抽象时,我们也是重点关注 Go 的 netpoller 中,这些 syscall 被封装进了哪个具体的流程里。 diff --git a/site/content/docs/runtime/scheduler/_index.md b/site/content/docs/runtime/scheduler/_index.md new file mode 100644 index 0000000..178e6d8 --- /dev/null +++ b/site/content/docs/runtime/scheduler/_index.md @@ -0,0 +1,5 @@ +--- +title: 调度器 +weight: 10 +--- + diff --git a/site/content/docs/runtime/scheduler/gmp.md b/site/content/docs/runtime/scheduler/gmp.md new file mode 100644 index 0000000..b3329eb --- /dev/null +++ b/site/content/docs/runtime/scheduler/gmp.md @@ -0,0 +1,7 @@ +--- +title: G、M、P 抽象 +weight: 10 +draft: true +--- + +# G、M、P 抽象 diff --git a/site/content/docs/runtime/scheduler/handling_blocking.md b/site/content/docs/runtime/scheduler/handling_blocking.md new file mode 100644 index 0000000..62174d5 --- /dev/null +++ b/site/content/docs/runtime/scheduler/handling_blocking.md @@ -0,0 +1,28 @@ +--- +title: 处理阻塞 +weight: 10 +draft: true +--- + +# 处理阻塞 + +在 Go 语言中,我们可能写出很多会阻塞执行的代码。 + +## channel 发送阻塞 + +![block on channel send](/images/runtime/block_on_channel_send.jpg) + + +## channel 接收阻塞 + +## net.Conn 读阻塞 + +## net.Conn 写阻塞 + +## time.Sleep 阻塞 + +## select 阻塞 + +## lock 阻塞 + + diff --git a/site/content/docs/runtime/scheduler/preemption.md b/site/content/docs/runtime/scheduler/preemption.md new file mode 100644 index 0000000..399a016 --- /dev/null +++ b/site/content/docs/runtime/scheduler/preemption.md @@ -0,0 +1,622 @@ +--- +title: 抢占 +weight: 2 +--- + +# 抢占 + +从 Go 1.14 开始,通过使用信号,Go 语言实现了调度和 GC 过程中的真“抢占“。 + +抢占流程由抢占的发起方向被抢占线程发送 SIGURG 信号。 + +当被抢占线程收到信号后,进入 SIGURG 的处理流程,将 asyncPreempt 的调用强制插入到用户当前执行的代码位置。 + +本节会对该过程进行详尽分析。 + +## 抢占发起的时机 + +抢占会在下列时机发生: + +* STW 期间 +* 在 P 上执行 safe point 函数期间 +* sysmon 后台监控期间 +* gc pacer 分配新的 dedicated worker 期间 +* panic 崩溃期间 + +{{< rawhtml >}} + +{{< /rawhtml >}} + +除了栈扫描,所有触发抢占最终都会去执行 preemptone 函数。栈扫描流程比较特殊: + +{{< rawhtml >}} + +{{< /rawhtml >}} + +从这些流程里,我们挑出三个来一探究竟。 + +### STW 抢占 + +![GC 的多个阶段](/images/runtime/memory/gcphases.jpg) + +上图是现在 Go 语言的 GC 流程图,在两个 STW 阶段都需要将正在执行的线程上的 running 状态的 goroutine 停下来。 + +```go +func stopTheWorldWithSema() { + ..... + preemptall() + ..... + // 等待剩余的 P 主动停下 + if wait { + for { + // wait for 100us, then try to re-preempt in case of any races + // 等待 100us,然后重新尝试抢占 + if notetsleep(&sched.stopnote, 100*1000) { + noteclear(&sched.stopnote) + break + } + preemptall() + } + } + +``` + + +### GC 栈扫描 + +goroutine 的栈是 GC 扫描期间的根,所有 markroot 中需要将用户的 goroutine 停下来,主要是 running 状态: + +```go +func markroot(gcw *gcWork, i uint32) { + // Note: if you add a case here, please also update heapdump.go:dumproots. + switch { + ...... + default: + // the rest is scanning goroutine stacks + var gp *g + ...... + + // scanstack must be done on the system stack in case + // we're trying to scan our own stack. + systemstack(func() { + stopped := suspendG(gp) + scanstack(gp, gcw) + resumeG(stopped) + }) + } +} +``` + +suspendG 中会调用 preemptM -> signalM 对正在执行的 goroutine 所在的线程发送抢占信号。 + +### sysmon 后台监控 + +```go +func sysmon() { + idle := 0 // how many cycles in succession we had not wokeup somebody + for { + ...... + // retake P's blocked in syscalls + // and preempt long running G's + if retake(now) != 0 { + idle = 0 + } else { + idle++ + } + } +} +``` + +执行 syscall 太久的,需要将 P 从 M 上剥离;运行用户代码太久的,需要抢占停止该 goroutine 执行。这里我们只看抢占 goroutine 的部分: + +```go +const forcePreemptNS = 10 * 1000 * 1000 // 10ms + +func retake(now int64) uint32 { + ...... + for i := 0; i < len(allp); i++ { + _p_ := allp[i] + s := _p_.status + if s == _Prunning || s == _Psyscall { + // Preempt G if it's running for too long. + t := int64(_p_.schedtick) + if int64(pd.schedtick) != t { + pd.schedtick = uint32(t) + pd.schedwhen = now + } else if pd.schedwhen+forcePreemptNS <= now { + preemptone(_p_) + } + } + ...... + } + ...... +} +``` + +## 协作式抢占原理 + +cooperative preemption 关键在于 cooperative,抢占的时机在各个版本实现差异不大,我们重点来看看这个协作过程。 + +### 函数头、函数尾插入的栈扩容检查 + +在 Go 语言中发生函数调用时,如果函数的 framesize > 0,说明在调用该函数时可能会发生 goroutine 的栈扩张,这时会在函数头、函数尾分别插入一段汇编码: + +```go +package main + +func main() { + add(1, 2) +} + +//go:noinline +func add(x, y int) (int, bool) { + var z = x + y + println(z) + return x + y, true +} +``` + +add 函数在使用 go tool compile -S 后会生成下面的结果: + +```go +// 这里的汇编代码使用 go1.14 生成 +// 在 go1.17 之后,函数的调用规约发生变化 +// 1.17 与以往版本头部的汇编代码也会有所不同,但逻辑保持一致 +"".add STEXT size=103 args=0x20 locals=0x18 + 0x0000 00000 (add.go:8) TEXT "".add(SB), ABIInternal, $24-32 + 0x0000 00000 (add.go:8) MOVQ (TLS), CX + 0x0009 00009 (add.go:8) CMPQ SP, 16(CX) + 0x000d 00013 (add.go:8) JLS 96 + ...... func body + 0x005f 00095 (add.go:11) RET + 0x0060 00096 (add.go:11) NOP + 0x0060 00096 (add.go:8) CALL runtime.morestack_noctxt(SB) + 0x0065 00101 (add.go:8) JMP 0 +``` + +TLS 中存储的是 G 的指针,偏移 16 字节即是 G 的结构体中的 stackguard0。由于 goroutine 的栈也是从高地址向低地址增长,因此这里检查当前 SP < stackguard0 的话,说明需要对栈进行扩容了。 + +### morestack 中的调度逻辑 + +```go +// morestack_noctxt 是个简单的汇编方法 +// 直接跳转到 morestack +TEXT runtime·morestack_noctxt(SB),NOSPLIT|NOFRAME,$0-0 + MOV ZERO, CTXT + JMP runtime·morestack(SB) + +TEXT runtime·morestack(SB),NOSPLIT,$0-0 + ...... + // 前面会切换将执行现场保存到 goroutine 的 gobuf 中 + // 并将执行栈切换到 g0 + // Call newstack on m->g0's stack. + MOVQ m_g0(BX), BX + MOVQ BX, g(CX) + MOVQ (g_sched+gobuf_sp)(BX), SP + CALL runtime·newstack(SB) + ...... + RET +``` + +morestack 会将 goroutine 的现场保存在当前 goroutine 的 gobuf 中,并将执行栈切换到 g0,然后在 g0 上执行 runtime.newstack。 + +在未实现信号抢占之前,用户的 g 到底啥时候能停下来,负责 GC 栈扫描的 goroutine 也不知道,所以 scanstack 也就只能设置一下 preemptscan 的标志位,最终栈扫描要 [newstack](https://github.com/golang/go/blob/2bc8d90fa21e9547aeb0f0ae775107dc8e05dc0a/src/runtime/stack.go#L917) 来配合,下面的 newstack 是 Go 1.13 版本的实现: + +```go +func newstack() { + thisg := getg() + gp := thisg.m.curg + preempt := atomic.Loaduintptr(&gp.stackguard0) == stackPreempt + if preempt { + if thisg.m.locks != 0 || thisg.m.mallocing != 0 || thisg.m.preemptoff != "" || thisg.m.p.ptr().status != _Prunning { + gp.stackguard0 = gp.stack.lo + _StackGuard + gogo(&gp.sched) // never return + } + } + + if preempt { + // 要和 scang 过程配合 + // 老版本的 newstack 和 gc scan 过程是有较重的耦合的 + casgstatus(gp, _Grunning, _Gwaiting) + if gp.preemptscan { + for !castogscanstatus(gp, _Gwaiting, _Gscanwaiting) { + } + if !gp.gcscandone { + gcw := &gp.m.p.ptr().gcw + // 注意这里,偶合了 GC 的 scanstack 逻辑代码 + scanstack(gp, gcw) + gp.gcscandone = true + } + gp.preemptscan = false + gp.preempt = false + casfrom_Gscanstatus(gp, _Gscanwaiting, _Gwaiting) + casgstatus(gp, _Gwaiting, _Grunning) + gp.stackguard0 = gp.stack.lo + _StackGuard + gogo(&gp.sched) // never return + } + + casgstatus(gp, _Gwaiting, _Grunning) + gopreempt_m(gp) // never return + } + ...... +} + +``` + +抢占成功后,当前的 goroutine 会被放在全局队列中: + +```go +func gopreempt_m(gp *g) { + goschedImpl(gp) +} + +func goschedImpl(gp *g) { + status := readgstatus(gp) + ...... + + casgstatus(gp, _Grunning, _Grunnable) + dropg() + lock(&sched.lock) + globrunqput(gp) // 将当前 goroutine 放进全局队列 + unlock(&sched.lock) + + schedule() // 当前线程重新进入调度循环 +} +``` + +### 信号式抢占实现后的 newstack + +在实现了信号式抢占之后,对于用户的 goroutine 何时中止有了一些预期,所以 newstack 就不需要耦合 scanstack 的逻辑了,新版的 [newstack](https://github.com/golang/go/blob/c3b47cb598e1ecdbbec110325d9d1979553351fc/src/runtime/stack.go#L948) 实现如下: + +```go +func newstack() { + thisg := getg() + gp := thisg.m.curg + preempt := atomic.Loaduintptr(&gp.stackguard0) == stackPreempt + + if preempt { + if !canPreemptM(thisg.m) { + // 让 goroutine 继续执行 + // 下次再抢占它 + gp.stackguard0 = gp.stack.lo + _StackGuard + gogo(&gp.sched) // never return + } + } + + if preempt { + // 当 GC 需要发起 goroutine 的栈扫描时 + // 会设置这个 preemptStop 为 true + // 这时候需要 goroutine 自己去 gopark + if gp.preemptStop { + preemptPark(gp) // never returns + } + + // 除了 GC 栈扫描以外的其它抢占场景走这个分支 + // 看起来就像 goroutine 自己调用了 runtime.Gosched 一样 + gopreempt_m(gp) // never return + } + ...... 后面就是正常的栈扩展逻辑了 +} +``` + +newstack 中会使用 [canPreemptM](https://github.com/golang/go/blob/287c5e8066396e953254d7980a80ec082edf11bd/src/runtime/preempt.go#L287)判断哪些场景适合抢占,哪些不适合。如果当前 goroutine 正在执行(即 status == running),并且满足下列任意其一: + +* 持有锁(主要是写锁,读锁其实判断不出来); +* 正在进行内存分配 +* preemptoff 非空 + +便不应该进行抢占,会在下一次进入到 newstack 时再进行判断。 + +## 非协作式抢占 + +非协作式抢占,就是通过信号处理来实现的。所以我们只要关注 SIGURG 的处理流程即可。 + +### 信号处理初始化 + +当 m0(即程序启动时的第一个线程)初始化时,会进行信号处理的初始化工作: + +```go +// mstartm0 implements part of mstart1 that only runs on the m0. +func mstartm0() { + initsig(false) +} + +// Initialize signals. +func initsig(preinit bool) { + for i := uint32(0); i < _NSIG; i++ { + setsig(i, funcPC(sighandler)) + } +} + +var sigtable = [...]sigTabT{ + ...... + /* 23 */ {_SigNotify + _SigIgn, "SIGURG: urgent condition on socket"}, + ...... +} + +``` + +最后都是执行 sigaction: + +```go +TEXT runtime·rt_sigaction(SB),NOSPLIT,$0-36 + MOVQ sig+0(FP), DI + MOVQ new+8(FP), SI + MOVQ old+16(FP), DX + MOVQ size+24(FP), R10 + MOVL $SYS_rt_sigaction, AX + SYSCALL + MOVL AX, ret+32(FP) + RET +``` + +与一般的 syscall 区别不大。 + +信号处理初始化的流程比较简单,就是给所有已知的需要处理的信号绑上 sighandler。 + +### 发送信号 + +```go +func preemptone(_p_ *p) bool { + mp := _p_.m.ptr() + gp := mp.curg + gp.preempt = true + gp.stackguard0 = stackPreempt + + // 向该线程发送 SIGURG 信号 + if preemptMSupported && debug.asyncpreemptoff == 0 { + _p_.preempt = true + preemptM(mp) + } + + return true +} +``` + +preemptM 的流程较为线性: + +```go +func preemptM(mp *m) { + if atomic.Cas(&mp.signalPending, 0, 1) { + signalM(mp, sigPreempt) + } +} + +func signalM(mp *m, sig int) { + tgkill(getpid(), int(mp.procid), sig) +} +``` + +最后使用 tgkill 这个 syscall 将信号发送给指定 id 的线程: + +```go +TEXT ·tgkill(SB),NOSPLIT,$0 + MOVQ tgid+0(FP), DI + MOVQ tid+8(FP), SI + MOVQ sig+16(FP), DX + MOVL $SYS_tgkill, AX + SYSCALL + RET +``` + +### 接收信号后的处理 + +当线程 m 接收到信号后,会从用户栈 g 切换到 gsignal 执行信号处理逻辑,即 sighandler 流程: + +```go +func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) { + _g_ := getg() + c := &sigctxt{info, ctxt} + + ...... + if sig == sigPreempt && debug.asyncpreemptoff == 0 { + doSigPreempt(gp, c) + } + ...... +} +``` + +如果收到的是抢占信号,那么执行 doSigPreempt 逻辑: + +```go +func doSigPreempt(gp *g, ctxt *sigctxt) { + // 检查当前 G 被抢占是否安全 + if wantAsyncPreempt(gp) { + if ok, newpc := isAsyncSafePoint(gp, ctxt.sigpc(), ctxt.sigsp(), ctxt.siglr()); ok { + // Adjust the PC and inject a call to asyncPreempt. + ctxt.pushCall(funcPC(asyncPreempt), newpc) + } + } + ...... +} +``` + +isAsyncSafePoint 中会把一些不应该抢占的场景过滤掉,具体包括: + +* 当前代码在汇编编写的函数中执行 +* 代码在 runtime,runtime/internal 或者 reflect 包中执行 + +doSigPreempt 代码中的 pushCall 是关键步骤: + +```go +func (c *sigctxt) pushCall(targetPC, resumePC uintptr) { + // Make it look like we called target at resumePC. + sp := uintptr(c.rsp()) + sp -= sys.PtrSize + *(*uintptr)(unsafe.Pointer(sp)) = resumePC + c.set_rsp(uint64(sp)) + c.set_rip(uint64(targetPC)) +} +``` + +pushCall 相当于将用户将要执行的下一条代码的地址直接 push 到栈上,并 jmp 到指定的 target 地址去执行代码: + +{{< columns >}} + +before + +```shell +----- PC = 0x123 +local var 1 +----- +local var 2 +----- <---- SP +``` + +<---> + +after + +```shell +----- PC = targetPC +local var 1 +----- +local var 2 +----- +prev PC = 0x123 +----- <---- SP +``` + +{{}} + +{{< columns>}} +{{< /columns >}} + +这里的 target 就是 asyncPreempt。 + +### asyncPreempt 执行流程分析 + +asyncPreempt 分为上半部分和下半部分,中间被 asyncPreempt2 隔开。上半部分负责将 goroutine 当前执行现场的所有寄存器都保存到当前的运行栈上。 + +下半部分负责在 asyncPreempt2 返回后将这些现场恢复出来。 + +```go +TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 + PUSHQ BP + MOVQ SP, BP + ...... 保存现场 1 + MOVQ AX, 0(SP) + MOVQ CX, 8(SP) + MOVQ DX, 16(SP) + MOVQ BX, 24(SP) + MOVQ SI, 32(SP) + ...... 保存现场 2 + MOVQ R15, 104(SP) + MOVUPS X0, 112(SP) + MOVUPS X1, 128(SP) + ...... + MOVUPS X15, 352(SP) + + CALL ·asyncPreempt2(SB) + + MOVUPS 352(SP), X15 + ...... 恢复现场 2 + MOVUPS 112(SP), X0 + MOVQ 104(SP), R15 + ...... 恢复现场 1 + MOVQ 8(SP), CX + MOVQ 0(SP), AX + ...... + RET + +``` + +asyncPreempt2 中有两个分支: + +```go +func asyncPreempt2() { + gp := getg() + gp.asyncSafePoint = true + if gp.preemptStop { // 这个 preemptStop 是在 GC 的栈扫描中才会设置为 true + mcall(preemptPark) + } else { // 除了栈扫描,其它抢占全部走这条分支 + mcall(gopreempt_m) + } + gp.asyncSafePoint = false +} +``` + +GC 栈扫描走 if 分支,除栈扫描以外所有情况均走 else 分支。 + +**栈扫描抢占流程** + +suspendG -> preemptM -> signalM 发信号。 + +sighandler -> asyncPreempt -> 保存执行现场 -> asyncPreempt2 -> **preemptPark** + +preemptPark 和 gopark 类似,挂起当前正在执行的 goroutine,该 goroutine 之前绑定的线程就可以继续执行调度循环了。 + +scanstack 执行完之后: + +resumeG -> ready -> runqput 会让之前被停下来的 goroutine 进当前 P 的队列或全局队列。 + +**其它流程** + +preemptone -> preemptM - signalM 发信号。 + +sighandler -> asyncPreempt -> 保存执行现场 -> asyncPreempt2 -> **gopreempt_m** + +gopreempt_m 会直接将被抢占的 goroutine 放进全局队列。 + +无论是栈扫描流程还是其它流程,当 goroutine 程序被调度到时,都是从汇编中的 `CALL ·asyncPreempt2(SB)` 的下一条指令开始执行的,即 asyncPreempt 汇编函数的下半部分。 + +这部分会将之前 goroutine 的现场完全恢复,就和抢占从来没有发生过一样。 + +## 动画演示 + +下面的动画是可以点的哦~ + +**sighandler 收到抢占信号,保存 PC,并将 PC 指向 asyncPreempt 的过程动画:** + +{{}} + +{{}} + + +**GC 栈扫描时的抢占和恢复过程:** + +{{}} + +{{}} + + +**除了栈扫描之外的抢占和恢复过程:** + +{{}} + +{{}} diff --git a/site/content/docs/runtime/scheduler/sched_loop.md b/site/content/docs/runtime/scheduler/sched_loop.md new file mode 100644 index 0000000..2ea1f2c --- /dev/null +++ b/site/content/docs/runtime/scheduler/sched_loop.md @@ -0,0 +1,34 @@ +--- +title: 调度流程(WIP) +weight: 1 +--- + +## 组件大图 + +![](/images/runtime/schedule/sche_big.png) + +## Go 的调度流程 + +我们可以认为 goroutine 的创建与调度循环是一个生产-消费流程。整个 go 程序的运行就是在不断地执行 goroutine 的生产与消费流程。 + +创建 goroutine 即是在创建任务,这些生产出来的 goroutine 可能会有三个去处,分别是: + +* p.runnext +* p.localrunq +* schedt.global runq + +按照执行权来讲,优先级是逐渐降低的。 + +调度循环会不断地从上面讲的三个目标中消费 goroutine,并执行。 + +## goroutine 生产 + +{{}} + +{{}} + +## goroutine 消费 + +{{}} + +{{}} diff --git a/site/content/docs/std_library/_index.md b/site/content/docs/std_library/_index.md new file mode 100644 index 0000000..e7e0be6 --- /dev/null +++ b/site/content/docs/std_library/_index.md @@ -0,0 +1,6 @@ +--- +title: 标准库 +weight: 5 +bookCollapseSection: true +draft: true +--- diff --git a/site/content/docs/sync/_index.md b/site/content/docs/sync/_index.md new file mode 100644 index 0000000..3e93950 --- /dev/null +++ b/site/content/docs/sync/_index.md @@ -0,0 +1,5 @@ +--- +title: 同步编程 +weight: 5 +bookCollapseSection: true +--- diff --git a/site/content/docs/sync/lock_free.md b/site/content/docs/sync/lock_free.md new file mode 100644 index 0000000..290f271 --- /dev/null +++ b/site/content/docs/sync/lock_free.md @@ -0,0 +1,8 @@ +--- +title: 同步编程 +weight: 5 +bookCollapseSection: true +draft: true +--- + +# lock free programming diff --git a/site/content/docs/sync/memory_barrier.md b/site/content/docs/sync/memory_barrier.md new file mode 100644 index 0000000..1da714b --- /dev/null +++ b/site/content/docs/sync/memory_barrier.md @@ -0,0 +1,7 @@ +--- +title: 同步编程 +weight: 5 +bookCollapseSection: true +draft: true +--- +# Memory Barrier diff --git a/site/content/docs/sync/patterns.md b/site/content/docs/sync/patterns.md new file mode 100644 index 0000000..e565ddc --- /dev/null +++ b/site/content/docs/sync/patterns.md @@ -0,0 +1,8 @@ +--- +title: 同步编程 +weight: 5 +bookCollapseSection: true +draft: true +--- + +# 并发编程模式 diff --git a/site/content/docs/sync/theory.md b/site/content/docs/sync/theory.md new file mode 100644 index 0000000..bdc60be --- /dev/null +++ b/site/content/docs/sync/theory.md @@ -0,0 +1,8 @@ +--- +title: 同步编程 +weight: 5 +bookCollapseSection: true +draft: true +--- + +# 并发编程理论 diff --git a/site/content/docs/sync/tools.md b/site/content/docs/sync/tools.md new file mode 100644 index 0000000..2662af1 --- /dev/null +++ b/site/content/docs/sync/tools.md @@ -0,0 +1,8 @@ +--- +title: 同步编程 +weight: 5 +bookCollapseSection: true +draft: true +--- + +# 并发工具 diff --git a/site/content/docs/sync/tools/_index.md b/site/content/docs/sync/tools/_index.md new file mode 100644 index 0000000..715279b --- /dev/null +++ b/site/content/docs/sync/tools/_index.md @@ -0,0 +1,6 @@ +--- +title: 同步工具 +weight: 5 +bookCollapseSection: true +--- + diff --git a/site/content/docs/sync/tools/syncPool.md b/site/content/docs/sync/tools/syncPool.md new file mode 100644 index 0000000..29e31da --- /dev/null +++ b/site/content/docs/sync/tools/syncPool.md @@ -0,0 +1,7 @@ +--- +title: sync.Pool[WIP] +weight: 5 +bookCollapseSection: true +--- + +![map struct](/images/sync/syncPool.png) diff --git a/site/content/docs/syntax_sugar/_index.md b/site/content/docs/syntax_sugar/_index.md new file mode 100644 index 0000000..11ae5e4 --- /dev/null +++ b/site/content/docs/syntax_sugar/_index.md @@ -0,0 +1,6 @@ +--- +title: 语法糖 +weight: 5 +bookCollapseSection: true +draft: true +--- diff --git a/site/content/docs/syntax_sugar/defer.md b/site/content/docs/syntax_sugar/defer.md new file mode 100644 index 0000000..45a4cc1 --- /dev/null +++ b/site/content/docs/syntax_sugar/defer.md @@ -0,0 +1 @@ +# defer 的实现 diff --git a/site/content/docs/system_programming/_index.md b/site/content/docs/system_programming/_index.md new file mode 100644 index 0000000..feb46d7 --- /dev/null +++ b/site/content/docs/system_programming/_index.md @@ -0,0 +1,6 @@ +--- +title: 系统编程 +weight: 5 +bookCollapseSection: true +draft: true +--- diff --git a/site/content/docs/system_programming/syscall.md b/site/content/docs/system_programming/syscall.md new file mode 100644 index 0000000..7bf6a30 --- /dev/null +++ b/site/content/docs/system_programming/syscall.md @@ -0,0 +1 @@ +# syscall 理论 diff --git a/site/content/docs/system_programming/vdso.md b/site/content/docs/system_programming/vdso.md new file mode 100644 index 0000000..63b9d7f --- /dev/null +++ b/site/content/docs/system_programming/vdso.md @@ -0,0 +1 @@ +# vdso syscall diff --git a/site/content/docs/third_party/_index.md b/site/content/docs/third_party/_index.md new file mode 100644 index 0000000..21f7d69 --- /dev/null +++ b/site/content/docs/third_party/_index.md @@ -0,0 +1,6 @@ +--- +title: 第三方库 +weight: 5 +bookCollapseSection: true +draft: true +--- diff --git a/site/content/docs/third_party/parser.md b/site/content/docs/third_party/parser.md new file mode 100644 index 0000000..a3c71f6 --- /dev/null +++ b/site/content/docs/third_party/parser.md @@ -0,0 +1,3 @@ +# parser + +## 使用 antlr 实现 parser diff --git a/site/content/docs/time/_index.md b/site/content/docs/time/_index.md new file mode 100644 index 0000000..3402c64 --- /dev/null +++ b/site/content/docs/time/_index.md @@ -0,0 +1,6 @@ +--- +title: 时间处理 +weight: 5 +bookCollapseSection: true +draft: true +--- diff --git a/site/content/docs/time/monotonic.md b/site/content/docs/time/monotonic.md new file mode 100644 index 0000000..7bf57e7 --- /dev/null +++ b/site/content/docs/time/monotonic.md @@ -0,0 +1 @@ +# monotonic diff --git a/site/layouts/shortcodes/rawhtml.html b/site/layouts/shortcodes/rawhtml.html new file mode 100644 index 0000000..b90bea2 --- /dev/null +++ b/site/layouts/shortcodes/rawhtml.html @@ -0,0 +1,2 @@ + +{{.Inner}} diff --git a/site/resources/_gen/assets/scss/book.scss_50fc8c04e12a2f59027287995557ceff.content b/site/resources/_gen/assets/scss/book.scss_50fc8c04e12a2f59027287995557ceff.content new file mode 100644 index 0000000..b200717 --- /dev/null +++ b/site/resources/_gen/assets/scss/book.scss_50fc8c04e12a2f59027287995557ceff.content @@ -0,0 +1 @@ +@charset "UTF-8";:root{--gray-100:#f8f9fa;--gray-200:#e9ecef;--gray-500:#adb5bd;--color-link:#0055bb;--color-visited-link:#8440f1;--body-background:white;--body-font-color:black;--icon-filter:none;--hint-color-info:#6bf;--hint-color-warning:#fd6;--hint-color-danger:#f66}/*!normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css*/html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button}button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{border-style:none;padding:0}button:-moz-focusring,[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}.flex{display:flex}.flex-auto{flex:auto}.flex-even{flex:1 1}.flex-wrap{flex-wrap:wrap}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.align-center{align-items:center}.mx-auto{margin:0 auto}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.hidden{display:none}input.toggle{height:0;width:0;overflow:hidden;opacity:0;position:absolute}.clearfix::after{content:"";display:table;clear:both}html{font-size:16px;scroll-behavior:smooth;touch-action:manipulation}body{min-width:20rem;color:var(--body-font-color);background:var(--body-background);letter-spacing:.33px;font-weight:400;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;box-sizing:border-box}body *{box-sizing:inherit}h1,h2,h3,h4,h5{font-weight:400}a{text-decoration:none;color:var(--color-link)}img{vertical-align:baseline}:focus{outline-style:auto;outline-color:currentColor;outline-color:-webkit-focus-ring-color}aside nav ul{padding:0;margin:0;list-style:none}aside nav ul li{margin:1em 0;position:relative}aside nav ul a{display:block}aside nav ul a:hover{opacity:.5}aside nav ul ul{padding-inline-start:1rem}ul.pagination{display:flex;justify-content:center;list-style-type:none}ul.pagination .page-item a{padding:1rem}.container{max-width:80rem;margin:0 auto}.book-icon{filter:var(--icon-filter)}.book-brand{margin-top:0}.book-brand img{height:1.5em;width:auto;vertical-align:middle;margin-inline-end:.5rem}.book-menu{flex:0 0 16rem;font-size:.875rem}.book-menu .book-menu-content{width:16rem;padding:1rem;background:var(--body-background);position:fixed;top:0;bottom:0;overflow-x:hidden;overflow-y:auto}.book-menu a,.book-menu label{color:inherit;cursor:pointer;word-wrap:break-word}.book-menu a.active{color:var(--color-link)}.book-menu input.toggle+label+ul{display:none}.book-menu input.toggle:checked+label+ul{display:block}.book-menu input.toggle+label::after{content:"▸"}.book-menu input.toggle:checked+label::after{content:"▾"}body[dir=rtl] .book-menu input.toggle+label::after{content:"◂"}body[dir=rtl] .book-menu input.toggle:checked+label::after{content:"▾"}.book-section-flat{margin-bottom:2rem}.book-section-flat:not(:first-child){margin-top:2rem}.book-section-flat>a,.book-section-flat>span,.book-section-flat>label{font-weight:bolder}.book-section-flat>ul{padding-inline-start:0}.book-page{min-width:20rem;flex-grow:1;padding:1rem}.book-post{margin-bottom:3rem}.book-header{display:none;margin-bottom:1rem}.book-header label{line-height:0}.book-header img.book-icon{height:1.5em;width:1.5em}.book-search{position:relative;margin:1rem 0;border-bottom:1px solid transparent}.book-search input{width:100%;padding:.5rem;border:0;border-radius:.25rem;background:var(--gray-100);color:var(--body-font-color)}.book-search input:required+.book-search-spinner{display:block}.book-search .book-search-spinner{position:absolute;top:0;margin:.5rem;margin-inline-start:calc(100% - 1.5rem);width:1rem;height:1rem;border:1px solid transparent;border-top-color:var(--body-font-color);border-radius:50%;animation:spin 1s ease infinite}@keyframes spin{100%{transform:rotate(360deg)}}.book-search small{opacity:.5}.book-toc{flex:0 0 16rem;font-size:.75rem}.book-toc .book-toc-content{width:16rem;padding:1rem;position:fixed;top:0;bottom:0;overflow-x:hidden;overflow-y:auto}.book-toc img{height:1em;width:1em}.book-toc nav>ul>li:first-child{margin-top:0}.book-footer{padding-top:1rem;font-size:.875rem}.book-footer img{height:1em;width:1em;margin-inline-end:.5rem}.book-comments{margin-top:1rem}.book-languages{position:relative;overflow:visible;padding:1rem;margin:-1rem}.book-languages ul{margin:0;padding:0;list-style:none}.book-languages ul li{white-space:nowrap;cursor:pointer}.book-languages:hover .book-languages-list,.book-languages:focus .book-languages-list,.book-languages:focus-within .book-languages-list{display:block}.book-languages .book-languages-list{display:none;position:absolute;bottom:100%;left:0;padding:.5rem 0;background:var(--body-background);box-shadow:0 0 .25rem rgba(0,0,0,.1)}.book-languages .book-languages-list li img{opacity:.25}.book-languages .book-languages-list li.active img,.book-languages .book-languages-list li:hover img{opacity:initial}.book-languages .book-languages-list a{color:inherit;padding:.5rem 1rem}.book-home{padding:1rem}.book-menu-content,.book-toc-content,.book-page,.book-header aside,.markdown{transition:.2s ease-in-out;transition-property:transform,margin,opacity,visibility;will-change:transform,margin,opacity}@media screen and (max-width:56rem){#menu-control,#toc-control{display:inline}.book-menu{visibility:hidden;margin-inline-start:-16rem;font-size:16px;z-index:1}.book-toc{display:none}.book-header{display:block}#menu-control:focus~main label[for=menu-control]{outline-style:auto;outline-color:currentColor;outline-color:-webkit-focus-ring-color}#menu-control:checked~main .book-menu{visibility:initial}#menu-control:checked~main .book-menu .book-menu-content{transform:translateX(16rem);box-shadow:0 0 .5rem rgba(0,0,0,.1)}#menu-control:checked~main .book-page{opacity:.25}#menu-control:checked~main .book-menu-overlay{display:block;position:absolute;top:0;bottom:0;left:0;right:0}#toc-control:focus~main label[for=toc-control]{outline-style:auto;outline-color:currentColor;outline-color:-webkit-focus-ring-color}#toc-control:checked~main .book-header aside{display:block}body[dir=rtl] #menu-control:checked~main .book-menu .book-menu-content{transform:translateX(-16rem)}}@media screen and (min-width:80rem){.book-page,.book-menu .book-menu-content,.book-toc .book-toc-content{padding:2rem 1rem}}@font-face{font-family:roboto;font-style:normal;font-weight:400;font-display:swap;src:local(""),url(fonts/roboto-v27-latin-regular.woff2)format("woff2"),url(fonts/roboto-v27-latin-regular.woff)format("woff")}@font-face{font-family:roboto;font-style:normal;font-weight:700;font-display:swap;src:local(""),url(fonts/roboto-v27-latin-700.woff2)format("woff2"),url(fonts/roboto-v27-latin-700.woff)format("woff")}@font-face{font-family:roboto mono;font-style:normal;font-weight:400;font-display:swap;src:local(""),url(fonts/roboto-mono-v13-latin-regular.woff2)format("woff2"),url(fonts/roboto-mono-v13-latin-regular.woff)format("woff")}body{font-family:roboto,sans-serif}code{font-family:roboto mono,monospace}@media print{.book-menu,.book-footer,.book-toc{display:none}.book-header,.book-header aside{display:block}main{display:block!important}}.markdown{line-height:1.6}.markdown>:first-child{margin-top:0}.markdown h1,.markdown h2,.markdown h3,.markdown h4,.markdown h5,.markdown h6{font-weight:400;line-height:1;margin-top:1.5em;margin-bottom:1rem}.markdown h1 a.anchor,.markdown h2 a.anchor,.markdown h3 a.anchor,.markdown h4 a.anchor,.markdown h5 a.anchor,.markdown h6 a.anchor{opacity:0;font-size:.75em;vertical-align:middle;text-decoration:none}.markdown h1:hover a.anchor,.markdown h1 a.anchor:focus,.markdown h2:hover a.anchor,.markdown h2 a.anchor:focus,.markdown h3:hover a.anchor,.markdown h3 a.anchor:focus,.markdown h4:hover a.anchor,.markdown h4 a.anchor:focus,.markdown h5:hover a.anchor,.markdown h5 a.anchor:focus,.markdown h6:hover a.anchor,.markdown h6 a.anchor:focus{opacity:initial}.markdown h4,.markdown h5,.markdown h6{font-weight:bolder}.markdown h5{font-size:.875em}.markdown h6{font-size:.75em}.markdown b,.markdown optgroup,.markdown strong{font-weight:bolder}.markdown a{text-decoration:none}.markdown a:hover{text-decoration:underline}.markdown a:visited{color:var(--color-visited-link)}.markdown img{max-width:100%}.markdown code{padding:0 .25rem;background:var(--gray-200);border-radius:.25rem;font-size:.875em}.markdown pre{padding:1rem;background:var(--gray-100);border-radius:.25rem;overflow-x:auto}.markdown pre code{padding:0;background:0 0}.markdown blockquote{margin:1rem 0;padding:.5rem 1rem .5rem .75rem;border-inline-start:.25rem solid var(--gray-200);border-radius:.25rem}.markdown blockquote :first-child{margin-top:0}.markdown blockquote :last-child{margin-bottom:0}.markdown table{overflow:auto;display:block;border-spacing:0;border-collapse:collapse;margin-top:1rem;margin-bottom:1rem}.markdown table tr th,.markdown table tr td{padding:.5rem 1rem;border:1px solid var(--gray-200)}.markdown table tr:nth-child(2n){background:var(--gray-100)}.markdown hr{height:1px;border:none;background:var(--gray-200)}.markdown ul,.markdown ol{padding-inline-start:2rem}.markdown dl dt{font-weight:bolder;margin-top:1rem}.markdown dl dd{margin-inline-start:0;margin-bottom:1rem}.markdown .highlight table tr td:nth-child(1) pre{margin:0;padding-inline-end:0}.markdown .highlight table tr td:nth-child(2) pre{margin:0;padding-inline-start:0}.markdown details{padding:1rem;border:1px solid var(--gray-200);border-radius:.25rem}.markdown details summary{line-height:1;padding:1rem;margin:-1rem;cursor:pointer}.markdown details[open] summary{margin-bottom:0}.markdown figure{margin:1rem 0}.markdown figure figcaption p{margin-top:0}.markdown-inner>:first-child{margin-top:0}.markdown-inner>:last-child{margin-bottom:0}.markdown .book-expand{margin-top:1rem;margin-bottom:1rem;border:1px solid var(--gray-200);border-radius:.25rem;overflow:hidden}.markdown .book-expand .book-expand-head{background:var(--gray-100);padding:.5rem 1rem;cursor:pointer}.markdown .book-expand .book-expand-content{display:none;padding:1rem}.markdown .book-expand input[type=checkbox]:checked+.book-expand-content{display:block}.markdown .book-tabs{margin-top:1rem;margin-bottom:1rem;border:1px solid var(--gray-200);border-radius:.25rem;overflow:hidden;display:flex;flex-wrap:wrap}.markdown .book-tabs label{display:inline-block;padding:.5rem 1rem;border-bottom:1px transparent;cursor:pointer}.markdown .book-tabs .book-tabs-content{order:999;width:100%;border-top:1px solid var(--gray-100);padding:1rem;display:none}.markdown .book-tabs input[type=radio]:checked+label{border-bottom:1px solid var(--color-link)}.markdown .book-tabs input[type=radio]:checked+label+.book-tabs-content{display:block}.markdown .book-tabs input[type=radio]:focus+label{outline-style:auto;outline-color:currentColor;outline-color:-webkit-focus-ring-color}.markdown .book-columns{margin-left:-1rem;margin-right:-1rem}.markdown .book-columns>div{margin:1rem 0;min-width:10rem;padding:0 1rem}.markdown a.book-btn{display:inline-block;font-size:.875rem;color:var(--color-link);line-height:2rem;padding:0 1rem;border:1px solid var(--color-link);border-radius:.25rem;cursor:pointer}.markdown a.book-btn:hover{text-decoration:none}.markdown .book-hint.info{border-color:#6bf;background-color:rgba(102,187,255,.1)}.markdown .book-hint.warning{border-color:#fd6;background-color:rgba(255,221,102,.1)}.markdown .book-hint.danger{border-color:#f66;background-color:rgba(255,102,102,.1)} \ No newline at end of file diff --git a/site/resources/_gen/assets/scss/book.scss_50fc8c04e12a2f59027287995557ceff.json b/site/resources/_gen/assets/scss/book.scss_50fc8c04e12a2f59027287995557ceff.json new file mode 100644 index 0000000..112aa6d --- /dev/null +++ b/site/resources/_gen/assets/scss/book.scss_50fc8c04e12a2f59027287995557ceff.json @@ -0,0 +1 @@ +{"Target":"book.min.958cea7827621d6fbcb3acf091344c3e44e3d2a9428f9c3c38bb9eb37bf8c45d.css","MediaType":"text/css","Data":{"Integrity":"sha256-lYzqeCdiHW+8s6zwkTRMPkTj0qlCj5w8OLues3v4xF0="}} \ No newline at end of file diff --git a/site/static/images/index/banner.jpg b/site/static/images/index/banner.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8e14478daed88dcd70f991d9bb8c8d1f585113ad GIT binary patch literal 144283 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 @Z=*7|!*t{3( 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 Date: Fri, 25 Dec 2020 10:18:55 +0800 Subject: [PATCH 040/120] add monotonic --- monotonic.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 monotonic.md diff --git a/monotonic.md b/monotonic.md new file mode 100644 index 0000000..78af96e --- /dev/null +++ b/monotonic.md @@ -0,0 +1,8 @@ +# monotonic time + +https://go.googlesource.com/proposal/+/master/design/12914-monotonic.md + + +time.Since + +time.Now From 8f8ad1277b92f34f12eae115d78beef02e0d7a61 Mon Sep 17 00:00:00 2001 From: Xargin Date: Sat, 26 Dec 2020 23:47:16 +0800 Subject: [PATCH 041/120] update signal --- 1.14/signal_based_preemption.md | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/1.14/signal_based_preemption.md b/1.14/signal_based_preemption.md index 1643c90..2274dee 100644 --- a/1.14/signal_based_preemption.md +++ b/1.14/signal_based_preemption.md @@ -2,7 +2,9 @@ ## 信号概念 -信号是一种发给进程的通知,以告知有事件发生。有时信号也被称为软件中断。从中断用户控制流来说,信号和硬件中断是类似的;在大多场景下,信号何时到达进程是无法预测的。 +信号是一种在有事件发生时,发送给进程的通知。也可以认为信号是进程之间的一种通信机制。 + +信号也被称为软件中断。从中断用户控制流来说,信号和硬件中断是类似的;在大多场景下,信号何时到达进程是无法预测的。 信号可以由内核发给用户进程,可以用户进程发给自己,也可以用户进程发给其它的用户进程。 @@ -10,7 +12,7 @@ ## 信号是易失的么 -有一种传言认为信号是易失的,但实际上在早期的 unix 实现中才是易失的。这里的易失说的是完全没有把信号传递给相应的进程。由 POSIX.1-1990 标准定义之后实现的信号基本都是可靠信号了。 +有一种传言认为信号是易失的,但实际上在早期的 unix 实现中才是易失的。这里的易失说的是信号在发送给进程的过程中完全丢失。由 POSIX.1-1990 标准定义之后实现的信号基本都是可靠信号了。 但不易失不代表你向同一个进程重复发送相同的信号 100 次,进程就能收到 100 次这个信号,这是怎么回事? @@ -30,10 +32,26 @@ TODO,画个图 信号处理涉及几个 syscall: -### tigkill +### tgkill 向某个进程的某个线程发信号。 +> tgkill() sends the signal sig to the thread with the thread ID tid in the thread group tgid. (By contrast, kill(2) can be used to send a signal only to a process (i.e., thread group) as a whole, and the signal will be delivered to an arbitrary thread within that process.) + +> tkill() is an obsolete predecessor to tgkill(). It allows only the target thread ID to be specified, which may result in the wrong thread being signaled if a thread terminates and its thread ID is recycled. Avoid using this system call. + +tgkill 是 thread group kill 的缩写,可以给某个进程的某个线程发信号,在 tgkill 之前有一个不带进程 id 的 tkill,不过这个 syscall 可能会杀掉错误的进程,比如正好在发送之前,老进程被销毁,而新进程使用了同样的线程 id。也就是说现在使用的话,都是 tgkill 这个 syscall 了。 + +与 tgkill 相比,kill 则只能给进程整体发信号,信号可能被进程内的任意线程处理。平常使用比较多的 kill -9 就是使用的 kill 这个 syscall,可以用 strace 来确认: + +sudo strace kill -9 pid + +``` +... +kill(444444, SIGKILL) = -1 ESRCH (No such process) +... +``` + ### sigaction 设置信号执行时的 sighandler, From 57e25666f2568339b27d80c9722446525e6d3ef6 Mon Sep 17 00:00:00 2001 From: Xargin Date: Sun, 27 Dec 2020 00:23:59 +0800 Subject: [PATCH 042/120] update signal --- 1.14/signal_based_preemption.md | 68 ++++++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/1.14/signal_based_preemption.md b/1.14/signal_based_preemption.md index 2274dee..41d83f6 100644 --- a/1.14/signal_based_preemption.md +++ b/1.14/signal_based_preemption.md @@ -52,9 +52,75 @@ kill(444444, SIGKILL) = -1 ESRCH (No such process) ... ``` +### signal + +最简单的修改信号行为的 syscall 就是 signal,因为在 Go 里写裸的 signal handler 不太容易,这里用 c 来演示: + +```cpp +// https://www.geeksforgeeks.org/signals-c-language/ +#include +#include + +void handle_sigint(int sig) +{ + printf("Caught signal %d\n", sig); +} + +int main() +{ + signal(SIGINT, handle_sigint); + while (1) ; + return 0; +} +``` + +在命令行中执行该程序,每次按下 ctrl+c 都会看到有 `^CCaught signal 2` 的输出。但是 signal 这个 syscall 实在是太简单了,信号到来的时候如果有更多的可配置项和程序上下文,就可以让我们做更复杂的操作。所以才会有 sigaction 这样的稍微复杂一些的信号处理 syscall。 + ### sigaction -设置信号执行时的 sighandler, +用 sigaction 可以写出和用 signal 完全一致的效果: + +```cpp +#include +#include + +void handle_sigint(int sig) +{ + printf("Caught signal %d\n", sig); +} + +int main() +{ + struct sigaction act; + act.sa_handler = handle_sigint; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + sigaction(SIGINT, &act, NULL); + while (1) ; + return 0; +} +``` + +这段代码和上面的效果是一样的,每次按 ctrl+c 都会输出 `^CCaught signal 2`。 + +sigaction 的 sa_handler 和 sa_sigaction 共同组成一个 C 语言的 union 结构(在 Mac OS 上是这样,暂时没 linux 环境,凑和看吧): + +```cpp +/* union for signal handlers */ +union __sigaction_u { + void (*__sa_handler)(int); + void (*__sa_sigaction)(int, struct __siginfo *, + void *); +}; +``` + +也就是说我们想简单处理,就用 sa_handler,想做复杂些的功能就用 sa_sigaction。 + +sigaction 的三个参数: + +* int 就是 signal number +* siginfo 中包含一些信号的上下文信息,比如发送信号的进程 id,出现信号的指令地址等等。 +* void * 是一个很特殊的参数 ### sigaltstack From bbed3eda33b57df6df4d1a871d0c1b4a888f3c12 Mon Sep 17 00:00:00 2001 From: Xargin Date: Thu, 7 Jan 2021 19:18:58 +0800 Subject: [PATCH 043/120] update signal --- 1.14/signal_based_preemption.md | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/1.14/signal_based_preemption.md b/1.14/signal_based_preemption.md index 41d83f6..af64143 100644 --- a/1.14/signal_based_preemption.md +++ b/1.14/signal_based_preemption.md @@ -137,13 +137,35 @@ sigaction 的三个参数: goto 虽然看起来无所不能,但实际上高级语言的 goto 是跳不出函数作用域的,比如下面这样的代码就没法通过编译: ```go -TODO +package main + +func xx() { +xx: +} + +func main() { + goto yy + goto xx +yy: +} ``` +goto yy,没有问题,但是 goto xx 是不行的。 + 在其它语言里也一样: ```c -TODO +#include + +void xx() { +XX: + printf("hello world"); +} + +int main() { + goto XX; + return 0; +} ``` ### non-local goto From 65350740c97521776ed42367daea99330792e1c0 Mon Sep 17 00:00:00 2001 From: Xargin Date: Thu, 7 Jan 2021 19:41:49 +0800 Subject: [PATCH 044/120] update signal --- 1.14/signal_based_preemption.md | 96 ++++++++++++++++++++++++++++++++- 1 file changed, 95 insertions(+), 1 deletion(-) diff --git a/1.14/signal_based_preemption.md b/1.14/signal_based_preemption.md index af64143..d2281d0 100644 --- a/1.14/signal_based_preemption.md +++ b/1.14/signal_based_preemption.md @@ -285,7 +285,101 @@ asyncPreempt 是汇编实现的,分为三个部分: 2. 执行 asyncPreempt2 3. 恢复 g 的所有寄存器,继续执行用户代码 -TODO,图 + +``` +┌────────────────┐ ┌────────────────┐ +│ stack change │ │ code │ +├────────────────┴────────────────────────────────────────────────────┐ ├────────────────┴─────────────────────────────────────────────────┐ +│ │ │ │ +│ │ │ │ +│ │ │ │ +│ │ │ ┌────────────────────────┬────────────────────────┐ │ +│ │ │ │ 0x21345 │ 0x21350 │ │ +│ ┌──────────────┐ │ │ ├────────────────────────┼────────────────────────┤ │ +│ │ stack │ │ │ │ var i = 1 │ i++ │ │ +│ ├──────────────┴───────────┐ │ │ └────────────────────────┴────────────────────────┘ │ +│ │ local var 1 │ │ │ │ +│ ├──────────────────────────┤ │ │ ┌──────────────────────────┐ │ +│ │ local var 2 │ │ │ │ │ │ │ +│ ├──────────────────────────┤ │ │ │ │ │ │ +│ ┌─────┐ │ ... │ │ │ │ │ signal handler │ │ +│ │ RSP │ ─────────────▶ └──────────────────────────┘ │ │ │ │ │ │ +│ └─────┘ │ │ │ │ push 0x21350 to stk top │ │ +│ │ │ │ │ │ │ +│ │ │ │ │ │ │ +│ │ │ │ └──────────────────────────┘ │ +│ │ │ ▼ │ +│ ┌──────────────┐ │ │ │ +│ │ stack │ │ │ │ +│ ├──────────────┴───────────┐ │ │ │ +│ │ local var 1 │ │ │ ┌────────────────────────┬───────────────────────┐ │ +│ ├──────────────────────────┤ │ │ │ 0x21345 │ 0x234234234234 │ │ +│ │ local var 2 │ │ │ ├────────────────────────┼───────────────────────┤ │ +│ ├──────────────────────────┤ │ │ │ var i = 1 │ asyncPreempt() │ │ +│ │ ... │ │ │ └────────────────────────┴───────────────────────┘ │ +│ ├──────────────────────────┤ │ │ │ +│ ┌─────┐ │ 0x21350 │ │ │ │ │ +│ │ RSP │ ─────────────▶ └──────────────────────────┘ │ │ │ │ +│ └─────┘ │ │ │ │ +│ │ │ │ │ +│ │ │ │ │ +│ │ │ │ │ +│ │ │ │ │ +│ ┌──────────────┐ │ │ │ │ +│ │ stack │ │ │ │ │ +│ ├──────────────┴───────────┐ │ │ │ │ +│ │ local var 1 │ │ │ │ │ +│ ├──────────────────────────┤ │ │ │ │ +│ │ local var 2 │ │ │ ▼ │ +│ ├──────────────────────────┤ │ │ │ +│ │ ... │ │ │ ┌────────────────────────────────┐ │ +│ ├──────────────────────────┤ │ │ │ store registers to stack │ │ +│ │ 0x21350 │ │ │ └────────────────────────────────┘ │ +│ ├──────────────────────────┤ │ │ │ +│ │ rax │ │ │ │ │ +│ ├──────────────────────────┤ │ │ │ │ +│ │ rbx │ │ │ │ │ +│ ├──────────────────────────┤ │ │ │ │ +│ ┌─────┐ │ .... │ │ │ │ │ +│ │ RSP │ ─────────────▶ └──────────────────────────┘ │ │ │ │ +│ └─────┘ │ │ ▼ │ +│ │ │ ┌───────────────────────┐ │ +│ │ │ │ asyncPreempt() │ │ +│ │ │ └───────────────────────┘ │ +│ │ │ │ +│ ┌──────────────┐ │ │ │ │ +│ │ stack │ │ │ │ │ +│ ├──────────────┴───────────┐ │ │ │ │ +│ │ local var 1 │ │ │ │ │ +│ ├──────────────────────────┤ │ │ │ │ +│ │ local var 2 │ │ │ │ │ +│ ├──────────────────────────┤ │ │ ▼ │ +│ │ ... │ │ │ │ +│ ├──────────────────────────┤ │ │ ┌────────────────────────────────┐ │ +│ ┌─────┐ │ 0x21350 │ │ │ │ restore registers │ │ +│ │ RSP │────────────────▶ └──────────────────────────┘ │ │ └────────────────────────────────┘ │ +│ └─────┘ │ │ │ +│ │ │ │ │ +│ │ │ │ │ +│ │ │ │ │ +│ │ │ │ │ +│ │ │ │ │ +│ ┌──────────────┐ │ │ │ │ +│ │ stack │ │ │ │ │ +│ ├──────────────┴───────────┐ │ │ │ │ +│ │ local var 1 │ │ │ │ │ +│ ├──────────────────────────┤ │ │ ▼ │ +│ │ local var 2 │ │ │ ┌────────────────────────────────┐ │ +│ ├──────────────────────────┤ │ │ │ asyncPreempt return │ │ +│ ┌─────┐ │ ... │ │ │ └────────────────────────────────┘ │ +│ │ RSP │──────────────────▶ └──────────────────────────┘ │ │ │ +│ └─────┘ │ │ │ +│ │ │ │ +│ │ │ │ +│ │ │ │ +│ │ │ │ +└─────────────────────────────────────────────────────────────────────┘ └──────────────────────────────────────────────────────────────────┘ +``` ## 总结 From 7a03a9600579f0c0802c336a6d35e80cbfdef341 Mon Sep 17 00:00:00 2001 From: Xargin Date: Thu, 7 Jan 2021 19:42:39 +0800 Subject: [PATCH 045/120] add asyncPreempt monodraw --- 1.14/signal_based_preemption.md | 11 +++++------ monodraw/asyncPreempt.monopic | Bin 0 -> 5947 bytes 2 files changed, 5 insertions(+), 6 deletions(-) create mode 100644 monodraw/asyncPreempt.monopic diff --git a/1.14/signal_based_preemption.md b/1.14/signal_based_preemption.md index d2281d0..775dd4b 100644 --- a/1.14/signal_based_preemption.md +++ b/1.14/signal_based_preemption.md @@ -285,7 +285,6 @@ asyncPreempt 是汇编实现的,分为三个部分: 2. 执行 asyncPreempt2 3. 恢复 g 的所有寄存器,继续执行用户代码 - ``` ┌────────────────┐ ┌────────────────┐ │ stack change │ │ code │ @@ -300,13 +299,13 @@ asyncPreempt 是汇编实现的,分为三个部分: │ ├──────────────┴───────────┐ │ │ └────────────────────────┴────────────────────────┘ │ │ │ local var 1 │ │ │ │ │ ├──────────────────────────┤ │ │ ┌──────────────────────────┐ │ -│ │ local var 2 │ │ │ │ │ │ │ +│ │ local var 2 │ │ │ │ │ signal handler │ │ │ ├──────────────────────────┤ │ │ │ │ │ │ -│ ┌─────┐ │ ... │ │ │ │ │ signal handler │ │ +│ ┌─────┐ │ ... │ │ │ │ │ push 0x21350 to stk top │ │ │ │ RSP │ ─────────────▶ └──────────────────────────┘ │ │ │ │ │ │ -│ └─────┘ │ │ │ │ push 0x21350 to stk top │ │ -│ │ │ │ │ │ │ +│ └─────┘ │ │ │ │ set pc to asyncPreempt │ │ │ │ │ │ │ │ │ +│ │ │ │ │ return │ │ │ │ │ │ └──────────────────────────┘ │ │ │ │ ▼ │ │ ┌──────────────┐ │ │ │ @@ -344,7 +343,7 @@ asyncPreempt 是汇编实现的,分为三个部分: │ │ RSP │ ─────────────▶ └──────────────────────────┘ │ │ │ │ │ └─────┘ │ │ ▼ │ │ │ │ ┌───────────────────────┐ │ -│ │ │ │ asyncPreempt() │ │ +│ │ │ │ asyncPreempt2() │ │ │ │ │ └───────────────────────┘ │ │ │ │ │ │ ┌──────────────┐ │ │ │ │ diff --git a/monodraw/asyncPreempt.monopic b/monodraw/asyncPreempt.monopic new file mode 100644 index 0000000000000000000000000000000000000000..ba2c4e966d0b916cdc181949432d8e1daaecb03c GIT binary patch literal 5947 zcmV-B7sTlQO;1iwP)S1pABzY8000000u$|B+m0JYlKqtipLSu&$hhQ9z&!2iJ{U8P zGe(2zrqmp_$X=52$ZH7Luh|dVFWIU}R`8;xUAO2eRM9Z3G({$ZnOTu>IdS6m+2+GP zmk;~h?EUZg@Le?dX#d;G@_zL=dkD3Z{Oy{$85;oet75ohv((`ahc7(-+o!nXP-BZ%jf(2 zlZP+yHv5*(FT48>o9*Lrn|;IcW;=V&=mSJ!f|<{DPm9ORzwQfBvQK62U+njbho?n0 zx6GT*@bJs0*FL|^e)VGg>C5wCd-#^@@8`3R+r{VQ{bK#_wAr5gY8SFEI0#dG<^6|k z`qr}-^F_&DeBM0#lK;Gqi|5_)wH16?Jw7hi@`i`~({}Ua)04dLVUdZQy|jM3&-Y^X z9?xD|uJfNM>$hKgn!V3{lWZFv7ijbG<8GO+tC^cw`TKUO|IX%|xiF9Ui@Pr$vUg{n z&4k>gZ9IHhzNi%Ruz?q|o-M+5_sYub4==8>ug^w*O{@RBydx3nVY6N5y8y?pDGypY z6!}{>+tsJl`r_wYNK=0Lm0dsI!h=kQ-@3{_wOj3fD-OcTCL69c>vBzw$DkO*!@!$z z0ObH@Kg$0;=h~hzuzvG>G^E!@{h~fr!7jy0rE}Teq1~(vuAC0 zc^=MxWjW=^kvcv!`F`9lA6NU6O+Kv0pDdqj3xBuQhezZ4N9Udk| zf|}Mu4oX6!qR>W5=%6$)K>c%v6;}huD+z7&geHi8I`?rR$R}Fm;?A-|!aOS_^h!US zsG)esbB0npva$98#;ObGn=T;IiHOB{e_pMZ7x%|OKVKLb^2RF{%!SEu<%X|!5nQq8 zk1E2?)JasRvLX5WGD@pgPHK3*_-bmzxJ>A0w5j$%7|pd`3?NETs+>dH~U4} zLwxNJ{=psk2bRQc`RQ~1SJ_X$5DU*mc*VXtv2Z>wUe0`6H@jxLy=tWt3!0XfUNehJ zDbFRexm@2_otGwbc@FjNWiYoho}Y2Ndd5loT@rtn#NTDp(-}VslSHU>#u?w>j5E|Z z;}}`(IzZ12uLD0PBljtj-eZWx+`E4*m+7fmt+OfbpO+ugyeCt>cDB;H_nY+DSB}x* zdG%@ixwzONcldu@rvH&|${vv?`Ty19Qhf4sLNEPY@pFAS!H3O$zxkY&GDQ-OJ|@Z% z8oebXW7TYGaO2yaAAkI@+?EKV`*o`yez?p35o;4|2R3UmS*wQ8oF7_h&x|pM=Ar-8 z9#)Q`6`@tJNZ!HxT@OKgiI+*Z%#|jyE6sPu(@O3+*EW0>6fZZnO1`|)yEQ2^)wyP# zsq4~s14YA-x-bmU>Pg7p)WS?mPUlcLy$7Mz11lyrkf0t&TuSN- zf^i~DeFz4<1q6d?K`)7BtHV1R+BQ^&T zn*)i>ffy+uMhZ+VHV3VNI9^G!1*IA`z*?vV8XT$tM~P}cI{;Jz{@cG-M>OC$q9M** z53W`^4QV8Z1{6dC3Zen0hG@XHhz5*GFhKNHEj*m(!gPctMWI1SXfTRFKqp+Qe*0=?ur z5sq3pThzjfD(*?dafaUS9-cBUWhtBE>m`O^Xwg{WTQs)AxJ8-0s2qt+JPId~SjHYA zu_S3=k~AE;f=bHe$>eF=ouvFm0wS zrj3K1ezg~McJWFyD4RY_ETNBDSz^SYWr+*i#jW|cct@C zl3W~P+T}P>JPV$rV^Vpfb?Nfyz{? zAruzXTZ8g7(A~nM9bVkIA74F03YQ_$KB{K~w1p_)P?SatD2*7%Ow7|DYztNeJp{0ziV=4X6cy#o#DD*yIVWxbQzOntYYOWU7DRz_}i>{fEDeVtf*qjh2?Dw**V z^gv1Eydo2%)8q31rNu@qf;04bs{uv}S&MpbU{K>S0#Y0|$ z#uiwcR?t{(P<&h&aIIBWnCzMg0Wi}x0A^YRzz%1oQUpvg_!Wq0URR7zz`-Pj3s(*e z6y*O^4-LF_XfR+GG~f;Tw^lY!8M~nP1OUY_?x+zrVD*;H>rG9dm&gV`dWaekb8-qxfkfT1jFH2S)L4y7$Ocp3i7C5!;%hbvtv)-BSF`sR3wD<4V9JPP2d#aO|G6LI5aL- zilgLmg}!3_580MiR%sN^2wc6N_n^_z2}mQBk(H5?(Zu*$DYoTrB@T-0l6sJxL}sVa zeLiTxko6V=!H+H&bIPOt?MQs2wuIJ?yf;a$J{9B#X9c)%GP?IgFOol5tGmC{=u(lZ z#uHN@U@wJ#=^Uhy*aA*$0VlSAo0=`)T64L3Mg1E6n0eR0XgP+c=oqqA$(0FO99dB> z21v0v!E`+GgEinKsLX@rm1rXx^vkQ9a7|PfuhLG3wlHak*oe4@q8bZFJD_(Y;w$Qk3Ko=B$$qjtPIx-oBLTM} zF34ED+rL4nZslv673Z!{QCp#(MNnMXT0oSXz-nN*RgbsaYPXc^r*vMGbF1E82u|}3mn}09;Tp`2d~28;Iipo&Zxr(=7 zbr8TAJ1_Kt0SFS~4-(@Kes;{RmZLZ|3_xpcUZrR)*6L_LZg9HC+FQ!8$7W3A&ee`_ zbLhe5PsbpQMDtkDJXSQ1otoxxZ8c8{7AaU%u3pSxa5SUt)&{Om%>(YT4O~Gt{MNw+ z0XTpF96$gL;Dl||g{cJ_+Omz>T4*Ss!38Dh)@-T2W=lYq&6c1C=96>=(nw;F(m4{O zb0pBzVv!VD*LnC+S2rJvo5AT}zJMnIO9u3c%(HrcCo)QCqBz3tDcM(M`~I|8KRz$F z|5*QH{qkk^ba${TQ!(`~*&i?YUu9F=y*%VEo<4K=-<)$O5JyE?;VCi2TqlZ&*N9?L zQmITll_QW*PrSXS!qn#NrSJy4y%cKk_L7TQ1vPrA5g~L!|5YpgX?bYzPxiO?XV*~( zw$`NY6AOP@YFltYK~II*PE?pzlrV;?2!z&9OE4p^ z=Yyz)(T#d&Me=$$!#qvXzTX8RYXRZ)I-nx!&CObsP3W{%I_RwifYMV3q*%+K#p9cv z=5cn3Akb4);Pp0}sG%CfaKaDI4ayEH!}wWo?gl(Ml`Gk_$bYyNMi1Q3y77X&b>r=; z8!x!aZoJ4nq@n~E0|JZz0mkI5y31_C#Jcf2ydXG-qbW+0*auI_QfdZ02Fk9G*Sm?qz*VWKoPYC6ww8bcV#95 z2F32gpp|-QTu?(fYqVrpy0|zvRqDS+Fj2JOY)LRtk2Py(Eb8Rpmcr>Xh2z}?XI39Q zveGF?Bb*(3Fda+wkKx&ArEq*((sZV)+J~Th>)5N4+xC+uzKpS>vpAw2sTtL}9oP-+ zcAz-M+rhWJ9H80dkn_P+z8IKKFNSyhG*3(ipsZs*TBPMjE(@lKT?}m7mNvN#vXMbA zCX#rcD@QZz&^o{Z-#Wnd-2oQtWd~Tq9-u@10c+jfApwGfPe zgUd|8Ei=`-lEBLhRUfrdAW=?`D5rFe2I(9PIJE>7wogzs<;?|il3hV9Zd>3kfRKQC zKu9p1Naz$L3{54pr&|nqG{Zy6v1vDqBe^TS+QgDOI-iP^EJs zjH_LxQ+WE?RXXJBROvtkLhL_B8Gu(rURu^dUg`;*pN?y6!nSR0ia?9L-|B(r8nl?G zjC}N3>k;(;i`anExboa+M;zf7%5G$B{`dJK+hZv*T5TzW}=ntj}{a|DWJs%t0=SRbU~|g{ z_kLf(Wj?qM)=VR59V2NSkSi4A3WceqgMnLv+)+u%I?dvrtM@Rr$A<^gMbk0X!zkvo z>Z9yAW=|>rs8Q_&SE#wa+Fm3a5vG=!w-?EIN(7KgU6eyaQmpi=g?FXF5&EOO;az*e zyV4n#znMc1rU6OS8%fn0N!1&n&|BfunEa_VlYdYP5e6lrBPA3odpu@v5kg~b9N_(N zfSb$MU?02-M*JEhevOgPppnp^nObPjxYiWl*ZP)22(8)aoEq3o53K~KCA3k5Sh*=d ztirtT@#{gX1pG=UH9#meK-d=%@`*6@iIDLvh>)=!5pu1TBN&HfZeTPw=cym(38TFz z7PLJW8|eU)X|r;KG9npG%-F!#+Sq8oxmx)K3{EfgWG8_z1JXn>O#%o;poH@QueUcj zh`LE&*+)H*tR5DtFi(jAfLuj4_rGMoCN_^O915CT+U}*CPe2LZdj7?%vYwMF@*ZVDU`4@Jc%h zp{MkodoA3FegDd zaXB-WGjln!G%ApSV5tX|a$uPpjR2x!t9vS0^0_&~>pZfkwA* zxgoT=`^yO@5h`_DXV#jyL2+s28jCQ#XC>zbl|Zr{>Jii!9vyW+Uz5`Y>A`xMfj%fS z`y2x5_3t1pTvh(=#q&#MR4n%=hV^dwU-#=TpFb?O#e7giJzenzPNHT40ew4>KYz9g zJNwN3HM5@u8=|;AUTJ+W>In@-@wrDWv@`1oZEK;2;?P=dM;s}>IQElmuhLQV|NiHH zD+kHrSx@fZdO;XTI9#IPvGgiq=~c#wwd0PVHSD;lG2+~5PaCazQBDj3uX4arTZA#& ztCQRIlQ$r2sN60mYW4T3)h>r%aGcx#w@Q_6`!G~*#6aNAQ>odpq#lx~BmrNNh%ZUV zmn7z!w;`Jr6ct)ia;KV%u_q?;T|^Q+xUm-U?)lm!_ht_R}!>!v+ zPXKz3m)&*>)8UPkLOK9R9znVZl%6GbkeBdJN;7b5+ik}+ZIX<0Iw7m97g#n!3oIA) zo;UxxE8AoH;cNA{a$+4ik1O}k42PuVmyE=ajeu~Cpm2?!SX|k}w&n-c3%?D`GeLjr z|J_&r@4UOoMd+;u<$%)Z6}^Pn{1Ngmn13Ip7J4(SEBKrq{4J=FkbA}6CRznw&;`sK zNV6)k3kh@J!U$o}M_jbm+kw2ric)I~JsdpwFixgEwsNMU(!QWtp9a)Zk!)gf{y}@> z4bq&{^!Ffm%?kM{!z3Myx0_DhW-fgig-GZfY$hSW9T9C$y~v*A7E#A?@8QpWor7 zd8~!5oX|gOA042Ve7*$9Bu3^-_>eD?dSPO%qeI)y(G;{QM1b^~DiV#kS{<$8(s`Oh zKgy`Lgkbb+Ee6{`FAGjwX|?O?onGNF+h!u!dj9^<-~f3)iJQN#(Gdx&`~5y#`u)BK zzu$|GFBw3V3?Ta>f!4%80qom?0=NPFU}c3ItrqRYqzVOiUXZ=jDipwYm}A Date: Thu, 7 Jan 2021 19:51:12 +0800 Subject: [PATCH 046/120] update signal --- 1.14/signal_based_preemption.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/1.14/signal_based_preemption.md b/1.14/signal_based_preemption.md index 775dd4b..e184c05 100644 --- a/1.14/signal_based_preemption.md +++ b/1.14/signal_based_preemption.md @@ -380,6 +380,21 @@ asyncPreempt 是汇编实现的,分为三个部分: └─────────────────────────────────────────────────────────────────────┘ └──────────────────────────────────────────────────────────────────┘ ``` +## asyncPreempt2 + +```go +func asyncPreempt2() { + gp := getg() + gp.asyncSafePoint = true + if gp.preemptStop { + mcall(preemptPark) + } else { + mcall(gopreempt_m) + } + gp.asyncSafePoint = false +} +``` + ## 总结 TODO,总览图 From 73f747d699e26e716010ccfb15f1034131bca5e4 Mon Sep 17 00:00:00 2001 From: wziww Date: Fri, 29 Jan 2021 18:26:03 +0800 Subject: [PATCH 047/120] select nil chan --- select.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/select.md b/select.md index e5a81bc..de04b68 100644 --- a/select.md +++ b/select.md @@ -563,7 +563,15 @@ loop: switch cas.kind { case caseNil: - // 这个 case 要怎么触发? + /* + * var nil_chan chan int + * var non_nil_chan chan int = make(chan int) + * select { + * case <-nil_chan: + * // here + * case <-non_nil_chan: + * } + */ continue case caseRecv: From 3d352abfb17bb9cece3c9ccd6d24a04578b6adcb Mon Sep 17 00:00:00 2001 From: wziww Date: Mon, 1 Feb 2021 17:10:28 +0800 Subject: [PATCH 048/120] =?UTF-8?q?=E8=A1=A5=E5=85=85=20select-chan=20A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- select.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/select.md b/select.md index de04b68..24d32d0 100644 --- a/select.md +++ b/select.md @@ -825,4 +825,4 @@ sclose: Q: 如果select多个channel,有一个channel触发了,其他channel的waitlist需要不要主动去除?还是一直在那等着? -A: TODO +A: waitlist 的出列是由 `func (q *waitq) dequeue() *sudog` 函数控制的,每个 sudog 携带了一个 `selectDone` 标志位,通过 `cas` 操作在每次 `dequeue` 的时候「惰性」去除队列中无效的元素 From 393913ee00567136ee61e7d46cef8df5feffe7bc Mon Sep 17 00:00:00 2001 From: wziww Date: Thu, 4 Feb 2021 17:19:04 +0800 Subject: [PATCH 049/120] slice with reflect --- slice.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/slice.md b/slice.md index 1e32544..396491e 100644 --- a/slice.md +++ b/slice.md @@ -318,3 +318,23 @@ func main() { fmt.Println(sux) } ``` + +## reflect 与 slice +对于 slice 的操作,除了计算 slice 的内存布局来获取 len, cap, data 以外,同样也可以利用 reflect 来实现 +```go + var b []byte = []byte("test") + bh := (*reflect.SliceHeader)(unsafe.Pointer(&b)) + _ = bh.Len // len + _ = bh.Cap // cap + _ = bh.Data // 底层数组指针 + + // 案例: 0 拷贝转换 slice 为 string + var str string + sh := (*reflect.StringHeader)(unsafe.Pointer(&str)) + // 从头转换 test + sh.Data = bh.Data + sh.Len = bh.Len + // 从中间截取转换 est + sh.Data = (uintptr)(unsafe.Pointer(&b[1])) + sh.Len = bh.Len - 1 +``` From 7b0057f7f78fce6fccc679867a1636c84a528ee6 Mon Sep 17 00:00:00 2001 From: Xargin Date: Fri, 5 Feb 2021 11:31:58 +0800 Subject: [PATCH 050/120] update authors --- readme.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/readme.md b/readme.md index 2b8af93..848a0c6 100644 --- a/readme.md +++ b/readme.md @@ -28,3 +28,10 @@ 22. [x] [context](context.md) 23. [x] [stack dump](runtime_stack.md) 24. [x] [Atomic](atomic.md) + +# Authors + +[cch123](https://github.com/cch123) + +[wziww](https://github.com/wziww) + From 9e4e4bf7d899a742dc655932d0bd0f07aca3d3cf Mon Sep 17 00:00:00 2001 From: Xargin Date: Sat, 13 Feb 2021 21:04:05 +0800 Subject: [PATCH 051/120] update interface --- generics.md | 0 interface.md | 91 +++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 87 insertions(+), 4 deletions(-) create mode 100644 generics.md diff --git a/generics.md b/generics.md new file mode 100644 index 0000000..e69de29 diff --git a/interface.md b/interface.md index 2d830dc..4696ecd 100644 --- a/interface.md +++ b/interface.md @@ -2,7 +2,90 @@ ## iface 和 eface -> Written with [StackEdit](https://stackedit.io/). - \ No newline at end of file +Go 使用 iface 和 eface 来表达 interface。iface 其实就是 interface,这种接口内部是有函数的。 + +eface 表示 empty interface。 + +非空接口: +```go +// runtime2.go +type iface struct { + tab *itab + data unsafe.Pointer +} + +type itab struct { + inter *interfacetype + _type *_type + hash uint32 // copy of _type.hash. Used for type switches. + _ [4]byte + fun [1]uintptr // variable sized. fun[0]==0 means _type does not implement inter. +} +``` + +空接口: + +```go +type eface struct { + _type *_type + data unsafe.Pointer +} +``` + +所以我们可以比较容易地推断出来下面两种 interface 在 Go 内部是怎么表示的: + +iface: +```go +type Reader interface { + Read([]byte) (int, error) +} +``` + +eface: +```go +var x interface{} +``` + +### iface 适用场景 + +在 Go 语言中,我们可以借助 iface,实现传统 OOP 编程中的 DIP(dependency inversion principle),即依赖反转。 + +```go + ┌─────────────────┐ + │ business logic │ + └─────────────────┘ + │ + │ + │ + ┌───────────────────────────────┼───────────────────────────────┐ + │ │ │ + │ │ │ + │ │ │ + ▼ ▼ ▼ +┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ +│ RPC │ │ DATABASE │ │ CONFIG_SERVICE │ +└─────────────────┘ └─────────────────┘ └─────────────────┘ +``` + +上面这样的控制流关系在企业软件中很常见,但如果我们让软件的依赖方向与实际控制流方向一致,很容易导致 RPC、CONFIG_SERVER,DATABASE 的抽象被泄露到上层。例如在业务逻辑中有形如下面的结构体定义: + +```go +type Person struct { + Age int `db:"age"` + Name string `db:"name"` +} +``` + +或者在业务逻辑中有类似下面的字眼: + +```go +func CreateOrder(order OrderStruct) { + config := GetConfigFromETCD() // abstraction leak + CalcOrderMinusByConfig(config, order) + SaveOrder() +} +``` + +业务逻辑不应该知道底层的实现,不应该有形如 db 的 tag,不应该知道配置是从 ETCD 中取出的。 + +### eface 适用场景 From cab2063dbd22d70b4f36e6ecb7f4822846690cbf Mon Sep 17 00:00:00 2001 From: Xargin Date: Sat, 13 Feb 2021 21:34:10 +0800 Subject: [PATCH 052/120] update inter --- interface.md | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/interface.md b/interface.md index 4696ecd..90a2f82 100644 --- a/interface.md +++ b/interface.md @@ -1,6 +1,4 @@ -# interface - -## iface 和 eface +# iface 和 eface Go 使用 iface 和 eface 来表达 interface。iface 其实就是 interface,这种接口内部是有函数的。 @@ -35,6 +33,7 @@ type eface struct { 所以我们可以比较容易地推断出来下面两种 interface 在 Go 内部是怎么表示的: iface: + ```go type Reader interface { Read([]byte) (int, error) @@ -42,10 +41,13 @@ type Reader interface { ``` eface: + ```go var x interface{} ``` +## iface + ### iface 适用场景 在 Go 语言中,我们可以借助 iface,实现传统 OOP 编程中的 DIP(dependency inversion principle),即依赖反转。 @@ -88,4 +90,29 @@ func CreateOrder(order OrderStruct) { 业务逻辑不应该知道底层的实现,不应该有形如 db 的 tag,不应该知道配置是从 ETCD 中取出的。 +``` +┌──────────────────────────────────────────────────────────────┐ +│ │ +│ │ +│ business logic │ +├────────────────────┬────────────────────┬────────────────────┤ +│ configInterface │ storeInterface │ getInterface │ +└────────────────────┴────────────────────┴────────────────────┘ + ▲ ▲ ▲ + │ │ │ + │ │ │ + │ │ │ + │ │ │ + │ │ │ + │ │ │ + │ │ │ + ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ + │ etcd Impl │ │ DATABASE │ │ RPC │ + └─────────────────┘ └─────────────────┘ └─────────────────┘ +``` + +## eface + ### eface 适用场景 + +### eface 实现原理 From 3cc9f01df778c8a024ab21862654cb37b489c3ff Mon Sep 17 00:00:00 2001 From: Xargin Date: Sat, 13 Feb 2021 21:50:17 +0800 Subject: [PATCH 053/120] add img --- interface.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/interface.md b/interface.md index 90a2f82..00e40f6 100644 --- a/interface.md +++ b/interface.md @@ -113,6 +113,43 @@ func CreateOrder(order OrderStruct) { ## eface +从定义来看,eface 其实是 iface 的一个子集: + +``` +┌─────────┐ ┌─────────┐ +│ iface │ ┌─────▶│ itab │ +├─────────┴───────────────────┐ │ ├─────────┴───────────────────┐ +│ tab *itab │──────┘ │ inter *interfacetype │ +┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫ ├─────────────────────────────┤ +┃ data unsafe.Pointer ┃ ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫ +┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ _type *_type ┃ + ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫ + ├─────────────────────────────┤ + │ hash uint32 │ + ├─────────────────────────────┤ + │ fun [1]uintptr │ + └─────────────────────────────┘ +┌─────────┐ +│ eface │ +├─────────┴───────────────────┐ +│ _type *_type │ +├─────────────────────────────┤ +│ data unsafe.Pointer │ +└─────────────────────────────┘ +``` + ### eface 适用场景 ### eface 实现原理 + +## 类型转换 + +### 普通类型转换为 eface + +### 普通类型转换为 iface + +### eface 转换为普通类型 + +### iface 转换为普通类型 + +### 类型转换图 From 618ad3807421ce9ddfaf110f4f2fb79b797d75a5 Mon Sep 17 00:00:00 2001 From: Xargin Date: Sat, 13 Feb 2021 21:57:39 +0800 Subject: [PATCH 054/120] update inter --- interface.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface.md b/interface.md index 00e40f6..36715b0 100644 --- a/interface.md +++ b/interface.md @@ -138,6 +138,8 @@ func CreateOrder(order OrderStruct) { └─────────────────────────────┘ ``` +既然是个子集,那已经有了 iface 为什么还需要 eface?显然是为了优化。 + ### eface 适用场景 ### eface 实现原理 From abee045092a06fd7bf80b68fccee290a086d8a5d Mon Sep 17 00:00:00 2001 From: Xargin Date: Sun, 14 Feb 2021 23:04:31 +0800 Subject: [PATCH 055/120] update interface --- interface.md | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 85 insertions(+), 2 deletions(-) diff --git a/interface.md b/interface.md index 36715b0..198b175 100644 --- a/interface.md +++ b/interface.md @@ -142,16 +142,99 @@ func CreateOrder(order OrderStruct) { ### eface 适用场景 -### eface 实现原理 +举个标准库里的例子: + +```go +func Slice(slice interface{}, less func(i, j int) bool) +``` + +标准库的 container package,因为不知道用户会在 container 中存储什么类型,所以大部分 API 也是用 interface{} 的。 + +除了标准库,我们在 fmt.Print 系列和 json.Unmarshal 中也会见到 interface{}。 + +```go +func Println(a ...interface{}) (n int, err error) +``` + +```go +func Unmarshal(data []byte, v interface{}) error +``` + +总结一下,使用 interface 主要是我们在不知道用户的输入类型信息的前提下,希望能够实现一些通用数据结构或函数。这时候便会将空 interface{} 作为函数的输入/输出参数。在其它语言里,解决这种问题一般使用泛型。 ## 类型转换 ### 普通类型转换为 eface -### 普通类型转换为 iface +```go + 1 package main + 2 + 3 var i interface{} + 4 + 5 func main() { + 6 var y = 999 + 7 i = y + 8 var x = i.(int) + 9 println(x) +10 } +``` + +老规矩,看汇编知一切,主要是第 7 行: + +```go +0x0021 00033 (interface.go:7) MOVQ $999, (SP) // 把 999 作为参数挪到栈底 +0x0029 00041 (interface.go:7) CALL runtime.convT64(SB) // 以 999 作为参数调用 runtime.convT64,内部会为该变量在堆上分配额外空间,并返回其地址 +0x002e 00046 (interface.go:7) MOVQ 8(SP), AX // 返回值移动到 AX 寄存器 +0x0033 00051 (interface.go:7) LEAQ type.int(SB), CX // 将 type.int 值赋值给 CX 寄存器 +0x003a 00058 (interface.go:7) MOVQ CX, "".i(SB) // 将 type.int 赋值给 eface._type +0x004a 00074 (interface.go:7) MOVQ AX, "".i+8(SB) // 将数据 999 的地址赋值给 eface.data +``` + +还是比较简单的。 ### eface 转换为普通类型 +空接口转成普通类型,其实就是断言。我们继续使用上面的 demo: + +```go + 1 package main + 2 + 3 var i interface{} + 4 + 5 func main() { + 6 var y = 999 + 7 i = y + 8 var x = i.(int) // 这次关注断言的实现 + 9 println(x) +10 } +``` + +看看第 8 行的输出: + +```go +0x0033 00051 (interface.go:7) LEAQ type.int(SB), CX // 在第 7 行把 type.int 搬到 CX 寄存器了,注意下面要用到 +..... +0x0051 00081 (interface.go:8) MOVQ "".i+8(SB), AX // AX = eface.data +0x0058 00088 (interface.go:8) MOVQ "".i(SB), DX // DX = eface._type +0x005f 00095 (interface.go:8) CMPQ DX, CX // 比较 _type 和 type.int 是否相等 +0x0062 00098 (interface.go:8) JNE 161 // 如果不等,说明断言失败,跳到 161 位置,开始执行 panic 流程 +0x0064 00100 (interface.go:8) MOVQ (AX), AX // 把 AX 地址里的内容搬到 AX 寄存器,现在 AX 里存的是 999 了 +0x0067 00103 (interface.go:8) MOVQ AX, "".x+24(SP) // 将 999 赋值给变量 x + +// 下面这部分是实现 panic 的,不是我们的重点 +0x00a1 00161 (interface.go:8) MOVQ DX, (SP) +0x00a5 00165 (interface.go:8) MOVQ CX, 8(SP) +0x00aa 00170 (interface.go:8) LEAQ type.interface {}(SB), AX +0x00b1 00177 (interface.go:8) MOVQ AX, 16(SP) +0x00b6 00182 (interface.go:8) CALL runtime.panicdottypeE(SB) +0x00bb 00187 (interface.go:8) XCHGL AX, AX +0x00bc 00188 (interface.go:8) NOP +``` + +也挺简单的,这里我们用的是 int 来进行实验,对于稍微复杂一些的复合类型,也只是多出了一些步骤,本质上没有什么区别,感兴趣的读者可以自行研究。 + +### 普通类型转换为 iface + ### iface 转换为普通类型 ### 类型转换图 From 4050917af847849d7cf8720eee287c1d68570d76 Mon Sep 17 00:00:00 2001 From: Xargin Date: Sun, 14 Feb 2021 23:31:04 +0800 Subject: [PATCH 056/120] interface done --- interface.md | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/interface.md b/interface.md index 198b175..413e74b 100644 --- a/interface.md +++ b/interface.md @@ -235,6 +235,88 @@ func Unmarshal(data []byte, v interface{}) error ### 普通类型转换为 iface +```go + 1 package main + 2 + 3 import ( + 4 "io" + 5 "os" + 6 ) + 7 + 8 var j io.Reader + 9 +10 func main() { +11 var i, _ = os.Open("ab.go") +12 j = i +13 var k = j.(*os.File) +14 println(k) +15 } +``` + +看看第 12 行的汇编: + +```go +0x0050 00080 (interface2.go:12) LEAQ go.itab.*os.File,io.Reader(SB), CX +0x0057 00087 (interface2.go:12) MOVQ CX, "".j(SB) +0x0067 00103 (interface2.go:12) MOVQ AX, "".j+8(SB) +``` + +和 eface 的赋值其实差不多,稍微有区别的是这里的 go.itab.*os.File,io.Reader(SB),这个符号(理解成一个全局变量,类型是 runtime._type 就行)是编译器帮我们生成的: + +```go +go.itab.*os.File,io.Reader SRODATA dupok size=32 + 0x0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + 0x0010 44 b5 f3 33 00 00 00 00 00 00 00 00 00 00 00 00 D..3............ + rel 0+8 t=1 type.io.Reader+0 + rel 8+8 t=1 type.*os.File+0 + rel 24+8 t=1 os.(*File).Read+0 +``` + +并且是按需生成的,如果你把 `*os.File` 赋值给 io.Writer,在编译结果中也能找到类似的符号: + +```go +go.itab.*os.File,io.Writer SRODATA dupok size=32 + 0x0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + 0x0010 44 b5 f3 33 00 00 00 00 00 00 00 00 00 00 00 00 D..3............ + rel 0+8 t=1 type.io.Writer+0 + rel 8+8 t=1 type.*os.File+0 + rel 24+8 t=1 os.(*File).Write+0 +``` + +看起来就容易理解了。 + ### iface 转换为普通类型 -### 类型转换图 +```go + 1 package main + 2 + 3 import ( + 4 "io" + 5 "os" + 6 ) + 7 + 8 var j io.Reader + 9 +10 func main() { +11 var i, _ = os.Open("ab.go") +12 j = i +13 var k = j.(*os.File) +14 println(k) +15 } +``` + +主要关心第 13 行的汇编结果: + +```go +// 数据准备在这里,AX 和 CX 寄存器 13 行会用到 +0x0050 00080 (interface2.go:12) LEAQ go.itab.*os.File,io.Reader(SB), CX +0x0057 00087 (interface2.go:12) MOVQ CX, "".j(SB) +0x0067 00103 (interface2.go:12) MOVQ AX, "".j+8(SB) + +0x006e 00110 (interface2.go:13) MOVQ "".j+8(SB), AX // AX = j.data +0x0075 00117 (interface2.go:13) MOVQ "".j(SB), DX // DX = j.itab +0x007c 00124 (interface2.go:13) CMPQ DX, CX // 比较 iface.itab 和 go.itab.*os.File,io.Reader(SB) 是否一致 +0x007f 00127 (interface2.go:13) JNE 187 // 不一致,说明断言失败,跳到 panic 流程 + +// 下面就是正常流程了 +``` From cc1b975a06fc5ebde7b7d0830904dde88090faec Mon Sep 17 00:00:00 2001 From: Xargin Date: Sun, 14 Feb 2021 23:32:45 +0800 Subject: [PATCH 057/120] add interface --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 848a0c6..75eca22 100644 --- a/readme.md +++ b/readme.md @@ -6,7 +6,7 @@ 1. [x] [Bootstrap](bootstrap.md) 2. [x] [Channel](channel.md) -3. [ ] [Interface](interface.md) +3. [x] [Interface](interface.md) 4. [x] [Select](select.md) 5. [x] [Slice](slice.md) 6. [x] [Timer](timer.md) From 5fb52209031932d92b5d923d935f09c11a80d057 Mon Sep 17 00:00:00 2001 From: Xargin Date: Sun, 14 Feb 2021 23:34:13 +0800 Subject: [PATCH 058/120] add generics --- readme.md | 1 + 1 file changed, 1 insertion(+) diff --git a/readme.md b/readme.md index 75eca22..8c35149 100644 --- a/readme.md +++ b/readme.md @@ -28,6 +28,7 @@ 22. [x] [context](context.md) 23. [x] [stack dump](runtime_stack.md) 24. [x] [Atomic](atomic.md) +25. [ ] [Generics](generics.md) # Authors From c88e584394d51ae27c96e5afd5846a90a3726325 Mon Sep 17 00:00:00 2001 From: wziww Date: Sat, 20 Feb 2021 14:06:58 +0800 Subject: [PATCH 059/120] runqget => localrunqget --- scheduler.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scheduler.md b/scheduler.md index 5f7afdc..083d871 100644 --- a/scheduler.md +++ b/scheduler.md @@ -1618,7 +1618,7 @@ findrunnable 比较复杂,流程图先把 gc 相关的省略掉了: ```mermaid graph TD -runqget --> A[gp == nil] +localrunqget --> A[gp == nil] A --> |no|return A --> |yes|globrunqget globrunqget --> B[gp == nil] @@ -1641,7 +1641,7 @@ H --> |yes|I[netpoll] I --> J[gp == nil] J --> |no| return J --> |yes| stopm -stopm --> runqget +stopm --> localrunqget ``` ```go From bcccb8e43d67c42a0a3dea6830430f769def542a Mon Sep 17 00:00:00 2001 From: wziww Date: Fri, 26 Feb 2021 13:57:19 +0800 Subject: [PATCH 060/120] =?UTF-8?q?RawSyscall=20Syscall=20=E5=8C=BA?= =?UTF-8?q?=E5=88=AB=E8=A1=A5=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- syscall.md | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 87 insertions(+), 1 deletion(-) diff --git a/syscall.md b/syscall.md index e1a44bc..6760847 100644 --- a/syscall.md +++ b/syscall.md @@ -175,12 +175,98 @@ ok2: RET ``` -RawSyscall 和 Syscall 的区别也非常微小,就只是在进入 Syscall 和退出的时候没有通知 runtime,这样 runtime 理论上是没有办法通过调度把这个 g 的 m 的 p 调度走的,所以如果用户代码使用了 RawSyscall 来做一些阻塞的系统调用,是有可能阻塞其它的 g 的,下面是官方开发的原话: +golang 旧版本中 RawSyscall 和 Syscall 的区别也非常微小,就只是在进入 Syscall 和退出的时候没有通知 runtime,这样 runtime 理论上是没有办法通过调度把这个 g 的 m 的 p 调度走的,所以如果用户代码使用了 RawSyscall 来做一些阻塞的系统调用,是有可能阻塞其它的 g 的,下面是官方开发的原话: > Yes, if you call RawSyscall you may block other goroutines from running. The system monitor may start them up after a while, but I think there are cases where it won't. I would say that Go programs should always call Syscall. RawSyscall exists to make it slightly more efficient to call system calls that never block, such as getpid. But it's really an internal mechanism. RawSyscall 只是为了在执行那些一定不会阻塞的系统调用时,能节省两次对 runtime 的函数调用消耗。 +#### 新版本抢占式调度中的 RawSyscall 和 Syscall +由于 `RawSyscall` 相较于 `Syscall` 缺少了 `runtime·entersyscall(SB)` 以及 `runtime·exitsyscall(SB)` 的调用,当 `g` 执行的是阻塞性质的系统调用的时候,当前 `g` 会维持 `running` 状态,runtime 系统监控在进行全局调度的时候一旦发现运行超过 10ms 的 `g` 就会执行抢占操作(1.14.3 版本, linux_amd64 下为例),通过发送信号量给 `g` 对应的线程,而由于线程在初始化的时候进行了信号量的监听以及设置了相应的 `sa_flags` 参数,会导致在收到信号量的时候对正在阻塞的系统调用产生中断,这种行为往往会给使用者带来意料之外的情况,以下通过一个简单的阻塞性系统调用案例来演示(futex): +```go +// futex +package main + +import ( + "fmt" + "os" + "syscall" + "time" + "unsafe" +) + +const ( + SYS_futex = 202 + _FUTEX_PRIVATE_FLAG = 128 + _FUTEX_WAIT = 0 + _FUTEX_WAKE = 1 + _FUTEX_WAIT_PRIVATE = _FUTEX_WAIT | _FUTEX_PRIVATE_FLAG + _FUTEX_WAKE_PRIVATE = _FUTEX_WAKE | _FUTEX_PRIVATE_FLAG +) + +func main() { + var futexVar uint32 = uint32(os.Getegid()) + futexVar++ + for i := 0; i < 3; i++ { + go func() { // 启动 3 个线程进行阻塞系统调用 + fmt.Println("sleep start") + type timespec struct { + tv_sec int64 + tv_nsec int64 + } + /* + * 设置 10 秒唤醒 futex + * 通过调节该参数大小可以测试: + * 当执行时间小于 runtime 调度器的判定时间的时候,阻塞调用可正常进行 + */ + var ns int64 = 1e9 * 10 + ts := timespec{ + tv_sec: ns / 1e9, + tv_nsec: ns % 1e9, + } + // var ts timespec + // ts.setNsec(ns) + fmt.Println(syscall.RawSyscall6( + SYS_futex, // trap AX 202 + uintptr(unsafe.Pointer(&futexVar)), // a1 DI 1 + uintptr(_FUTEX_WAIT), // a2 SI 0 + 1, // a3 DX + // futex 超时参数,经测试,在设置为 NULL 的情况下(永不超时) + // 再起一个线程(下方注释部分代码)修改 futexVar 的值可以发现另一个系统调用异常,感兴趣的读者可自行测试验证 + uintptr(unsafe.Pointer(&ts)), // a4 R10 + 0, // a5 R8 + 0), // a6 R9 + ) + fmt.Println("sleep end") + }() + } + // go func () { + // select { + // case <-time.After(time.Second): + // futexVar = 2 + // } + // } + for { + select { + case <-time.After(time.Hour): + } + } +} + +``` +执行结果: +```shell +sleep start +sleep start +sleep start +18446744073709551615 0 interrupted system call # 线程收到 SIGURG ,系统调用被信号中断 +sleep end +18446744073709551615 0 interrupted system call +sleep end +18446744073709551615 0 interrupted system call +sleep end +``` +综上,在使用 `RawSyscall` 的时候需要自行根据 golang 版本确定是否符合你的语气 ## vdso vdso 可以认为是一种特殊的调用,在使用时,没有本文开头的用户态到内核态的切换,引用一段参考资料: From c424be1bd5532b692161054d446af372768977df Mon Sep 17 00:00:00 2001 From: wziww Date: Fri, 26 Feb 2021 13:58:36 +0800 Subject: [PATCH 061/120] fix --- syscall.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syscall.md b/syscall.md index 6760847..7dfeafd 100644 --- a/syscall.md +++ b/syscall.md @@ -266,7 +266,7 @@ sleep end 18446744073709551615 0 interrupted system call sleep end ``` -综上,在使用 `RawSyscall` 的时候需要自行根据 golang 版本确定是否符合你的语气 +综上,在使用 `RawSyscall` 的时候需要自行根据 golang 版本确定是否符合你的预期 ## vdso vdso 可以认为是一种特殊的调用,在使用时,没有本文开头的用户态到内核态的切换,引用一段参考资料: From f834c9522e89e8410ab1042280d55d7278e16db2 Mon Sep 17 00:00:00 2001 From: wziww Date: Fri, 26 Feb 2021 14:01:44 +0800 Subject: [PATCH 062/120] fix code --- syscall.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/syscall.md b/syscall.md index 7dfeafd..8d3882a 100644 --- a/syscall.md +++ b/syscall.md @@ -189,7 +189,6 @@ package main import ( "fmt" - "os" "syscall" "time" "unsafe" @@ -205,8 +204,7 @@ const ( ) func main() { - var futexVar uint32 = uint32(os.Getegid()) - futexVar++ + var futexVar uint32 = 1 for i := 0; i < 3; i++ { go func() { // 启动 3 个线程进行阻塞系统调用 fmt.Println("sleep start") From 32fd5a45146f54e10cc0707c029db7cbcf5884cf Mon Sep 17 00:00:00 2001 From: wziww Date: Fri, 26 Feb 2021 18:49:19 +0800 Subject: [PATCH 063/120] =?UTF-8?q?=E7=BB=86=E8=8A=82=E8=A1=A5=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- syscall.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syscall.md b/syscall.md index 8d3882a..a60250c 100644 --- a/syscall.md +++ b/syscall.md @@ -182,7 +182,7 @@ golang 旧版本中 RawSyscall 和 Syscall 的区别也非常微小,就只是 RawSyscall 只是为了在执行那些一定不会阻塞的系统调用时,能节省两次对 runtime 的函数调用消耗。 #### 新版本抢占式调度中的 RawSyscall 和 Syscall -由于 `RawSyscall` 相较于 `Syscall` 缺少了 `runtime·entersyscall(SB)` 以及 `runtime·exitsyscall(SB)` 的调用,当 `g` 执行的是阻塞性质的系统调用的时候,当前 `g` 会维持 `running` 状态,runtime 系统监控在进行全局调度的时候一旦发现运行超过 10ms 的 `g` 就会执行抢占操作(1.14.3 版本, linux_amd64 下为例),通过发送信号量给 `g` 对应的线程,而由于线程在初始化的时候进行了信号量的监听以及设置了相应的 `sa_flags` 参数,会导致在收到信号量的时候对正在阻塞的系统调用产生中断,这种行为往往会给使用者带来意料之外的情况,以下通过一个简单的阻塞性系统调用案例来演示(futex): +由于 `RawSyscall` 相较于 `Syscall` 缺少了 `runtime·entersyscall(SB)` 以及 `runtime·exitsyscall(SB)` 的调用,当 `g` 执行的是阻塞性质的系统调用的时候,当前 `g` 会维持 `running` 状态,runtime 系统监控在进行全局调度的时候一旦发现运行超过 10ms 的 `g` 就会执行抢占操作(1.14.3 版本, linux_amd64 下为例),通过发送信号量给 `g` 对应的线程,而由于线程在初始化的时候进行了信号量的监听以及设置了相应的 `sa_flags` 参数,虽然包含诸如`SA_RESTART`参数会让系统调用在信号中断后自动恢复,但是不是对所有系统调用都会有效,这将会导致在收到信号量的时候对正在阻塞的系统调用产生中断,这种行为往往会给使用者带来意料之外的情况,以下通过一个简单的阻塞性系统调用案例来演示(futex): ```go // futex package main From dede52c75ac698ab98381d6ab9f40d71e99aaa6c Mon Sep 17 00:00:00 2001 From: wziww Date: Tue, 9 Mar 2021 18:04:53 +0800 Subject: [PATCH 064/120] =?UTF-8?q?sync.Map=20=E6=8B=93=E5=B1=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sync.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sync.md b/sync.md index 0e9fb5a..501fa84 100644 --- a/sync.md +++ b/sync.md @@ -1004,7 +1004,9 @@ func (e *entry) tryExpungeLocked() (isExpunged bool) { return p == expunged } ``` - +- sync.Map 利用了读写分离的思路为读多写少或读写不同 key 的场景而设计,当违背这种设计初衷来使用 sync.Map 的时候性能或许达不到你的期待 +- 可以参考下其他诸如散列思路减少锁开销的并发安全 [Map](https://github.com/orcaman/concurrent-map/ +) # 参考资料 http://www.weixianmanbu.com/article/736.html From e73f3b5e53604063d398a6d275835a83d0a2527a Mon Sep 17 00:00:00 2001 From: Xargin Date: Sun, 28 Mar 2021 01:44:35 +0800 Subject: [PATCH 065/120] fix asm --- assembly.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assembly.md b/assembly.md index 2fc8e4b..a246c16 100644 --- a/assembly.md +++ b/assembly.md @@ -10,7 +10,7 @@ ### 栈调整 -intel 或 AT&T 汇编提供了 push 和 pop 指令族,plan9 中没有 push 和 pop,栈的调整是通过对硬件 SP 寄存器进行运算来实现的,例如: +intel 或 AT&T 汇编提供了 push 和 pop 指令族,~~plan9 中没有 push 和 pop~~,plan9 中虽然有 push 和 pop 指令,但一般生成的代码中是没有的,我们看到的栈的调整大多是通过对硬件 SP 寄存器进行运算来实现的,例如: ```go SUBQ $0x18, SP // 对 SP 做减法,为函数分配函数栈帧 From d1e8b4ba53989454dd7f62d9e9cec060bc707870 Mon Sep 17 00:00:00 2001 From: wziww Date: Wed, 31 Mar 2021 19:57:09 +0800 Subject: [PATCH 066/120] interface - eface --- interface.md | 225 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 220 insertions(+), 5 deletions(-) diff --git a/interface.md b/interface.md index 2d830dc..19daf4b 100644 --- a/interface.md +++ b/interface.md @@ -1,8 +1,223 @@ # interface +## eface +#### 从类型定义说起 +首先来看一段代码 +```go +package main -## iface 和 eface +type eface_struct struct { + f float32 + i int +} -> Written with [StackEdit](https://stackedit.io/). - \ No newline at end of file +func main() { + var test_interface interface{} + test_interface = eface_struct{} + _ = test_interface +} +``` +在这段简单的代码中我们声明了 `test_interface` 是一个不含任何函数的基础 `interface` 结构,并把 `eface_struct` 类型结构体赋予了它 + +那么我们来看看编译器是如何处理`声明`这一过程的 +> go tool compile -l -S -N ./test.go +注意这边将编译器优化关闭`-N`,以便我们更好的看看编译器的工作流程 + +```assembly +(... 以下已去除多余代码) +"".main STEXT nosplit size=91 args=0x0 locals=0x38 + 0x0000 00000 (./test.go:8) TEXT "".main(SB), NOSPLIT|ABIInternal, $56-0 + 0x0000 00000 (./test.go:8) SUBQ $56, SP + 0x0004 00004 (./test.go:8) MOVQ BP, 48(SP) + 0x0009 00009 (./test.go:8) LEAQ 48(SP), BP + 0x000e 00014 (./test.go:8) PCDATA $0, $-2 + 0x000e 00014 (./test.go:8) PCDATA $1, $-2 + 0x000e 00014 (./test.go:8) FUNCDATA $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) + 0x000e 00014 (./test.go:8) FUNCDATA $1, gclocals·f207267fbf96a0178e8758c6e3e0ce28(SB) + 0x000e 00014 (./test.go:8) FUNCDATA $2, gclocals·9fb7f0986f647f17cb53dda1484e0f7a(SB) + 0x000e 00014 (./test.go:9) PCDATA $0, $0 + 0x000e 00014 (./test.go:9) PCDATA $1, $0 + 0x000e 00014 (./test.go:9) XORPS X0, X0 + 0x0011 00017 (./test.go:9) MOVUPS X0, "".test_interface+32(SP) + 0x0016 00022 (./test.go:10) XORPS X0, X0 + 0x0019 00025 (./test.go:10) MOVSS X0, ""..autotmp_1+16(SP) + 0x001f 00031 (./test.go:10) MOVQ $0, ""..autotmp_1+24(SP) + 0x0028 00040 (./test.go:10) MOVSS ""..autotmp_1+16(SP), X0 + 0x002e 00046 (./test.go:10) MOVSS X0, ""..autotmp_2(SP) + 0x0033 00051 (./test.go:10) MOVQ $0, ""..autotmp_2+8(SP) + 0x003c 00060 (./test.go:10) PCDATA $0, $1 + 0x003c 00060 (./test.go:10) LEAQ type."".eface_struct(SB), AX + 0x0043 00067 (./test.go:10) PCDATA $0, $0 + 0x0043 00067 (./test.go:10) MOVQ AX, "".test_interface+32(SP) + 0x0048 00072 (./test.go:10) PCDATA $0, $1 + 0x0048 00072 (./test.go:10) LEAQ ""..autotmp_2(SP), AX + 0x004c 00076 (./test.go:10) PCDATA $0, $0 + 0x004c 00076 (./test.go:10) MOVQ AX, "".test_interface+40(SP) + 0x0051 00081 (./test.go:12) MOVQ 48(SP), BP + 0x0056 00086 (./test.go:12) ADDQ $56, SP + 0x005a 00090 (./test.go:12) RET + 0x0000 48 83 ec 38 48 89 6c 24 30 48 8d 6c 24 30 0f 57 H..8H.l$0H.l$0.W + 0x0010 c0 0f 11 44 24 20 0f 57 c0 f3 0f 11 44 24 10 48 ...D$ .W....D$.H + 0x0020 c7 44 24 18 00 00 00 00 f3 0f 10 44 24 10 f3 0f .D$........D$... + 0x0030 11 04 24 48 c7 44 24 08 00 00 00 00 48 8d 05 00 ..$H.D$.....H... + 0x0040 00 00 00 48 89 44 24 20 48 8d 04 24 48 89 44 24 ...H.D$ H..$H.D$ + 0x0050 28 48 8b 6c 24 30 48 83 c4 38 c3 (H.l$0H..8. + rel 63+4 t=16 type."".eface_struct+0 ...i +type."".eface_struct SRODATA size=144 + 0x0000 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + 0x0010 ca b4 29 a7 07 08 08 19 00 00 00 00 00 00 00 00 ..)............. + 0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + 0x0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + 0x0040 02 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 ................ + 0x0050 00 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 ........@....... + 0x0060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + 0x0070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + 0x0080 00 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 ................ + rel 24+8 t=1 type..eqfunc."".eface_struct+0 + rel 32+8 t=1 runtime.gcbits.+0 + rel 40+4 t=5 type..namedata.*main.eface_struct-+0 + rel 44+4 t=5 type.*"".eface_struct+0 + rel 48+8 t=1 type..importpath."".+0 + rel 56+8 t=1 type."".eface_struct+96 + rel 80+4 t=5 type..importpath."".+0 + rel 96+8 t=1 type..namedata.f-+0 + rel 104+8 t=1 type.float32+0 + rel 120+8 t=1 type..namedata.i-+0 + rel 128+8 t=1 type.int+0 +``` +那么其中的 +```assembly + 0x003c 00060 (./test.go:10) LEAQ type."".eface_struct(SB), AX + 0x0043 00067 (./test.go:10) PCDATA $0, $0 + 0x0043 00067 (./test.go:10) MOVQ AX, "".test_interface+32(SP) + 0x0048 00072 (./test.go:10) PCDATA $0, $1 + 0x0048 00072 (./test.go:10) LEAQ ""..autotmp_2(SP), AX + 0x004c 00076 (./test.go:10) PCDATA $0, $0 + 0x004c 00076 (./test.go:10) MOVQ AX, "".test_interface+40(SP) +``` +就是对 `test_interface` 变量的赋值过程了,可以看到首先将`type."".eface_struct`放到了该变量的首字节地址上 + +##### type."".eface_struct 是什么? +在声明一个结构体的同时, 编译器会在只读内存段定义对应的 `*face` 结构,在平时正常使用 `struct` 的时候,编译器会通过用户定义的结构进行内存布局的计算来满足结构体的实现,这是属于普通情况的使用,而一旦涉及到`interface`,那么这个`*face`结构就开始发挥它的作用了 +##### interface 的声明和赋值过程到底干了些什么? + +> var test_interface interface{} +test_interface = eface_struct{} + +让我们来看看对 `test_interface` 这个 `interface` 类型变量赋值的过程 +对应 assembly: +```assembly + # 将 type."".eface_struct 这个 *face 结构插入 test_interface 首地址 + x003c 00060 (./test.go:10) LEAQ type."".eface_struct(SB), AX + 0x0043 00067 (./test.go:10) PCDATA $0, $0 + 0x0043 00067 (./test.go:10) MOVQ AX, "".test_interface+32(SP) # 不用纠结这边的 32 偏移量,与局部变量有关,可认为 test_interface 起始地址为 32(SP) + 0x0048 00072 (./test.go:10) PCDATA $0, $1 + 0x0048 00072 (./test.go:10) LEAQ ""..autotmp_2(SP), AX + 0x004c 00076 (./test.go:10) PCDATA $0, $0 + # test_interface 数据部分值插入,偏移量为 40-32 = 8 + 0x004c 00076 (./test.go:10) MOVQ AX, "".test_interface+40(SP) +``` +这边的案例首先分析的是不包含 `func` 的 `interface`,对应的 `*face` 结构为 `eface` +```go +// src/runtime/runtime2.go +type eface struct { + _type *_type // size 8 正好对应上述 assembly 部分 *face 结构插入 + data unsafe.Pointer // 数据部分 +} +// src/runtime/type.go +type _type struct { + size uintptr + ptrdata uintptr // size of memory prefix holding all pointers + hash uint32 + tflag tflag + align uint8 + fieldAlign uint8 + kind uint8 + // function for comparing objects of this type + // (ptr to object A, ptr to object B) -> ==? + equal func(unsafe.Pointer, unsafe.Pointer) bool + // gcdata stores the GC type data for the garbage collector. + // If the KindGCProg bit is set in kind, gcdata is a GC program. + // Otherwise it is a ptrmask bitmap. See mbitmap.go for details. + gcdata *byte + str nameOff + ptrToThis typeOff +} +``` +再看 `_type` 结构,那么有了到目前为止的分析,我们可以试着来将该结构与编译器自动生成的`type."".eface_struct`对应着拆解看看 +```assembly +type."".eface_struct SRODATA size=144 + 0x0000 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + 0x0010 ca b4 29 a7 07 08 08 19 00 00 00 00 00 00 00 00 ..)............. + 0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + 0x0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + 0x0040 02 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 ................ + 0x0050 00 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 ........@....... + 0x0060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + 0x0070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + 0x0080 00 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 ................ + #------------------------ 24 字节 ---------------- + rel 24+8 t=1 type..eqfunc."".eface_struct+0 # 对应 _type.equal + rel 32+8 t=1 runtime.gcbits.+0 + rel 40+4 t=5 type..namedata.*main.eface_struct-+0 + rel 44+4 t=5 type.*"".eface_struct+0 + rel 48+8 t=1 type..importpath."".+0 + rel 56+8 t=1 type."".eface_struct+96 + rel 80+4 t=5 type..importpath."".+0 + rel 96+8 t=1 type..namedata.f-+0 + rel 104+8 t=1 type.float32+0 + rel 120+8 t=1 type..namedata.i-+0 + rel 128+8 t=1 type.int+0 +``` + +```go +fmt.Println(reflect.TypeOf(test_interface)) +/* + * reflect.rtype + * |- size: 16 + * |- ptrdata:0 + * |- hash 2804528330 + const ( + // tflagUncommon means that there is a pointer, *uncommonType, + // just beyond the outer type structure. + // + // For example, if t.Kind() == Struct and t.tflag&tflagUncommon != 0, + // then t has uncommonType data and it can be accessed as: + // + // type tUncommon struct { + // structType + // u uncommonType + // } + // u := &(*tUncommon)(unsafe.Pointer(t)).u + tflagUncommon tflag = 1 << 0 + + // tflagExtraStar means the name in the str field has an + // extraneous '*' prefix. This is because for most types T in + // a program, the type *T also exists and reusing the str data + // saves binary size. + tflagExtraStar tflag = 1 << 1 + + // tflagNamed means the type has a name. + tflagNamed tflag = 1 << 2 + + // tflagRegularMemory means that equal and hash functions can treat + // this type as a single region of t.size bytes. + tflagRegularMemory tflag = 1 << 3 +) + * |- tflag:tflagUncommon|tflagExtraStar|tflagNamed (7) + * |- align:8 + * |- fieldAlign:8 + * |- kind:25 + * |- equal:type..eq.main.eface_struct + * |- gcdata:<*uint8>(0x11004e6) + * |- str:23831 nameOff,通过该字段及 base pointer 计算偏移量来获取 struct name 相关信息 + * |_ ptrToThis:38080 + */ +``` +- size:16 + - 0x 00 00 00 00 00 00 00 10 +- ptrdata:0 + - 0x 00 00 00 00 00 00 00 00 +- hash:2804528330 + - 0x a7 29 b4 ca + +大小端的关系,和上述`assembly`中的前 20 个字节顺序相反,分析到这儿就可以知道`interface`结构整体的情况,以及起核心作用的为`*face`字段,此处分析了`eface`,而另一种带有函数实现的`interface`的`iface`结构也可以通过相同的过程拆解,值得一提的是`iface`结构首字段的`itab`结构内部也内置了 `_type`结构,所以编译器可以通过相同的赋值过程处理这两种类型的`interface` \ No newline at end of file From 47d2b673e6d29efbf8a4ebdfc5daa0e844b91d6b Mon Sep 17 00:00:00 2001 From: wziww Date: Wed, 31 Mar 2021 20:07:19 +0800 Subject: [PATCH 067/120] =?UTF-8?q?=E8=A1=A5=E5=85=85=20interface=20?= =?UTF-8?q?=E5=AE=9A=E4=B9=89=E5=8F=8A=E8=B5=8B=E5=80=BC=E9=98=B6=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- interface.md | 223 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 223 insertions(+) diff --git a/interface.md b/interface.md index 413e74b..703507d 100644 --- a/interface.md +++ b/interface.md @@ -1,3 +1,226 @@ + +#### 从类型定义说起 +首先来看一段代码 +```go +package main + +type eface_struct struct { + f float32 + i int +} + +func main() { + var test_interface interface{} + test_interface = eface_struct{} + _ = test_interface +} +``` +在这段简单的代码中我们声明了 `test_interface` 是一个不含任何函数的基础 `interface` 结构,并把 `eface_struct` 类型结构体赋予了它 + +那么我们来看看编译器是如何处理`声明`这一过程的 +> go tool compile -l -S -N ./test.go +> 注意这边将编译器优化关闭`-N`,以便我们更好的看看编译器的工作流程 + + +```assembly +(... 以下已去除多余代码) +"".main STEXT nosplit size=91 args=0x0 locals=0x38 + 0x0000 00000 (./test.go:8) TEXT "".main(SB), NOSPLIT|ABIInternal, $56-0 + 0x0000 00000 (./test.go:8) SUBQ $56, SP + 0x0004 00004 (./test.go:8) MOVQ BP, 48(SP) + 0x0009 00009 (./test.go:8) LEAQ 48(SP), BP + 0x000e 00014 (./test.go:8) PCDATA $0, $-2 + 0x000e 00014 (./test.go:8) PCDATA $1, $-2 + 0x000e 00014 (./test.go:8) FUNCDATA $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) + 0x000e 00014 (./test.go:8) FUNCDATA $1, gclocals·f207267fbf96a0178e8758c6e3e0ce28(SB) + 0x000e 00014 (./test.go:8) FUNCDATA $2, gclocals·9fb7f0986f647f17cb53dda1484e0f7a(SB) + 0x000e 00014 (./test.go:9) PCDATA $0, $0 + 0x000e 00014 (./test.go:9) PCDATA $1, $0 + 0x000e 00014 (./test.go:9) XORPS X0, X0 + 0x0011 00017 (./test.go:9) MOVUPS X0, "".test_interface+32(SP) + 0x0016 00022 (./test.go:10) XORPS X0, X0 + 0x0019 00025 (./test.go:10) MOVSS X0, ""..autotmp_1+16(SP) + 0x001f 00031 (./test.go:10) MOVQ $0, ""..autotmp_1+24(SP) + 0x0028 00040 (./test.go:10) MOVSS ""..autotmp_1+16(SP), X0 + 0x002e 00046 (./test.go:10) MOVSS X0, ""..autotmp_2(SP) + 0x0033 00051 (./test.go:10) MOVQ $0, ""..autotmp_2+8(SP) + 0x003c 00060 (./test.go:10) PCDATA $0, $1 + 0x003c 00060 (./test.go:10) LEAQ type."".eface_struct(SB), AX + 0x0043 00067 (./test.go:10) PCDATA $0, $0 + 0x0043 00067 (./test.go:10) MOVQ AX, "".test_interface+32(SP) + 0x0048 00072 (./test.go:10) PCDATA $0, $1 + 0x0048 00072 (./test.go:10) LEAQ ""..autotmp_2(SP), AX + 0x004c 00076 (./test.go:10) PCDATA $0, $0 + 0x004c 00076 (./test.go:10) MOVQ AX, "".test_interface+40(SP) + 0x0051 00081 (./test.go:12) MOVQ 48(SP), BP + 0x0056 00086 (./test.go:12) ADDQ $56, SP + 0x005a 00090 (./test.go:12) RET + 0x0000 48 83 ec 38 48 89 6c 24 30 48 8d 6c 24 30 0f 57 H..8H.l$0H.l$0.W + 0x0010 c0 0f 11 44 24 20 0f 57 c0 f3 0f 11 44 24 10 48 ...D$ .W....D$.H + 0x0020 c7 44 24 18 00 00 00 00 f3 0f 10 44 24 10 f3 0f .D$........D$... + 0x0030 11 04 24 48 c7 44 24 08 00 00 00 00 48 8d 05 00 ..$H.D$.....H... + 0x0040 00 00 00 48 89 44 24 20 48 8d 04 24 48 89 44 24 ...H.D$ H..$H.D$ + 0x0050 28 48 8b 6c 24 30 48 83 c4 38 c3 (H.l$0H..8. + rel 63+4 t=16 type."".eface_struct+0 ...i +type."".eface_struct SRODATA size=144 + 0x0000 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + 0x0010 ca b4 29 a7 07 08 08 19 00 00 00 00 00 00 00 00 ..)............. + 0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + 0x0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + 0x0040 02 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 ................ + 0x0050 00 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 ........@....... + 0x0060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + 0x0070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + 0x0080 00 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 ................ + rel 24+8 t=1 type..eqfunc."".eface_struct+0 + rel 32+8 t=1 runtime.gcbits.+0 + rel 40+4 t=5 type..namedata.*main.eface_struct-+0 + rel 44+4 t=5 type.*"".eface_struct+0 + rel 48+8 t=1 type..importpath."".+0 + rel 56+8 t=1 type."".eface_struct+96 + rel 80+4 t=5 type..importpath."".+0 + rel 96+8 t=1 type..namedata.f-+0 + rel 104+8 t=1 type.float32+0 + rel 120+8 t=1 type..namedata.i-+0 + rel 128+8 t=1 type.int+0 +``` +那么其中的 +```assembly + 0x003c 00060 (./test.go:10) LEAQ type."".eface_struct(SB), AX + 0x0043 00067 (./test.go:10) PCDATA $0, $0 + 0x0043 00067 (./test.go:10) MOVQ AX, "".test_interface+32(SP) + 0x0048 00072 (./test.go:10) PCDATA $0, $1 + 0x0048 00072 (./test.go:10) LEAQ ""..autotmp_2(SP), AX + 0x004c 00076 (./test.go:10) PCDATA $0, $0 + 0x004c 00076 (./test.go:10) MOVQ AX, "".test_interface+40(SP) +``` +就是对 `test_interface` 变量的赋值过程了,可以看到首先将`type."".eface_struct`放到了该变量的首字节地址上 + +##### type."".eface_struct 是什么? +在声明一个结构体的同时, 编译器会在只读内存段定义对应的 `*face` 结构,在平时正常使用 `struct` 的时候,编译器会通过用户定义的结构进行内存布局的计算来满足结构体的实现,这是属于普通情况的使用,而一旦涉及到`interface`,那么这个`*face`结构就开始发挥它的作用了 +##### interface 的声明和赋值过程到底干了些什么? + +> var test_interface interface{} +> test_interface = eface_struct{} + +让我们来看看对 `test_interface` 这个 `interface` 类型变量赋值的过程 +对应 assembly: +```assembly + # 将 type."".eface_struct 这个 *face 结构插入 test_interface 首地址 + x003c 00060 (./test.go:10) LEAQ type."".eface_struct(SB), AX + 0x0043 00067 (./test.go:10) PCDATA $0, $0 + 0x0043 00067 (./test.go:10) MOVQ AX, "".test_interface+32(SP) # 不用纠结这边的 32 偏移量,与局部变量有关,可认为 test_interface 起始地址为 32(SP) + 0x0048 00072 (./test.go:10) PCDATA $0, $1 + 0x0048 00072 (./test.go:10) LEAQ ""..autotmp_2(SP), AX + 0x004c 00076 (./test.go:10) PCDATA $0, $0 + # test_interface 数据部分值插入,偏移量为 40-32 = 8 + 0x004c 00076 (./test.go:10) MOVQ AX, "".test_interface+40(SP) +``` +这边的案例首先分析的是不包含 `func` 的 `interface`,对应的 `*face` 结构为 `eface` +```go +// src/runtime/runtime2.go +type eface struct { + _type *_type // size 8 正好对应上述 assembly 部分 *face 结构插入 + data unsafe.Pointer // 数据部分 +} +// src/runtime/type.go +type _type struct { + size uintptr + ptrdata uintptr // size of memory prefix holding all pointers + hash uint32 + tflag tflag + align uint8 + fieldAlign uint8 + kind uint8 + // function for comparing objects of this type + // (ptr to object A, ptr to object B) -> ==? + equal func(unsafe.Pointer, unsafe.Pointer) bool + // gcdata stores the GC type data for the garbage collector. + // If the KindGCProg bit is set in kind, gcdata is a GC program. + // Otherwise it is a ptrmask bitmap. See mbitmap.go for details. + gcdata *byte + str nameOff + ptrToThis typeOff +} +``` +再看 `_type` 结构,那么有了到目前为止的分析,我们可以试着来将该结构与编译器自动生成的`type."".eface_struct`对应着拆解看看 +```assembly +type."".eface_struct SRODATA size=144 + 0x0000 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + 0x0010 ca b4 29 a7 07 08 08 19 00 00 00 00 00 00 00 00 ..)............. + 0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + 0x0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + 0x0040 02 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 ................ + 0x0050 00 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 ........@....... + 0x0060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + 0x0070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + 0x0080 00 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 ................ + #------------------------ 24 字节 ---------------- + rel 24+8 t=1 type..eqfunc."".eface_struct+0 # 对应 _type.equal + rel 32+8 t=1 runtime.gcbits.+0 + rel 40+4 t=5 type..namedata.*main.eface_struct-+0 + rel 44+4 t=5 type.*"".eface_struct+0 + rel 48+8 t=1 type..importpath."".+0 + rel 56+8 t=1 type."".eface_struct+96 + rel 80+4 t=5 type..importpath."".+0 + rel 96+8 t=1 type..namedata.f-+0 + rel 104+8 t=1 type.float32+0 + rel 120+8 t=1 type..namedata.i-+0 + rel 128+8 t=1 type.int+0 +``` + +```go +fmt.Println(reflect.TypeOf(test_interface)) +/* + * reflect.rtype + * |- size: 16 + * |- ptrdata:0 + * |- hash 2804528330 + const ( + // tflagUncommon means that there is a pointer, *uncommonType, + // just beyond the outer type structure. + // + // For example, if t.Kind() == Struct and t.tflag&tflagUncommon != 0, + // then t has uncommonType data and it can be accessed as: + // + // type tUncommon struct { + // structType + // u uncommonType + // } + // u := &(*tUncommon)(unsafe.Pointer(t)).u + tflagUncommon tflag = 1 << 0 + + // tflagExtraStar means the name in the str field has an + // extraneous '*' prefix. This is because for most types T in + // a program, the type *T also exists and reusing the str data + // saves binary size. + tflagExtraStar tflag = 1 << 1 + + // tflagNamed means the type has a name. + tflagNamed tflag = 1 << 2 + + // tflagRegularMemory means that equal and hash functions can treat + // this type as a single region of t.size bytes. + tflagRegularMemory tflag = 1 << 3 +) + * |- tflag:tflagUncommon|tflagExtraStar|tflagNamed (7) + * |- align:8 + * |- fieldAlign:8 + * |- kind:25 + * |- equal:type..eq.main.eface_struct + * |- gcdata:<*uint8>(0x11004e6) + * |- str:23831 nameOff,通过该字段及 base pointer 计算偏移量来获取 struct name 相关信息 + * |_ ptrToThis:38080 + */ +``` +- size:16 + - 0x 00 00 00 00 00 00 00 10 +- ptrdata:0 + - 0x 00 00 00 00 00 00 00 00 +- hash:2804528330 + - 0x a7 29 b4 ca + +大小端的关系,和上述`assembly`中的前 20 个字节顺序相反,分析到这儿就可以知道`interface`结构整体的情况,以及起核心作用的为`*face`字段,此处分析了`eface`,而另一种带有函数实现的`interface`的`iface`结构也可以通过相同的过程拆解,值得一提的是`iface`结构首字段的`itab`结构内部也内置了 `_type`结构,所以编译器可以通过相同的赋值过程处理这两种类型的`interface` # iface 和 eface Go 使用 iface 和 eface 来表达 interface。iface 其实就是 interface,这种接口内部是有函数的。 From b389febf8993051ee95c3a83eee70c914a76f83e Mon Sep 17 00:00:00 2001 From: Xargin Date: Wed, 31 Mar 2021 22:25:41 +0800 Subject: [PATCH 068/120] add wechat --- images/gzh.png | Bin 0 -> 221411 bytes readme.md | 3 +++ 2 files changed, 3 insertions(+) create mode 100644 images/gzh.png diff --git a/images/gzh.png b/images/gzh.png new file mode 100644 index 0000000000000000000000000000000000000000..58046bbc349521b3b3614f450999ed53a2b1ef79 GIT binary patch literal 221411 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