返回介绍

上卷 程序设计

中卷 标准库

下卷 运行时

源码剖析

附录

unsafe 1.18

发布于 2024-10-12 19:15:54 字数 4354 浏览 0 评论 0 收藏 0

绕开安全类型的一些操作。

  • 不可移植和不兼容风险。
  • 内存安全风险。

获取对齐、偏移量和尺寸。

func main() {
	a := struct{
		x byte
		y byte
	}{}

	println(unsafe.Alignof(a))     // 1
	println(unsafe.Sizeof(a))      // 2
	println(unsafe.Offsetof(a.y))  // 1

	b := struct {
		x byte
		y int32
		z byte
	}{}

	println(unsafe.Alignof(b))     // 4
	println(unsafe.Sizeof(b))      // 12
	println(unsafe.Offsetof(b.y))  // 4
}

非安全的指针转换和运算。

  • 任意类型指针可转换为 Pointer ,反之亦然。
  • 可在 Pointeruintptr 间转换。
  • uintptr 是个纯粹的 “整数” 不同, Pointer 可保对象存活。
func main() {
	d := [8]byte{}

	// [8]byte => int
	x := (*int)(unsafe.Pointer(&d))
	*x = 0x1122
    
	fmt.Printf("% X\n", d)

	// d[2] = 0x33
	p := unsafe.Add(unsafe.Pointer(&d), unsafe.Sizeof(d[0]) * 2)
	*(*byte)(p) = 0x33
    
	fmt.Printf("% X\n", d)  
}

// 22 11 00 00 00 00 00 00
// 22 11 33 00 00 00 00 00
func main() {
	d := [...]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
	p := &d[2]

	// 动态构建切片,和 s2 等价。
	s := unsafe.Slice(p, 2)

	s2 := *(*[]byte)(unsafe.Pointer((&struct {
		ptr unsafe.Pointer
		len int
		cap int
	}{
		unsafe.Pointer(p),
		2,
		2,
	})))

	fmt.Printf("%T, %v\n", s, s)
	fmt.Printf("%T, %v\n", s2, s2)
}

// []uint8, [2 3]
// []uint8, [2 3]

示例

越界访问。

提示:与编译后的具体内存布局有关,以下代码仅作示例,以实际为准。

package main

import (
	"unsafe"
)

func main() {
	d := [3]int{1, 2, 3}
	x := [2]int{4, 5}

	// x[2] = 100
	p := unsafe.Add(unsafe.Pointer(&x), unsafe.Sizeof(x[0]) * uintptr(len(x)))
	*(*int)(p) = 100

	println(d[0])  // 100
}

/*

$ go build -gcflags "-N -l"
$ go tool objdump -S -s "main\.main" ./test

func main() {
	d := [3]int{1, 2, 3}
  0x462c04		MOVQ $0x1, 0x20(SP)	
  0x462c0d		MOVQ $0x2, 0x28(SP)	
  0x462c16		MOVQ $0x3, 0x30(SP)	
	x := [2]int{4, 5}
  0x462c25		MOVQ $0x4, 0x10(SP)	
  0x462c2e		MOVQ $0x5, 0x18(SP)	
	p := unsafe.Add(Pointer(&x), Sizeof(x[0]) * uintptr(len(x)))
  0x462c37		LEAQ 0x20(SP), AX	
  0x462c3c		MOVQ AX, 0x38(SP)	
	*(*int)(p) = 100
  0x462c41		MOVQ AX, 0x40(SP)	
  0x462c46		TESTB AL, 0(AX)		
  0x462c48		MOVQ $0x64, 0x20(SP)

*/

悬垂指针。

之所以没有 “segment fault”,是因为这段内存依旧被内存管理器缓存,并未真正释放。

package main

import (
	"runtime"
	"time"
	"unsafe"
)

func main() {
	d := 123
	runtime.SetFinalizer(&d, func(o *int){ println("drop!") })

    // 非引用指针。
	p := uintptr(unsafe.Pointer(&d))

    // 两次以上,因为 Finalizer 让对象在下轮回收。
	for i := 0; i < 4; i++ {
		time.Sleep(time.Second)
		runtime.GC()
		println("gc", i)
	}

    // 回收后访问。
	println(*(*int)(unsafe.Pointer(p)))
}

/*

gc 0

drop!

gc 1
gc 2
gc 3

123

*/

测试

比对 Pointeruintptr 是否能保持对象存活,不被垃圾回收。

package main

import (
	"unsafe"
	"runtime"
	"time"
)

func main() {
    
    // 容器,持有指针。
	var data = make([]unsafe.Pointer, 0)
	// var data = make([]uintptr, 0)

	for i := 0; i < 10; i++ {
        
        // 10MB 数据样本。
		d := [10<<20]byte{ 1, 2 }
        
        // 将样本指针添加到容器。
		data = append(data, unsafe.Pointer(&d))
		// data = append(data, uintptr(unsafe.Pointer(&d)))
	}

    // 强制垃圾回收。
    // 不需等到调用结束,只要后续不再引用,就可能被回收。
	for i := 0; i < 5; i++ {
		time.Sleep(time.Second)
		runtime.GC()
	}

    // 确保容器不被回收。
	runtime.KeepAlive(&data)
}
# unsafe.Pointer

$ go build && GODEBUG=gctrace=1 ./test

gc 1 @0.001s 26%: ..., 10->10->10 MB, 10 MB goal
gc 2 @0.301s  1%: ..., 20->20->20 MB, 20 MB goal
gc 3 @0.319s  2%: ..., 40->40->40 MB, 40 MB goal
gc 4 @0.345s  2%: ..., 80->80->80 MB, 80 MB goal

gc 5 @1.370s  0%: ..., 100->100->100 MB, 160 MB goal, ... (forced)
gc 6 @2.377s  0%: ..., 100->100->100 MB, 200 MB goal, ... (forced)
gc 7 @3.383s  0%: ..., 100->100->100 MB, 200 MB goal, ... (forced)
gc 8 @4.391s  0%: ..., 100->100->100 MB, 200 MB goal, ... (forced)
# uintptr

$ go build && GODEBUG=gctrace=1 ./test

gc 1 @1.015s 0%: ..., 0->0->0 MB,  4 MB goal, ... (forced)
gc 2 @2.032s 0%: ..., 0->0->0 MB, 16 MB goal, ... (forced)
gc 3 @3.037s 0%: ..., 0->0->0 MB, 16 MB goal, ... (forced)
gc 4 @4.046s 0%: ..., 0->0->0 MB, 16 MB goal, ... (forced)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文