非空interface - iface

type iface struct {
  tab *itab
  data unsafe.Pointer
}

非空interface的底层结构是iface。iface结构中,存在两个字段,*itab类型的tab字段,unsafe.Pointer类型的data字段。

tab *itab

tab字段描述的是此interface代表的变量的类型,拥有的方法这些信息。itab结构体描述了这些信息

type itab struct {
  inter    *interfacetype
  _type    *_type
  hash     uint32
  _        [4]byte
  fun      [1]uintptr
}

接下来展开介绍其中的字段。

inter *interfacetype

inter字段存放的是interface自己的静态类型。即代表interface这个类型

type interfacetype struct {
  typ     _type
  pkgpath name
  mhdr    []imethod
}

如代码所示,interfacetype结构体中,字段typ既是代表interface这个类型,因为_type类型的数据其实就是指的是go里的类型数据;pkgpath字段代表的是这个interface所在的pkg的路径。mhdr代表的是这个interface里拥有的方法的List。看imethod结构的代码,可以得知,interfacetype结构中存储方法,实际上是存储的是方法在最终段内的偏移量,知道偏移量后才能方便调用

type imethod struct {
  name nameOff
  ityp typeOff
}

nameOff和typeOff两种类型,在下面会介绍到

_type *_type

type字段存储的是interface代表的数据的类型。详细看看_type的结构。 _type可以说就是所有类型最原始的元信息

type _type struct {
  size       uintptr // 类型占用内存大小
  ptrdata    uintptr // 包含所有指针的内存前缀大小
  hash       uint32  // 类型hash
  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   // GC类型数据
  str       nameOff // 类型名称字符串在二进制文件段中的偏移量
  ptrToThis typeOff // 类型元信息指针在二进制文件段中的偏移量
}

结构体中str和ptrToThis对应的类型是nameOff和typeOff,是由链接器在段合并和符号重定向的时候赋值的。

链接器将各个 .o 文件中的段合并到输出文件,会进行段合并,有的放入 .text 段,有的放入 .data 段,有的放入 .bss 段。name 和 type 针对最终输出文件所在段内的偏移量 offset 是由 resolveNameOff 和 resolveTypeOff 函数计算出来的,然后链接器把结果保存在 str 和 ptrToThis 中。

func resolveNameOff(ptrInModule unsafe.Pointer, off nameOff) name {}  
func resolveTypeOff(ptrInModule unsafe.Pointer, off typeOff) *_type {}

hash uint32

itab结构体中的hash字段,其实就是 _type 类型内的hash字段

fun [1]uintptr

fun字段,存放的是该interface代表的数据所拥有的具体的方法的函数指针。这里虽然长度为1,但可以通过指针便宜,找到后面依次排列的其他方法。同时,如果fun[0] == 0,就代表 _type 这个类型没有实现这个interface.

data unsafe.Pointer

iface结构的的data字段,指向了这个interface代表的具体数据。

空interface - eface

type eface struct {
  _type    *_type
  data     unsafe.Pointer
}

Go语言底层对非空interface和空interface实现上做了区分。空interface的底层结构是eface结构。它与iface的区别在于,它拥有的不再是 *itab类型数据,而是 _type 类型的数据。是因为,eface面向的是空的interface数据,既然是空的interface,那么只需要用 *_type 代表类型,data字段指向具体数据即可。这里的 *_type 是此interface代表的数据的类型。

总的来说

只有 interfacetype类型的数据,才代表的interface自身的类型和数据。其他情况下使用 _type 都是代表了interface代表的那个具体数据的类型。