• Go语言的特点
  • Go语言的优势
  • Go语言的用途

为什么要用Go语言

  • C/C++
    • C语言不是面向对象
    • 直接编译为机器码,不需要执行环境
    • 一次编码只能适用一种平台
    • 自己处理GC问题(内存泄露)

image.png

  • Java
    • 编译为中间码(字节码)
    • 需要特定的执行环境(JVM)
    • 一次编译多处执行
    • 有虚拟化损失

image.png

  • JavaScript
    • 不需要编译,直接解释执行
    • 需要执行环境(浏览器)
    • 有虚拟化损失(需要浏览器)

image.png

  • Go
    • 直接编译为二进制,没有虚拟化损失
    • 自带运行环境,无需处理GC问题
    • 一次编码可以适用多种平台
    • 超强的并发支持与并发易用性

image.png image.png 总结

  • Go综合了多种语言的优势
  • Go是一种天生支持高性能并发的语言
  • Go在工业界有广泛的应用

何为Runtime

语言在运行的时候作为支撑的部分 很多语言都有runtime Java-JVM image.png JavaScript-浏览器内核

Go的Runtime特点

  • 没有虚拟机的概念
  • Runtime作为程序的一部分打包进二进制产物
  • Runtime随用户程序一起运行
  • Runtime与用户程序没有明显界限,直接通过函数调用

Go的Runtime能力

  • 内存管理能力
  • 垃圾回收能力(GC)
  • 超强的并发能力(协程调度)

Go的Runtime其他特点

  • Runtime有一定的屏蔽system call的能力
  • 一些Go的关键字其实是Runtime下的函数

总结

  • Go的Runtime负责内存管理、垃圾回收、协程调度
  • Go的Runtime被编译为用户程序的一部分、一起运行
关键字 函数
go newproc
new newobject
make makeslcie、makechain、makemap…
<- chansend,chanrecv1

:::success Go程序是如何编译的?
Example: :::

  1. package main
  2. import "fmt"
  3. func main() {
  4. fmt.Println("Hello world")
  5. }

:::success go build -n 输出编译过程 :::

显示结果: #

main

#

mkdir -p $WORK\b001\ cat >$WORK\b001\importcfg << ‘EOF’ # internal

import config //引包

packagefile fmt=D:\Program Files\Go\go1.19\pkg\windowsamd64\fmt.a //编译出来的中间文件 packagefile runtime=D:\Program Files\Go\go1.19\pkg\windows_amd64\runtime.a //中间的机器码文件 EOF cd D:\JetbrainsCode\Learn_Go\GoRedis “D:\Program Files\Go\go1.19\pkg\tool\windows_amd64\compile.exe” -o “$WORK\b001\_pkg.a” -trimpath “$WORK\b001=>” -p main -lang=go1.19 -complet e -buildid yL1vkHRRlkuKDXTvd4Ij/yL1vkHRRlkuKDXTvd4Ij -goversion go1.19 -c=4 -nolocalimports -importcfg “$WORK\b001\importcfg” -pack “D:\JetbrainsCode \LearnGo\GoRedis\main.go” “D:\Program Files\Go\go1.19\pkg\tool\windows_amd64\buildid.exe” -w “$WORK\b001\_pkg.a” # internal cat >$WORK\b001\importcfg.link << ‘EOF’ # internal packagefile main=$WORK\b001_pkg.a packagefile fmt=D:\Program Files\Go\go1.19\pkg\windows_amd64\fmt.a packagefile runtime=D:\Program Files\Go\go1.19\pkg\windows_amd64\runtime.a packagefile errors=D:\Program Files\Go\go1.19\pkg\windows_amd64\errors.a packagefile internal/fmtsort=D:\Program Files\Go\go1.19\pkg\windows_amd64\internal\fmtsort.a packagefile io=D:\Program Files\Go\go1.19\pkg\windows_amd64\io.a packagefile math=D:\Program Files\Go\go1.19\pkg\windows_amd64\math.a packagefile os=D:\Program Files\Go\go1.19\pkg\windows_amd64\os.a packagefile reflect=D:\Program Files\Go\go1.19\pkg\windows_amd64\reflect.a packagefile strconv=D:\Program Files\Go\go1.19\pkg\windows_amd64\strconv.a packagefile sync=D:\Program Files\Go\go1.19\pkg\windows_amd64\sync.a packagefile unicode/utf8=D:\Program Files\Go\go1.19\pkg\windows_amd64\unicode\utf8.a packagefile internal/abi=D:\Program Files\Go\go1.19\pkg\windows_amd64\internal\abi.a packagefile internal/bytealg=D:\Program Files\Go\go1.19\pkg\windows_amd64\internal\bytealg.a packagefile internal/cpu=D:\Program Files\Go\go1.19\pkg\windows_amd64\internal\cpu.a packagefile internal/goarch=D:\Program Files\Go\go1.19\pkg\windows_amd64\internal\goarch.a packagefile internal/goexperiment=D:\Program Files\Go\go1.19\pkg\windows_amd64\internal\goexperiment.a packagefile internal/goos=D:\Program Files\Go\go1.19\pkg\windows_amd64\internal\goos.a packagefile runtime/internal/atomic=D:\Program Files\Go\go1.19\pkg\windows_amd64\runtime\internal\atomic.a packagefile runtime/internal/math=D:\Program Files\Go\go1.19\pkg\windows_amd64\runtime\internal\math.a packagefile runtime/internal/sys=D:\Program Files\Go\go1.19\pkg\windows_amd64\runtime\internal\sys.a packagefile internal/reflectlite=D:\Program Files\Go\go1.19\pkg\windows_amd64\internal\reflectlite.a packagefile sort=D:\Program Files\Go\go1.19\pkg\windows_amd64\sort.a packagefile math/bits=D:\Program Files\Go\go1.19\pkg\windows_amd64\math\bits.a packagefile internal/itoa=D:\Program Files\Go\go1.19\pkg\windows_amd64\internal\itoa.a packagefile internal/oserror=D:\Program Files\Go\go1.19\pkg\windows_amd64\internal\oserror.a packagefile internal/poll=D:\Program Files\Go\go1.19\pkg\windows_amd64\internal\poll.a packagefile internal/syscall/execenv=D:\Program Files\Go\go1.19\pkg\windows_amd64\internal\syscall\execenv.a packagefile internal/syscall/windows=D:\Program Files\Go\go1.19\pkg\windows_amd64\internal\syscall\windows.a packagefile internal/testlog=D:\Program Files\Go\go1.19\pkg\windows_amd64\internal\testlog.a packagefile internal/unsafeheader=D:\Program Files\Go\go1.19\pkg\windows_amd64\internal\unsafeheader.a packagefile io/fs=D:\Program Files\Go\go1.19\pkg\windows_amd64\io\fs.a packagefile sync/atomic=D:\Program Files\Go\go1.19\pkg\windows_amd64\sync\atomic.a packagefile syscall=D:\Program Files\Go\go1.19\pkg\windows_amd64\syscall.a packagefile time=D:\Program Files\Go\go1.19\pkg\windows_amd64\time.a packagefile unicode/utf16=D:\Program Files\Go\go1.19\pkg\windows_amd64\unicode\utf16.a packagefile unicode=D:\Program Files\Go\go1.19\pkg\windows_amd64\unicode.a packagefile internal/race=D:\Program Files\Go\go1.19\pkg\windows_amd64\internal\race.a packagefile internal/syscall/windows/sysdll=D:\Program Files\Go\go1.19\pkg\windows_amd64\internal\syscall\windows\sysdll.a packagefile path=D:\Program Files\Go\go1.19\pkg\windows_amd64\path.a packagefile internal/syscall/windows/registry=D:\Program Files\Go\go1.19\pkg\windows_amd64\internal\syscall\windows\registry.a modinfo “0w\xaf\f\x92t\b\x02A\xe1\xc1\a\xe6\xd6\x18\xe6path\tmain\nmod\tmain\t(devel)\t\nbuild\t-compiler=gc\nbuild\tCGO_ENABLED=1\nbuild\tCGO_CFLAGS=\n build\tCGO_CPPFLAGS=\nbuild\tCGO_CXXFLAGS=\nbuild\tCGO_LDFLAGS=\nbuild\tGOARCH=amd64\nbuild\tGOOS=windows\nbuild\tGOAMD64=v1\nbuild\tvcs=git\nbuild\tvcs .revision=55e510c654b410d49fee106291d645a06d084643\nbuild\tvcs.time=2022-09-21T06:23:56Z\nbuild\tvcs.modified=true\n\xf92C1\x86\x18 r\x00\x82B\x10A\x16\ xd8\xf2” EOF mkdir -p $WORK\b001\exe\ cd . “D:\Program Files\Go\go1.19\pkg\tool\windows_amd64\link.exe” -o //链接这些.a文件成.exe文件 “$WORK\b001\exe\a.out.exe” -importcfg “$WORK\b001\importcfg.link” -buildmode =pie -buildid=2naWCJTmkVUCrFKumC0d/yL1vkHRRlkuKDXTvd4Ij/yL1vkHRRlkuKDXTvd4Ij/2naWCJTmkVUCrFKumC0d -extld=gcc “$WORK\b001\_pkg.a” “D:\Program Files\Go\go1.19\pkg\tool\windows_amd64\buildid.exe” -w “$WORK\b001\exe\a.out.exe” # internal mv $WORK\b001\exe\a.out.exe main.exe

image.png

  • 词法分析、句法分析、语义分析归属于编译

词法分析

  • 将源代码翻译成Token
  • Token是代码中的最小语义结构

句法分析

  • Token序列经过处理,变成语法树
  • SST(抽象语法树)

image.png

:::info 语义分析

  • 类型检查
  • 类型推断
  • 查看类型是否匹配
  • 函数调用内联
  • 逃逸分析

    • Go的变量是放在上还是放在上 ::: :::success 中间码生成(SSA)
  • 为了处理不同平台的差异,先生成中间代码(SSA 平台无关汇编)

image.png
代码优化

  • 每一个过程当中都有代码优化的过程 ::: :::tips 查看从代码到SSA中间码的整个过程
    Windows:$env:GOSSAFUNC="main"
    Linux:export GOSSAFUNC="main" :::

    go build

:::tips

runtime
dumped SSA to D:\JetbrainsCode\Learn_Go\GoRedis\ssa.html
# main
dumped SSA to .\ssa.html

::: ssa.html

截图示意

image.png

最后一步,生成SSA 研究和排查Go语言的时候有用

image.png

  1. # D:\JetbrainsCode\Learn_Go\GoRedis\main.go
  2. 00000 (5) TEXT main.main(SB), ABIInternal
  3. 00001 (5) FUNCDATA $0, gclocals·g2BeySu+wFnoycgXfElmcg==(SB)
  4. 00002 (5) FUNCDATA $1, gclocals·EaPwxsZ75yY1hHMVZLmk6g==(SB)
  5. 00003 (5) FUNCDATA $2, main.main.stkobj(SB)
  6. v35
  7. 00004 (+6) MOVUPS X15, main..autotmp_8-16(SP)
  8. v14
  9. 00005 (6) LEAQ type.string(SB), DX
  10. v5
  11. 00006 (6) MOVQ DX, main..autotmp_8-16(SP)
  12. v20
  13. 00007 (6) LEAQ main..stmp_0(SB), DX
  14. v36
  15. 00008 (6) MOVQ DX, main..autotmp_8-8(SP)
  16. v27
  17. 00009 (?) NOP
  18. # $GOROOT\src\fmt\print.go
  19. v30
  20. 00010 (+294) MOVQ os.Stdout(SB), BX
  21. v22
  22. 00011 (294) LEAQ go.itab.*os.File,io.Writer(SB), AX
  23. v13
  24. 00012 (294) LEAQ main..autotmp_8-16(SP), CX
  25. v16
  26. 00013 (294) MOVL $1, DI
  27. v7
  28. 00014 (294) MOVQ DI, SI
  29. v32
  30. 00015 (294) PCDATA $1, $0
  31. v32
  32. 00016 (294) CALL fmt.Fprintln(SB)
  33. # D:\JetbrainsCode\Learn_Go\GoRedis\main.go
  34. b4
  35. 00017 (7) RET
  36. 00018 (?) END

机器码生成

  • 先生成Plan9汇编代码
  • 最后编译为机器码
  • 输出的机器码为.a文件
  • 查看Plan9汇编代码

go build -gcflags -S main.go

  1. # runtime
  2. dumped SSA to D:\JetbrainsCode\Learn_Go\GoRedis\ssa.html
  3. # command-line-arguments
  4. dumped SSA to .\ssa.html
  5. main.main STEXT size=103 args=0x0 locals=0x40 funcid=0x0 align=0x0
  6. 0x0000 00000 (D:\JetbrainsCode\Learn_Go\GoRedis\main.go:5) TEXT main.main(SB), ABIInternal, $64-0
  7. 0x0000 00000 (D:\JetbrainsCode\Learn_Go\GoRedis\main.go:5) CMPQ SP, 16(R14)
  8. 0x0004 00004 (D:\JetbrainsCode\Learn_Go\GoRedis\main.go:5) PCDATA $0, $-2
  9. 0x0004 00004 (D:\JetbrainsCode\Learn_Go\GoRedis\main.go:5) JLS 92
  10. 0x0006 00006 (D:\JetbrainsCode\Learn_Go\GoRedis\main.go:5) PCDATA $0, $-1
  11. 0x0006 00006 (D:\JetbrainsCode\Learn_Go\GoRedis\main.go:5) SUBQ $64, SP
  12. 0x000a 00010 (D:\JetbrainsCode\Learn_Go\GoRedis\main.go:5) MOVQ BP, 56(SP)
  13. 0x000f 00015 (D:\JetbrainsCode\Learn_Go\GoRedis\main.go:5) LEAQ 56(SP), BP
  14. 0x0014 00020 (D:\JetbrainsCode\Learn_Go\GoRedis\main.go:5) FUNCDATA $0, gclocals·g2BeySu+wFnoycgXfElmcg==(SB)
  15. 0x0014 00020 (D:\JetbrainsCode\Learn_Go\GoRedis\main.go:5) FUNCDATA $1, gclocals·EaPwxsZ75yY1hHMVZLmk6g==(SB)
  16. 0x0014 00020 (D:\JetbrainsCode\Learn_Go\GoRedis\main.go:5) FUNCDATA $2, main.main.stkobj(SB)
  17. 0x0014 00020 (D:\JetbrainsCode\Learn_Go\GoRedis\main.go:6) MOVUPS X15, main..autotmp_8+40(SP)
  18. 0x001a 00026 (D:\JetbrainsCode\Learn_Go\GoRedis\main.go:6) LEAQ type.string(SB), DX
  19. 0x0021 00033 (D:\JetbrainsCode\Learn_Go\GoRedis\main.go:6) MOVQ DX, main..autotmp_8+40(SP)
  20. 0x0026 00038 (D:\JetbrainsCode\Learn_Go\GoRedis\main.go:6) LEAQ main..stmp_0(SB), DX
  21. 0x002d 00045 (D:\JetbrainsCode\Learn_Go\GoRedis\main.go:6) MOVQ DX, main..autotmp_8+48(SP)
  22. 0x0032 00050 (<unknown line number>) NOP
  23. 0x0032 00050 ($GOROOT\src\fmt\print.go:294) MOVQ os.Stdout(SB), BX
  24. 0x0039 00057 ($GOROOT\src\fmt\print.go:294) LEAQ go.itab.*os.File,io.Writer(SB), AX
  25. 0x0040 00064 ($GOROOT\src\fmt\print.go:294) LEAQ main..autotmp_8+40(SP), CX
  26. 0x0045 00069 ($GOROOT\src\fmt\print.go:294) MOVL $1, DI
  27. 0x004a 00074 ($GOROOT\src\fmt\print.go:294) MOVQ DI, SI
  28. 0x004d 00077 ($GOROOT\src\fmt\print.go:294) PCDATA $1, $0
  29. 0x004d 00077 ($GOROOT\src\fmt\print.go:294) CALL fmt.Fprintln(SB)
  30. 0x0052 00082 (D:\JetbrainsCode\Learn_Go\GoRedis\main.go:7) MOVQ 56(SP), BP
  31. 0x0057 00087 (D:\JetbrainsCode\Learn_Go\GoRedis\main.go:7) ADDQ $64, SP
  32. 0x005b 00091 (D:\JetbrainsCode\Learn_Go\GoRedis\main.go:7) RET
  33. 0x005c 00092 (D:\JetbrainsCode\Learn_Go\GoRedis\main.go:7) NOP
  34. 0x005c 00092 (D:\JetbrainsCode\Learn_Go\GoRedis\main.go:5) PCDATA $1, $-1
  35. 0x005c 00092 (D:\JetbrainsCode\Learn_Go\GoRedis\main.go:5) PCDATA $0, $-2
  36. 0x005c 00092 (D:\JetbrainsCode\Learn_Go\GoRedis\main.go:5) NOP
  37. 0x0060 00096 (D:\JetbrainsCode\Learn_Go\GoRedis\main.go:5) CALL runtime.morestack_noctxt(SB)
  38. 0x0065 00101 (D:\JetbrainsCode\Learn_Go\GoRedis\main.go:5) PCDATA $0, $-1
  39. 0x0065 00101 (D:\JetbrainsCode\Learn_Go\GoRedis\main.go:5) JMP 0
  40. 0x0000 49 3b 66 10 76 56 48 83 ec 40 48 89 6c 24 38 48 I;f.vVH..@H.l$8H
  41. 0x0010 8d 6c 24 38 44 0f 11 7c 24 28 48 8d 15 00 00 00 .l$8D..|$(H.....
  42. 0x0020 00 48 89 54 24 28 48 8d 15 00 00 00 00 48 89 54 .H.T$(H......H.T
  43. 0x0030 24 30 48 8b 1d 00 00 00 00 48 8d 05 00 00 00 00 $0H......H......
  44. 0x0040 48 8d 4c 24 28 bf 01 00 00 00 48 89 fe e8 00 00 H.L$(.....H.....
  45. 0x0050 00 00 48 8b 6c 24 38 48 83 c4 40 c3 0f 1f 40 00 ..H.l$8H..@...@.
  46. 0x0060 e8 00 00 00 00 eb 99 .......
  47. rel 2+0 t=23 type.string+0
  48. rel 2+0 t=23 type.*os.File+0
  49. rel 29+4 t=14 type.string+0
  50. rel 41+4 t=14 main..stmp_0+0
  51. rel 53+4 t=14 os.Stdout+0
  52. rel 60+4 t=14 go.itab.*os.File,io.Writer+0
  53. rel 78+4 t=7 fmt.Fprintln+0
  54. rel 97+4 t=7 runtime.morestack_noctxt+0
  55. go.cuinfo.producer.main SDWARFCUINFO dupok size=0
  56. 0x0000 72 65 67 61 62 69 regabi
  57. go.cuinfo.packagename.main SDWARFCUINFO dupok size=0
  58. 0x0000 6d 61 69 6e main
  59. go.info.fmt.Println$abstract SDWARFABSFCN dupok size=42
  60. 0x0000 05 66 6d 74 2e 50 72 69 6e 74 6c 6e 00 01 01 13 .fmt.Println....
  61. 0x0010 61 00 00 00 00 00 00 13 6e 00 01 00 00 00 00 13 a.......n.......
  62. 0x0020 65 72 72 00 01 00 00 00 00 00 err.......
  63. rel 0+0 t=22 type.[]interface {}+0
  64. rel 0+0 t=22 type.error+0
  65. rel 0+0 t=22 type.int+0
  66. rel 19+4 t=31 go.info.[]interface {}+0
  67. rel 27+4 t=31 go.info.int+0
  68. rel 37+4 t=31 go.info.error+0
  69. main..inittask SNOPTRDATA size=32
  70. 0x0000 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 ................
  71. 0x0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  72. rel 24+8 t=1 fmt..inittask+0
  73. go.string."Hello world" SRODATA dupok size=11
  74. 0x0000 48 65 6c 6c 6f 20 77 6f 72 6c 64 Hello world
  75. go.itab.*os.File,io.Writer SRODATA dupok size=32
  76. 0x0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  77. 0x0010 5a 22 ee 60 00 00 00 00 00 00 00 00 00 00 00 00 Z".`............
  78. rel 0+8 t=1 type.io.Writer+0
  79. rel 8+8 t=1 type.*os.File+0
  80. rel 24+8 t=-32767 os.(*File).Write+0
  81. main..stmp_0 SRODATA static size=16
  82. 0x0000 00 00 00 00 00 00 00 00 0b 00 00 00 00 00 00 00 ................
  83. rel 0+8 t=1 go.string."Hello world"+0
  84. runtime.nilinterequal·f SRODATA dupok size=8
  85. 0x0000 00 00 00 00 00 00 00 00 ........
  86. rel 0+8 t=1 runtime.nilinterequal+0
  87. runtime.memequal64·f SRODATA dupok size=8
  88. 0x0000 00 00 00 00 00 00 00 00 ........
  89. rel 0+8 t=1 runtime.memequal64+0
  90. runtime.gcbits.01 SRODATA dupok size=1
  91. 0x0000 01 .
  92. type..namedata.*interface {}- SRODATA dupok size=15
  93. 0x0000 00 0d 2a 69 6e 74 65 72 66 61 63 65 20 7b 7d ..*interface {}
  94. type.*interface {} SRODATA dupok size=56
  95. 0x0000 08 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 ................
  96. 0x0010 3b fc f8 8f 08 08 08 36 00 00 00 00 00 00 00 00 ;......6........
  97. 0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  98. 0x0030 00 00 00 00 00 00 00 00 ........
  99. rel 24+8 t=1 runtime.memequal64·f+0
  100. rel 32+8 t=1 runtime.gcbits.01+0
  101. rel 40+4 t=5 type..namedata.*interface {}-+0
  102. rel 48+8 t=1 type.interface {}+0
  103. runtime.gcbits.02 SRODATA dupok size=1
  104. 0x0000 02 .
  105. type.interface {} SRODATA dupok size=80
  106. 0x0000 10 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 ................
  107. 0x0010 39 7a 09 0f 02 08 08 14 00 00 00 00 00 00 00 00 9z..............
  108. 0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  109. 0x0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  110. 0x0040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  111. rel 24+8 t=1 runtime.nilinterequal·f+0
  112. rel 32+8 t=1 runtime.gcbits.02+0
  113. rel 40+4 t=5 type..namedata.*interface {}-+0
  114. rel 44+4 t=-32763 type.*interface {}+0
  115. rel 56+8 t=1 type.interface {}+80
  116. type..namedata.*[]interface {}- SRODATA dupok size=17
  117. 0x0000 00 0f 2a 5b 5d 69 6e 74 65 72 66 61 63 65 20 7b ..*[]interface {
  118. 0x0010 7d }
  119. type.*[]interface {} SRODATA dupok size=56
  120. 0x0000 08 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 ................
  121. 0x0010 9d 9c 0e 59 08 08 08 36 00 00 00 00 00 00 00 00 ...Y...6........
  122. 0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  123. 0x0030 00 00 00 00 00 00 00 00 ........
  124. rel 24+8 t=1 runtime.memequal64·f+0
  125. rel 32+8 t=1 runtime.gcbits.01+0
  126. rel 40+4 t=5 type..namedata.*[]interface {}-+0
  127. rel 48+8 t=1 type.[]interface {}+0
  128. type.[]interface {} SRODATA dupok size=56
  129. 0x0000 18 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 ................
  130. 0x0010 76 de 99 0d 02 08 08 17 00 00 00 00 00 00 00 00 v...............
  131. 0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  132. 0x0030 00 00 00 00 00 00 00 00 ........
  133. rel 32+8 t=1 runtime.gcbits.01+0
  134. rel 40+4 t=5 type..namedata.*[]interface {}-+0
  135. rel 44+4 t=-32763 type.*[]interface {}+0
  136. rel 48+8 t=1 type.interface {}+0
  137. type..namedata.*[1]interface {}- SRODATA dupok size=18
  138. 0x0000 00 10 2a 5b 31 5d 69 6e 74 65 72 66 61 63 65 20 ..*[1]interface
  139. 0x0010 7b 7d {}
  140. type.*[1]interface {} SRODATA dupok size=56
  141. 0x0000 08 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 ................
  142. 0x0010 a8 0e 57 36 08 08 08 36 00 00 00 00 00 00 00 00 ..W6...6........
  143. 0x0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
  144. 0x0030 00 00 00 00 00 00 00 00 ........
  145. rel 24+8 t=1 runtime.memequal64·f+0
  146. rel 32+8 t=1 runtime.gcbits.01+0
  147. rel 40+4 t=5 type..namedata.*[1]interface {}-+0
  148. rel 48+8 t=1 type.[1]interface {}+0
  149. type.[1]interface {} SRODATA dupok size=72
  150. 0x0000 10 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 ................
  151. 0x0000 00 03 66 6d 74 ..fmt
  152. gclocals·g2BeySu+wFnoycgXfElmcg== SRODATA dupok size=8
  153. 0x0000 01 00 00 00 00 00 00 00 ........
  154. gclocals·EaPwxsZ75yY1hHMVZLmk6g== SRODATA dupok size=9
  155. 0x0000 01 00 00 00 02 00 00 00 00 .........
  156. main.main.stkobj SRODATA static size=24
  157. 0x0000 01 00 00 00 00 00 00 00 f0 ff ff ff 10 00 00 00 ................
  158. 0x0010 10 00 00 00 00 00 00 00 ........
  159. rel 20+4 t=5 runtime.gcbits.02+0

总结
image.png

Go程序是如何运行的?

Go程序的入口

  • main方法?
  • runtime/rt0_操作系统_芯片架构.s

    此处的版本为go1.19 windows/amd64

$GOROOT\src\runtime\rt0_windows_amd64.s

  1. // Copyright 2011 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. #include "go_asm.h"
  5. #include "go_tls.h"
  6. #include "textflag.h"
  7. TEXT _rt0_amd64_windows(SB),NOSPLIT,$-8
  8. JMP _rt0_amd64(SB)
  9. // When building with -buildmode=(c-shared or c-archive), this
  10. // symbol is called. For dynamic libraries it is called when the
  11. // library is loaded. For static libraries it is called when the
  12. // final executable starts, during the C runtime initialization
  13. // phase.
  14. // Leave space for four pointers on the stack as required
  15. // by the Windows amd64 calling convention.
  16. TEXT _rt0_amd64_windows_lib(SB),NOSPLIT,$0x20
  17. // Create a new thread to do the runtime initialization and return.
  18. MOVQ _cgo_sys_thread_create(SB), AX
  19. MOVQ $_rt0_amd64_windows_lib_go(SB), CX
  20. MOVQ $0, DX
  21. CALL AX
  22. RET
  23. TEXT _rt0_amd64_windows_lib_go(SB),NOSPLIT,$0
  24. MOVQ $0, DI
  25. MOVQ $0, SI
  26. MOVQ $runtime·rt0_go(SB), AX
  27. JMP AX

查找_rt0_amd64跳转到那个文件 runtime/asm_amd64.s

  1. // _rt0_amd64 is common startup code for most amd64 systems when using
  2. // internal linking. This is the entry point for the program from the
  3. // kernel for an ordinary -buildmode=exe program. The stack holds the
  4. // number of arguments and the C-style argv.
  5. TEXT _rt0_amd64(SB),NOSPLIT,$-8
  6. MOVQ 0(SP), DI // argc
  7. LEAQ 8(SP), SI // argv
  8. JMP runtime·rt0_go(SB)

line 6/7 将argc和argc放入到寄存器当中 JMP runtime·rt0_go(SB)调用runtime·rt0_go

  1. Line 159 ~ 212
  2. TEXT runtime·rt0_go(SB),NOSPLIT|TOPFRAME,$0
  3. // copy arguments forward on an even stack
  4. // 在均匀的堆栈上向前复制实参(YNMT翻译)
  5. MOVQ DI, AX // argc
  6. MOVQ SI, BX // argv
  7. SUBQ $(5*8), SP // 3args 2auto
  8. ANDQ $~15, SP
  9. MOVQ AX, 24(SP)
  10. MOVQ BX, 32(SP)
  11. // create istack out of the given (operating system) stack.
  12. // _cgo_init may update stackguard.
  13. // 从给定的(操作系统)堆栈中创建istack。
  14. // _cgo_init可能会更新stackguard。
  15. // 初始化一个g0的协程 不归调度器管理
  16. MOVQ $runtime·g0(SB), DI
  17. LEAQ (-64*1024+104)(SP), BX
  18. MOVQ BX, g_stackguard0(DI)
  19. MOVQ BX, g_stackguard1(DI)
  20. MOVQ BX, (g_stack+stack_lo)(DI)
  21. MOVQ SP, (g_stack+stack_hi)(DI)
  22. // find out information about the processor we're on
  23. MOVL $0, AX
  24. CPUID
  25. CMPL AX, $0
  26. JE nocpuinfo
  27. CMPL BX, $0x756E6547 // "Genu"
  28. JNE notintel
  29. CMPL DX, $0x49656E69 // "ineI"
  30. JNE notintel
  31. CMPL CX, $0x6C65746E // "ntel"
  32. JNE notintel
  33. MOVB $1, runtime·isIntel(SB)
  34. notintel:
  35. // Load EAX=1 cpuid flags
  36. MOVL $1, AX
  37. CPUID
  38. MOVL AX, runtime·processorVersionInfo(SB)
  39. nocpuinfo:
  40. // if there is an _cgo_init, call it.
  41. MOVQ _cgo_init(SB), AX
  42. TESTQ AX, AX
  43. JZ needtls
  44. // arg 1: g0, already in DI
  45. MOVQ $setg_gcc<>(SB), SI // arg 2: setg_gcc
  46. #ifdef GOOS_android
  47. MOVQ $runtime·tls_g(SB), DX // arg 3: &tls_g
  48. // arg 4: TLS base, stored in slot 0 (Android's TLS_SLOT_SELF).
  49. // Compensate for tls_g (+16).
  50. MOVQ -16(TLS), CX
  51. #else
  52. MOVQ $0, DX // arg 3, 4: not used when using platform's TLS
  53. MOVQ $0, CX
  54. #endif

读取命令行参数

  • 复制参数数量argc和参数值argv到栈上

    初始化g0执行栈

  • g0是为了调度协程而产生的协程(母协程)

  • g0是每个Go程序的第一个协程 ``` Line337

  1. CALL runtime·check(SB)
  1. > 然后跳转到`runtime/runtime1.go`
  2. <a name="zSVJb"></a>
  3. ## 运行时检测
  4. - 检查各种类型的长度
  5. - 检查指针操作
  6. - 检查结构体字段的偏移量
  7. - 检查`atomic`原子操作
  8. - 检查CAS操作
  9. - 检查栈大小是否是2的幂次

Line343~345

  1. CALL runtime·args(SB) //argc argv 拷贝到Go语言当中
  2. CALL runtime·osinit(SB) //osinit判断系统的字长和系统的核数
  3. CALL runtime·schedinit(SB)
  1. <a name="IQwfs"></a>
  2. ## 参数初始化runtime.argc
  3. - 对命令行中的参数进行处理
  4. - 参数数量复制给argc int32
  5. - 参数值复制给argv **byte
  6. <a name="mlbGR"></a>
  7. ## 调度器初始化 runtime.schedinit
  8. - 全局栈空间内存分配
  9. - 加载命令行参数到`os.Args`
  10. - 堆内存空间的初始化
  11. - 加载操作系统环境变量
  12. - 初始化当前系统线程
  13. - 垃圾回收器的参数初始化
  14. - 算法初始化(map、hash)
  15. - 设置`process`数量

Line348

  1. MOVQ $runtime·mainPC(SB), AX // entry
  1. > mainPC

Line375~379

// mainPC is a function value for runtime.main, to be passed to newproc. // The reference to runtime.main is made via ABIInternal, since the // actual function (not the ABI0 wrapper) is needed by newproc. DATA runtime·mainPC+0(SB)/8,$runtime·main(SB) GLOBL runtime·mainPC(SB),RODATA,$8

  1. > runtime.main跳转runtime/proc.go
  2. > 执行main.main

line199 doInit(&runtime_inittask) // Must be before defer. line209 gcenable() //打开垃圾回收器 line233 doInit(&main_inittask)

//go:linkname main_main main.main func main_main()

  1. ```
  2. Line353~356
  3. ----------------------------------------------
  4. // start this M
  5. CALL runtime·mstart(SB)
  6. CALL runtime·abort(SB) // mstart should never return
  1. Line389~391
  2. ----------------------------------------------
  3. TEXT runtime·mstart(SB),NOSPLIT|TOPFRAME,$0
  4. CALL runtime·mstart0(SB)
  5. RET // not reached

创建主协程

  • 创建一个新的协程,执行runtime.main
  • 放入调度器等待调度

    初始化M

  • 初始化一个M,用来调度主协程

    主协程执行主函数

  • 执行runtime包中的init方法

  • 启动GC垃圾收集器
  • 执行用户包依赖的init方法
  • 执行用户主函数 main.main()

    总结

  • Go启动时经历了检查、各种初始化、初始化协程调度的过程

  • main.main()也是在协程中运行的

    问题

  • 调度器是什么?

  • 为什么初始化M
  • 为什么不是直接执行main.main(),而是将其放入调度器?

    体会

  • Go程序的启动过程像不像一个虚拟机,或者框架?

    Go语言是面向对象的吗?

    :::info Yes and no. Although Go has types and methods and allows an object-oriented style of programming, there is no type hierarchy. The concept of “interface” in Go provides a different approach that we believe is easy to use and in some ways more general. There are also ways to embed types in other types to provide something analogous—but not identical—to subclassing. Moreover, methods in Go are more general than in C++ or Java: they can be defined for any sort of data, even built-in types such as plain, “unboxed” integers. They are not restricted to structs (classes).
    Also, the lack of a type hierarchy makes “objects” in Go feel much more lightweight than in languages such as C++ or Java. :::


是又不是。尽管 Go 有类型和方法并允许面向对象的编程风格,但没有类型层次结构。Go 中的“接口”概念提供了一种不同的方法,我们认为这种方法易于使用,并且在某些方面更通用。还有一些方法可以将类型嵌入到其他类型中,以提供与子类化类似但不完全相同的东西。此外,Go 中的方法比 C++ 或 Java 中的方法更通用:它们可以为任何类型的数据定义,甚至是内置类型,例如普通的“unboxed”整数。它们不限于结构(类)。 此外,没有类型层次结构使得 Go 中的“对象”感觉比 C++ 或 Java 等语言更轻量。

“Yes and No”

  • Go允许面向对象的编程风格
  • Go的struct可以看作是其他语言的class
  • Go缺乏其他语言的继承结构
  • Go的接口与其他语言有很大差异

    Go的 “类”

  • 其他语言中,往往用class表示一类数据

  • class的每个实例称作”对象”
  • Go中用struct表示一类数据
  • struct中的每个实例并不是”对象”,而是此类型的”值”
  • struct也可以定义方法

    Go的继承

  • Go中并没有继承关系

  • 所谓Go的继承只是组合
  • 组合中的匿名字段,通过语法糖达成了类似继承的效果

    Go的接口

  • 接口可以定义Go中的一组行为相似的struct

  • struct并不显式实现接口,而是隐式实现

    总结

  • Go没有对象、没有类、没有继承

  • Go通过组合匿名字段来达到类似继承的效果
  • 通过以上手段去掉了面向对象中复杂而冗余的部分
  • 保留了基本的面向对象特性