ToB企服应用市场:ToB评测及商务社交产业平台

标题: 标准库unsafe:带你突破golang中的类型限制 [打印本页]

作者: 没腿的鸟    时间: 2024-5-15 19:02
标题: 标准库unsafe:带你突破golang中的类型限制
本文分享自华为云社区《突破语言golang中的类型限制》,作者:码乐。
1 简介

在利用c语言编程时,常常由于类型的问题大伤头脑,而其他语言比如java,python默认类型又是难以改变的,golang提供了一些方式用于喜欢hack的用户。

2 标准库unsafe的简朴介绍

官方阐明标准库 unsafe 包含绕过 Go 程序的类型安全的操作。
导入unsafe包大概是不可移植的,并且不受 Go 1 兼容性指南的保护。
在1.20中,标准库的unsafe包很小, 二个结构体类型,八个函数,在一个文件中。
  1.     package unsage
  2.     type ArbitraryType int
  3.     type IntegerType int
  4.     type Pointer *ArbitraryType
  5.     func Sizeof(x ArbitraryType) uintptr
  6.     func Offsetof(x ArbitraryType) uintptr
  7.     func Alignof(x ArbitraryType) uintptr
  8.     func Add(ptr Pointer, len IntegerType) Pointer
  9.     func Slice(ptr *ArbitraryType, len IntegerType) []ArbitraryType
  10.     func SliceData(slice []ArbitraryType) *ArbitraryType
  11.     func String(ptr *byte, len IntegerType) string
  12.     func StringData(str string) *byte
复制代码
unsafe包定义了 二个类型和 八个函数,二个类型 ArbitraryType 和 IntegerType 不真正属于unsafe包,我们在Go代码中并不能利用它们定义变量。
它表示一个任意表达式的类型,仅用于文档目的,Go编译器会对其做特殊处理。
固然位于 unsafe,但是 Alignof,Offsetof,Sizeof,这三个函数的利用是绝对安全的。 以至于Go筹划者Rob pike提议移走它们。
这三个函数的共同点是 都返回 uintptr 类型。
之所以利用 uintptr 类型而不是 uint64 整型,由于这三个函数更多应用于 有 unsafe.Pointer和 uintptr类型参数的指针运算。
采用uintptr做为返回值类型可以减少指针运算表达式的显式类型转换。
2.1 获取巨细 Sizeof

Sizeof 用于获取一个表达式的巨细。 该函数获取一个任意类型的表达式 x,并返回 按bytes盘算 的巨细,假设变量v,并且v通过 v =x声明。
Sizeof 接收任何类型的表达式x,并返回以bytes字节为单元的巨细, 并且假设变量v是通过var v = x声明的。该巨细不包括任何大概被x引用的内存。
比方,如果x是一个切片,Sizeof返回切片形貌符的巨细,而不是该片所引用的内存的巨细。
对于一个结构体,其巨细包括由字段对齐引入的任何填充。
如果参数x的类型没有厘革,不具有可变的巨细,Sizeof的返回值是一个Go常数不可变值 。
(如果一个类型是一个类型参数,或者是一个数组,则该类型具有可变的巨细或结构类型中的元素巨细可变)。
示例:
  1.     var (
  2.         i  int = 5
  3.         a      = [10]int{}
  4.         ss     = a[:]
  5.         f  FuncFoo
  6.         preValue = map[string]uintptr{
  7.             "i":       8,
  8.             "a":       80,
  9.             "ss":      24,
  10.             "f":       48,
  11.             "f.c":     10,
  12.             "int_nil": 8,
  13.         }
  14.     )
  15.     type FuncFoo struct {
  16.         a int
  17.         b string
  18.         c [10]byte
  19.         d float64
  20.     }
  21.     func TestFuncSizeof(t *testing.T) {
  22.         defer setUp(t.Name())()
  23.         fmt.Printf("\tExecute test:%v\n", t.Name())
  24.         if unsafe.Sizeof(i) != preValue["i"] {
  25.             ErrorHandler(fmt.Sprintf("size: %v not equal %v", unsafe.Sizeof(i), preValue["i"]), t)
  26.         }
  27.         if unsafe.Sizeof(a) != preValue["a"] {
  28.             ErrorHandler(fmt.Sprintf("size: %v not equal %v", unsafe.Sizeof(i), preValue["a"]), t)
  29.         }
  30.         if unsafe.Sizeof(ss) != preValue["ss"] {
  31.             ErrorHandler(fmt.Sprintf("size: %v not equal %v", unsafe.Sizeof(i), preValue["ss"]), t)
  32.         }
  33.         if unsafe.Sizeof(f) != preValue["f"] {
  34.             ErrorHandler(fmt.Sprintf("size: %v not equal %v", unsafe.Sizeof(i), preValue["f"]), t)
  35.         }
  36.         if unsafe.Sizeof(f.c) != preValue["f.c"] {
  37.             ErrorHandler(fmt.Sprintf("size: %v not equal %v", unsafe.Sizeof(i), preValue["f.c"]), t)
  38.         }
  39.         if unsafe.Sizeof(unsafe.Sizeof((*int)(nil))) != preValue["int_nil"] {
  40.             ErrorHandler(fmt.Sprintf("size: %v not equal %v", unsafe.Sizeof(i), preValue["int_nil"]), t)
  41.         }
  42.     }
复制代码
Sizeof 函数不支持之间传入无类型信息的nil值,如下错误
  1.     unsafe.Sizeof(nil)  
复制代码
我们必须显式告知 Sizeof 传入的nil究竟是谁人类型,
  1.     unsafe.Sizeof(unsafe.Sizeof((*int)(nil)))
复制代码
必须显式告知nil是哪个类型的nil,这就是传入一个值 nil 但是类型明确的变量。
对齐系数 Alignof 用于获取一个表达式的内地地点对齐系数,对齐系数 alignment factor 是一个盘算机体系架构 computer architecture 层面的术语。
在不同盘算机体系中,处理器对变量地点都有对齐要求,即变量的地点必须可被该变量的对齐系数整除。
它接收一个任何类型的表达式x,并返回所需的排列方式 假设变量v是通过var v = x声明的。
它是m一个最大的值。
例1,
  1.         a      = [10]int{}
  2.         reflect.TypeOf(x).Align()  //8
  3.         unsafe.Alignof(a)   //8
复制代码
它与reflect.TypeOf(x).Align()返回的值雷同。
作为一个特例,如果一个变量s是结构类型,f是一个字段,那么Alignof(s.f)将返回所需的对齐方式。
该类型的字段在结构中的位置。这种情况与reeflect.TypeOf(s.f).FieldAlign()返回的值。
Alignof的返回值是一个Go常数,如果参数的类型不具有可变巨细。
(关于可变巨细类型的定义,请参见[Sizeof]的形貌)。
继上 例2:
  1.       var (
  2.         i  int = 5
  3.         a      = [10]int{}
  4.         ss     = a[:]
  5.         f  FuncFoo
  6.         zhs = "文"
  7.         preValue = map[string]uintptr{
  8.             "i":       8,
  9.             "a":       80,
  10.             "ss":      24,
  11.             "f":       48,
  12.             "f.c":     10,
  13.             "int_nil": 8,
  14.         }
  15.     )
  16.     func TestAlignof(t *testing.T) {
  17.         defer setUp(t.Name())()
  18.         fmt.Printf("\tExecute test:%v\n", t.Name())
  19.         var x int
  20.         b := uintptr(unsafe.Pointer(&x))%unsafe.Alignof(x) == 0
  21.         t.Log("alignof:", b)
  22.         if unsafe.Alignof(i) != preValue["i"] {
  23.             ErrorHandler(fmt.Sprintf("Alignof: %v not equal %v", unsafe.Sizeof(i), preValue["int_nil"]), t)
  24.         }
  25.         if unsafe.Alignof(a) != preValue["i"] {
  26.             ErrorHandler(fmt.Sprintf("Alignof: %v not equal %v", unsafe.Sizeof(i), preValue["int_nil"]), t)
  27.         }
  28.         if unsafe.Alignof(ss) != preValue["i"] {
  29.             ErrorHandler(fmt.Sprintf("Alignof: %v not equal %v", unsafe.Sizeof(i), preValue["int_nil"]), t)
  30.         }
  31.         if unsafe.Alignof(f.a) != preValue["i"] {
  32.             ErrorHandler(fmt.Sprintf("Alignof: %v not equal %v", unsafe.Sizeof(i), preValue["int_nil"]), t)
  33.         }
  34.         if unsafe.Alignof(f) != preValue["i"] {
  35.             ErrorHandler(fmt.Sprintf("Alignof: %v not equal %v", unsafe.Sizeof(i), preValue["int_nil"]), t)
  36.         }
复制代码
中文对齐系数 为 8
  1.         if unsafe.Alignof(zhs) != preValue["i"] {
  2.             ErrorHandler(fmt.Sprintf("Alignof: %v not equal %v", unsafe.Sizeof(i), preValue["i"]), t)
  3.         }
复制代码
空结构体对齐系数 1
  1.         if unsafe.Alignof(struct{}{}) != 1 {
  2.             ErrorHandler(fmt.Sprintf("Alignof: %v not equal %v", unsafe.Sizeof(i), 1), t)
  3.         }
复制代码
byte 数组对齐系数为 1
  1.         if unsafe.Alignof(sbyte) != 1 {
  2.             ErrorHandler(fmt.Sprintf("Alignof: %v not equal %v", unsafe.Sizeof(i), 1), t)
  3.         }
复制代码
长度为0 的数组,与其元素的对齐系数雷同
  1.         if unsafe.Alignof([0]int{}) != 8 {
  2.             ErrorHandler(fmt.Sprintf("Alignof: %v not equal %v", unsafe.Sizeof(i), 8), t)
  3.         }
复制代码
长度为0 的数组,与其元素的对齐系数雷同
  1.         if unsafe.Alignof([0]struct{}{}) != 1 {
  2.             ErrorHandler(fmt.Sprintf("Alignof: %v not equal %v", unsafe.Sizeof(i), 1), t)
  3.         }
  4.     }
复制代码
执行它:
  1.     go test -timeout 30s -run ^TestAlignof$ ./unsafe_case.go
复制代码
对齐系数 alignment factor,变量的地点必须可被该变量的对齐系数整除。
2.2 利用对齐的例子

我们利用雷同字段,分别创建两个结构体属性分别为对齐或不对齐,帮助 go 更好地分配内存和 利用cpu读取,检察效果
  1.     type RandomResource struct {
  2.         Cloud               string // 16 bytes
  3.         Name                string // 16 bytes
  4.         HaveDSL             bool   //  1 byte
  5.         PluginVersion       string // 16 bytes
  6.         IsVersionControlled bool   //  1 byte
  7.         TerraformVersion    string // 16 bytes
  8.         ModuleVersionMajor  int32  //  4 bytes
  9.     }
  10.     type OrderResource struct {
  11.         ModuleVersionMajor  int32  //  4 bytes
  12.         HaveDSL             bool   //  1 byte
  13.         IsVersionControlled bool   //  1 byte
  14.         Cloud               string // 16 bytes
  15.         Name                string // 16 bytes
  16.         PluginVersion       string // 16 bytes
  17.         TerraformVersion    string // 16 bytes
  18.     }
复制代码
字段 存储利用的空间与 字段值没有关系
  1.          var d RandomResource
  2.          d.Cloud = "aws-singapore"
  3.          ...
  4.          InfoHandler(fmt.Sprintf("随机顺序属性的结构体内存 总共占用 StructType: %T => [%d]\n", d, unsafe.Sizeof(d)), m)
  5.          var te = OrderResource{}
  6.          te.Cloud = "aws-singapore"  
  7.          ...
  8.          m.Logf("属性对齐的结构体内存 总共占用  StructType:d %T => [%d]\n", te, unsafe.Sizeof(te))
复制代码
然后复制结构体,并改变其属性值,检察存储空间和值的长度厘革
  1.         te2 := te
  2.         te2.Cloud = "ali2"
  3.         m.Logf("结构体2 te2:%#v\n", &te2)
  4.         m.Logf("结构体1 te:%#v\n", &te)
  5.         m.Log("改变 te3 将同时改变 te,te3 指向了 te的地址")
  6.         m.Log("复制了对齐结构体,并重新赋值,用于查看字段长度。")
  7.         m.Log("(*te).Cloud:", (te).Cloud, "*te.Cloud", te.Cloud, "te size:", unsafe.Sizeof(te.Cloud), "te value len:", len(te.Cloud))
  8.         te3 := &te
  9.         te3.Cloud = "HWCloud2"
  10.         m.Log("(*te3).Cloud:", (*te3).Cloud, "*te3.Cloud", te3.Cloud, "te3 size:", unsafe.Sizeof(te3.Cloud), "te3 value len:", len(te3.Cloud))
  11.         m.Logf("字段 Cloud:%v te3:%p\n", (*te3).Cloud, te3)
  12.         m.Logf("字段 Cloud:%v order:%v te:%v, addr:%p\n", te.Cloud, (te).Cloud, te, &te)
复制代码
执行它,
  1.     go test -v .\case_test.go
复制代码
得到以下输出:
随机顺序属性的结构体内存 总共占用 StructType: main.Raesource => [88]
  1.     ...
复制代码
属性对齐的结构体内存 总共占用 StructType:d main.OrderResource => [72]
改变 te3 将同时改变 te,te3 指向了 te的地点
  1.     case_test.go:186: 复制了对齐结构体,并重新赋值,用于查看字段长度。
  2.     case_test.go:188: (*te).Cloud: aws-singapore *te.Cloud aws-singapore te size: 16 te Alignof: 8 te value len: 13 reflect Align len and field Align len: 8 8
  3.     case_test.go:190: (*te2).Cloud: ali2 *te2.Cloud aws-singapore te2 size: 16 te2 Alignof: 8 te2 value len: 4 reflect Align len and field Align len: 8 8
  4.     case_test.go:196: (*te3).Cloud: HWCloud2-asia-southeast-from-big-plant-place-air-local-video-service-picture-merge-from-other-all-company *te3.Cloud HWCloud2-asia-southeast-from-big-plant-place-air-local-video-service-picture-merge-from-other-all-company te3
  5. size: 16 te3 Alignof: 8 te3 value len: 105 reflect Align len and field Align len: 8 8
  6.     case_test.go: 结构体1字段 Cloud:HWCloud2-asia-southeast-from-big-plant-place-air-local-video-service-picture-merge-from-other-all-company te2:0xc0000621e0
  7.     case_test.go:198: 结构体2字段 Cloud:ali2 te2:0xc000062280
  8.     case_test.go:199: 结构体3字段 Cloud:HWCloud2-asia-southeast-from-big-plant-place-air-local-video-service-picture-merge-from-other-all-company te3:0xc0000621e0
复制代码
小结

我们介绍了unsafe包的检查功能,在初始化时,go结构体已经分配了对于的内存空间,
一个结构体而言,结构体属性为随机顺序的,go将分配更多内存空间。 纵然是复制后。
比如 结构体的Cloud 字段。
Sizeof表达式巨细总是16,
而对齐系数 Alignof 巨细总是8,
而在不同的结构体实例中值长度可以为 4,13, 105.
本节源码地点:
  1. https://github.com/hahamx/examples/tree/main/alg_practice/2_sys_io
复制代码
 
点击关注,第一时间了解华为云奇怪技术~
 

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4