Skip to content

Commit fdb74cc

Browse files
authored
Merge pull request cch123#26 from wziww/master
新增 atomic 详细分析
2 parents 5314c42 + 6857406 commit fdb74cc

File tree

3 files changed

+364
-0
lines changed

3 files changed

+364
-0
lines changed

atomic.md

Lines changed: 362 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,362 @@
1+
# atomic 详解
2+
> golang 中 atomic 类操作最终是使用 assembly 进行 cpu 指令调用实现的。本章将会对相应 api 及对应 assembly 进行分析
3+
> 本章所有函数分析均以 amd64 架构为例
4+
5+
sync/atomic/doc.go
6+
```go
7+
// Copyright 2011 The Go Authors. All rights reserved.
8+
// Use of this source code is governed by a BSD-style
9+
// license that can be found in the LICENSE file.
10+
11+
// Package atomic provides low-level atomic memory primitives
12+
// useful for implementing synchronization algorithms.
13+
//
14+
// These functions require great care to be used correctly.
15+
// Except for special, low-level applications, synchronization is better
16+
// done with channels or the facilities of the sync package.
17+
// Share memory by communicating;
18+
// don't communicate by sharing memory.
19+
//
20+
// The swap operation, implemented by the SwapT functions, is the atomic
21+
// equivalent of:
22+
//
23+
// old = *addr
24+
// *addr = new
25+
// return old
26+
//
27+
// The compare-and-swap operation, implemented by the CompareAndSwapT
28+
// functions, is the atomic equivalent of:
29+
//
30+
// if *addr == old {
31+
// *addr = new
32+
// return true
33+
// }
34+
// return false
35+
//
36+
// The add operation, implemented by the AddT functions, is the atomic
37+
// equivalent of:
38+
//
39+
// *addr += delta
40+
// return *addr
41+
//
42+
// The load and store operations, implemented by the LoadT and StoreT
43+
// functions, are the atomic equivalents of "return *addr" and
44+
// "*addr = val".
45+
//
46+
package atomic
47+
48+
import (
49+
"unsafe"
50+
)
51+
52+
// BUG(rsc): On x86-32, the 64-bit functions use instructions unavailable before the Pentium MMX.
53+
//
54+
// On non-Linux ARM, the 64-bit functions use instructions unavailable before the ARMv6k core.
55+
//
56+
// On ARM, x86-32, and 32-bit MIPS,
57+
// it is the caller's responsibility to arrange for 64-bit
58+
// alignment of 64-bit words accessed atomically. The first word in a
59+
// variable or in an allocated struct, array, or slice can be relied upon to be
60+
// 64-bit aligned.
61+
62+
// SwapInt32 atomically stores new into *addr and returns the previous *addr value.
63+
func SwapInt32(addr *int32, new int32) (old int32)
64+
65+
// SwapInt64 atomically stores new into *addr and returns the previous *addr value.
66+
func SwapInt64(addr *int64, new int64) (old int64)
67+
68+
// SwapUint32 atomically stores new into *addr and returns the previous *addr value.
69+
func SwapUint32(addr *uint32, new uint32) (old uint32)
70+
71+
// SwapUint64 atomically stores new into *addr and returns the previous *addr value.
72+
func SwapUint64(addr *uint64, new uint64) (old uint64)
73+
74+
// SwapUintptr atomically stores new into *addr and returns the previous *addr value.
75+
func SwapUintptr(addr *uintptr, new uintptr) (old uintptr)
76+
77+
// SwapPointer atomically stores new into *addr and returns the previous *addr value.
78+
func SwapPointer(addr *unsafe.Pointer, new unsafe.Pointer) (old unsafe.Pointer)
79+
80+
// CompareAndSwapInt32 executes the compare-and-swap operation for an int32 value.
81+
func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool)
82+
83+
// CompareAndSwapInt64 executes the compare-and-swap operation for an int64 value.
84+
func CompareAndSwapInt64(addr *int64, old, new int64) (swapped bool)
85+
86+
// CompareAndSwapUint32 executes the compare-and-swap operation for a uint32 value.
87+
func CompareAndSwapUint32(addr *uint32, old, new uint32) (swapped bool)
88+
89+
// CompareAndSwapUint64 executes the compare-and-swap operation for a uint64 value.
90+
func CompareAndSwapUint64(addr *uint64, old, new uint64) (swapped bool)
91+
92+
// CompareAndSwapUintptr executes the compare-and-swap operation for a uintptr value.
93+
func CompareAndSwapUintptr(addr *uintptr, old, new uintptr) (swapped bool)
94+
95+
// CompareAndSwapPointer executes the compare-and-swap operation for a unsafe.Pointer value.
96+
func CompareAndSwapPointer(addr *unsafe.Pointer, old, new unsafe.Pointer) (swapped bool)
97+
98+
// AddInt32 atomically adds delta to *addr and returns the new value.
99+
func AddInt32(addr *int32, delta int32) (new int32)
100+
101+
// AddUint32 atomically adds delta to *addr and returns the new value.
102+
// To subtract a signed positive constant value c from x, do AddUint32(&x, ^uint32(c-1)).
103+
// In particular, to decrement x, do AddUint32(&x, ^uint32(0)).
104+
func AddUint32(addr *uint32, delta uint32) (new uint32)
105+
106+
// AddInt64 atomically adds delta to *addr and returns the new value.
107+
func AddInt64(addr *int64, delta int64) (new int64)
108+
109+
// AddUint64 atomically adds delta to *addr and returns the new value.
110+
// To subtract a signed positive constant value c from x, do AddUint64(&x, ^uint64(c-1)).
111+
// In particular, to decrement x, do AddUint64(&x, ^uint64(0)).
112+
func AddUint64(addr *uint64, delta uint64) (new uint64)
113+
114+
// AddUintptr atomically adds delta to *addr and returns the new value.
115+
func AddUintptr(addr *uintptr, delta uintptr) (new uintptr)
116+
117+
// LoadInt32 atomically loads *addr.
118+
func LoadInt32(addr *int32) (val int32)
119+
120+
// LoadInt64 atomically loads *addr.
121+
func LoadInt64(addr *int64) (val int64)
122+
123+
// LoadUint32 atomically loads *addr.
124+
func LoadUint32(addr *uint32) (val uint32)
125+
126+
// LoadUint64 atomically loads *addr.
127+
func LoadUint64(addr *uint64) (val uint64)
128+
129+
// LoadUintptr atomically loads *addr.
130+
func LoadUintptr(addr *uintptr) (val uintptr)
131+
132+
// LoadPointer atomically loads *addr.
133+
func LoadPointer(addr *unsafe.Pointer) (val unsafe.Pointer)
134+
135+
// StoreInt32 atomically stores val into *addr.
136+
func StoreInt32(addr *int32, val int32)
137+
138+
// StoreInt64 atomically stores val into *addr.
139+
func StoreInt64(addr *int64, val int64)
140+
141+
// StoreUint32 atomically stores val into *addr.
142+
func StoreUint32(addr *uint32, val uint32)
143+
144+
// StoreUint64 atomically stores val into *addr.
145+
func StoreUint64(addr *uint64, val uint64)
146+
147+
// StoreUintptr atomically stores val into *addr.
148+
func StoreUintptr(addr *uintptr, val uintptr)
149+
150+
// StorePointer atomically stores val into *addr.
151+
func StorePointer(addr *unsafe.Pointer, val unsafe.Pointer)
152+
153+
// Helper for ARM. Linker will discard on other systems
154+
func panic64() {
155+
panic("sync/atomic: broken 64-bit atomic operations (buggy QEMU)")
156+
}
157+
158+
```
159+
> go 文件内只进行了函数的定义,具体实现为在 asm 文件中
160+
161+
sync/atomic/asm.s
162+
```assembly
163+
// Copyright 2011 The Go Authors. All rights reserved.
164+
// Use of this source code is governed by a BSD-style
165+
// license that can be found in the LICENSE file.
166+
167+
// +build !race
168+
169+
#include "textflag.h"
170+
171+
TEXT ·SwapInt32(SB),NOSPLIT,$0
172+
JMP runtime∕internal∕atomic·Xchg(SB)
173+
174+
TEXT ·SwapUint32(SB),NOSPLIT,$0
175+
JMP runtime∕internal∕atomic·Xchg(SB)
176+
177+
TEXT ·SwapInt64(SB),NOSPLIT,$0
178+
JMP runtime∕internal∕atomic·Xchg64(SB)
179+
180+
TEXT ·SwapUint64(SB),NOSPLIT,$0
181+
JMP runtime∕internal∕atomic·Xchg64(SB)
182+
183+
TEXT ·SwapUintptr(SB),NOSPLIT,$0
184+
JMP runtime∕internal∕atomic·Xchguintptr(SB)
185+
186+
TEXT ·CompareAndSwapInt32(SB),NOSPLIT,$0
187+
JMP runtime∕internal∕atomic·Cas(SB)
188+
189+
TEXT ·CompareAndSwapUint32(SB),NOSPLIT,$0
190+
JMP runtime∕internal∕atomic·Cas(SB)
191+
192+
TEXT ·CompareAndSwapUintptr(SB),NOSPLIT,$0
193+
JMP runtime∕internal∕atomic·Casuintptr(SB)
194+
195+
TEXT ·CompareAndSwapInt64(SB),NOSPLIT,$0
196+
JMP runtime∕internal∕atomic·Cas64(SB)
197+
198+
TEXT ·CompareAndSwapUint64(SB),NOSPLIT,$0
199+
JMP runtime∕internal∕atomic·Cas64(SB)
200+
201+
TEXT ·AddInt32(SB),NOSPLIT,$0
202+
JMP runtime∕internal∕atomic·Xadd(SB)
203+
204+
TEXT ·AddUint32(SB),NOSPLIT,$0
205+
JMP runtime∕internal∕atomic·Xadd(SB)
206+
207+
TEXT ·AddUintptr(SB),NOSPLIT,$0
208+
JMP runtime∕internal∕atomic·Xadduintptr(SB)
209+
210+
TEXT ·AddInt64(SB),NOSPLIT,$0
211+
JMP runtime∕internal∕atomic·Xadd64(SB)
212+
213+
TEXT ·AddUint64(SB),NOSPLIT,$0
214+
JMP runtime∕internal∕atomic·Xadd64(SB)
215+
216+
TEXT ·LoadInt32(SB),NOSPLIT,$0
217+
JMP runtime∕internal∕atomic·Load(SB)
218+
219+
TEXT ·LoadUint32(SB),NOSPLIT,$0
220+
JMP runtime∕internal∕atomic·Load(SB)
221+
222+
TEXT ·LoadInt64(SB),NOSPLIT,$0
223+
JMP runtime∕internal∕atomic·Load64(SB)
224+
225+
TEXT ·LoadUint64(SB),NOSPLIT,$0
226+
JMP runtime∕internal∕atomic·Load64(SB)
227+
228+
TEXT ·LoadUintptr(SB),NOSPLIT,$0
229+
JMP runtime∕internal∕atomic·Loaduintptr(SB)
230+
231+
TEXT ·LoadPointer(SB),NOSPLIT,$0
232+
JMP runtime∕internal∕atomic·Loadp(SB)
233+
234+
TEXT ·StoreInt32(SB),NOSPLIT,$0
235+
JMP runtime∕internal∕atomic·Store(SB)
236+
237+
TEXT ·StoreUint32(SB),NOSPLIT,$0
238+
JMP runtime∕internal∕atomic·Store(SB)
239+
240+
TEXT ·StoreInt64(SB),NOSPLIT,$0
241+
JMP runtime∕internal∕atomic·Store64(SB)
242+
243+
TEXT ·StoreUint64(SB),NOSPLIT,$0
244+
JMP runtime∕internal∕atomic·Store64(SB)
245+
246+
TEXT ·StoreUintptr(SB),NOSPLIT,$0
247+
JMP runtime∕internal∕atomic·Storeuintptr(SB)
248+
249+
```
250+
可以看到这边函数的实现仅为 JMP 跳转指令,统一跳转到 runtime/internal/atomic 下的各个函数进行
251+
252+
253+
runtime/internal/atomic/stubs.go
254+
```go
255+
// Copyright 2015 The Go Authors. All rights reserved.
256+
// Use of this source code is governed by a BSD-style
257+
// license that can be found in the LICENSE file.
258+
259+
// +build !wasm
260+
261+
package atomic
262+
263+
import "unsafe"
264+
265+
//go:noescape
266+
func Cas(ptr *uint32, old, new uint32) bool
267+
268+
// NO go:noescape annotation; see atomic_pointer.go.
269+
func Casp1(ptr *unsafe.Pointer, old, new unsafe.Pointer) bool
270+
271+
//go:noescape
272+
func Casuintptr(ptr *uintptr, old, new uintptr) bool
273+
274+
//go:noescape
275+
func Storeuintptr(ptr *uintptr, new uintptr)
276+
277+
//go:noescape
278+
func Loaduintptr(ptr *uintptr) uintptr
279+
280+
//go:noescape
281+
func Loaduint(ptr *uint) uint
282+
283+
// TODO(matloob): Should these functions have the go:noescape annotation?
284+
285+
//go:noescape
286+
func Loadint64(ptr *int64) int64
287+
288+
//go:noescape
289+
func Xaddint64(ptr *int64, delta int64) int64
290+
291+
```
292+
#### func Cas(ptr *uint32, old, new uint32) bool
293+
- 入参为 uint32 指针,旧值,新值
294+
- 返回值为是否 cas 成功
295+
296+
go 文件内同样只进行了函数的定义,具体实现为在各平台的 asm 文件中:
297+
298+
runtime/internal/atomic/asm_amd64.s
299+
```assembly
300+
TEXT runtime∕internal∕atomic·Cas(SB),NOSPLIT,$0-17 // 17 = sizeof(*uint32 + sizeof(uint32) + sizeof(uint32) + sizeof(uint8/bool))
301+
MOVQ ptr+0(FP), BX // 入参 1 ,8 字节 uint32 指针
302+
MOVL old+8(FP), AX // 入参 2 ,4 字节 uint32
303+
MOVL new+12(FP), CX // 入参 3 ,4 字节 uint32
304+
LOCK // lock 指令前缀
305+
/*
306+
* CMPXCHGL r, [m]
307+
* if AX == [m] {
308+
* ZF = 1;
309+
* [m] = r;
310+
* } else {
311+
* ZF = 0;
312+
* AX = [m];
313+
* }
314+
*/
315+
CMPXCHGL CX, 0(BX) // 比较并交换指令, ZF set to 1 if success
316+
SETEQ ret+16(FP) // 1 if ZF set to 1
317+
RET
318+
```
319+
> ZF: 标志寄存器的一种,零标志:用于判断结果是否为0。运算结果0,ZF置1,否则置0。
320+
321+
#### func SwapInt32(addr *int32, new int32) (old int32)
322+
> cmd/compile/internal/gc/ssa.go
323+
> alias("sync/atomic", "SwapInt32", "runtime/internal/atomic", "Xchg", all...)
324+
> 以上可知 SwapInt32 函数为 runtime/internal/atomic·Xchg 函数的一个别名。
325+
326+
runtime/internal/atomic/asm_amd64.s
327+
```assembly
328+
TEXT runtime∕internal∕atomic·Xchg(SB), NOSPLIT, $0-20
329+
MOVQ ptr+0(FP), BX
330+
MOVL new+8(FP), AX
331+
XCHGL AX, 0(BX) // 交换指令
332+
MOVL AX, ret+16(FP) // 交换后的 AX(old value) 写入 FP 返回值位
333+
RET
334+
```
335+
336+
#### func AddInt32(addr *int32, new int32) (old int32)
337+
> alias("sync/atomic", "AddInt32", "runtime/internal/atomic", "Xadd", all...)
338+
339+
runtime/internal/atomic/asm_amd64.s
340+
```assembly
341+
TEXT runtime∕internal∕atomic·Xadd(SB), NOSPLIT, $0-20
342+
MOVQ ptr+0(FP), BX
343+
MOVL delta+8(FP), AX
344+
MOVL AX, CX
345+
LOCK
346+
XADDL AX, 0(BX) // Exchange and Add
347+
ADDL CX, AX // AX += CX
348+
MOVL AX, ret+16(FP)
349+
RET
350+
```
351+
#### func StoreInt32(addr *int32, val int32)
352+
> alias("sync/atomic", "StoreInt32", "runtime/internal/atomic", "Store", all...)
353+
354+
runtime/internal/atomic/asm_amd64.s
355+
356+
```assembly
357+
TEXT runtime∕internal∕atomic·Store(SB), NOSPLIT, $0-12
358+
MOVQ ptr+0(FP), BX
359+
MOVL val+8(FP), AX
360+
XCHGL AX, 0(BX) // 交换指令
361+
RET
362+
```

readme.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,4 @@
2727
21. [ ] [Lock Free](lockfree.md)
2828
22. [x] [context](context.md)
2929
23. [x] [stack dump](runtime_stack.md)
30+
24. [x] [Atomic](atomic.md)

sync.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ atomic.CompareAndSwap 即是使用 lock cmpxchg 来实现的。
111111

112112
> 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).
113113
114+
[Atomic 详解](atomic.md)
114115
## waitgroup
115116

116117
```go

0 commit comments

Comments
 (0)