0%

Go内存对齐的一些问题

1. atomic.AddInt64在 32 位操作系统上内存对齐的问题

1.1 问题

在32位机器上使用int64可能会出现panic: unaligned 64-bit atomic operation

1.2 触发代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import (
"sync/atomic"
)

type Foo struct {
a int64
b int32
c int64
}

func main() {
var f Foo
atomic.AddInt64(&f.a, 1) // 这里不会崩溃
atomic.AddInt64(&f.c, 1) // 这里会崩溃
}

2. 溯源

先看源码。源码是汇编写的,在runtime/internal/atomic/atomic_<系统架构>.s文件中,我这边使用的是 arm 架构,所以源码也是atomic_arm.s中的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
TEXT ·Xadd64(SB),NOSPLIT,$-4-20
NO_LOCAL_POINTERS
MOVW addr+0(FP), R1
CHECK_ALIGN

MOVB runtime·goarm(SB), R11
CMP $7, R11
BLT 2(PC)
JMP armXadd64<>(SB)
JMP ·goXadd64(SB)

TEXT armXadd64<>(SB),NOSPLIT,$0-20
// addr is already in R1
MOVW delta_lo+4(FP), R2
MOVW delta_hi+8(FP), R3
1
2
3
4
5
6
7
8
9
10
11
12
13
//go:nosplit
func goXadd64(addr *uint64, delta int64) uint64 {
if uintptr(unsafe.Pointer(addr))&7 != 0 {
*(*int)(nil) = 0 // crash on unaligned uint64
}
_ = *addr // if nil, fault before taking the lock
var r uint64
addrLock(addr).lock()
r = *addr + uint64(delta)
*addr = r
addrLock(addr).unlock()
return r
}

什么?汇编看不懂吗?其实我也看不懂…,问问 gpt 吧

汇编在调用时做了CHECK_ALIGN(内存对齐的检查)

在内存未对齐时,会直接产生 panic 进而中断程序,包括在goXadd64中也进行了判断*(*int)(nil) = 0 // crash on unaligned uint64

参考:yoko blog | 32位系统下,Go标准库中atomic原子操作int64有崩溃bug