<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          如何閱讀Go源碼?

          共 10898字,需瀏覽 22分鐘

           ·

          2022-05-31 08:02

          前言

          哈嘍,大家好,我是asong;最近在看Go語言調(diào)度器相關(guān)的源碼,發(fā)現(xiàn)看源碼真是個(gè)技術(shù)活,所以本文就簡單總結(jié)一下該如何查看Go源碼,希望對你們有幫助。

          Go源碼包括哪些?

          以我個(gè)人理解,Go源碼主要分為兩部分,一部分是官方提供的標(biāo)準(zhǔn)庫,一部分是Go語言的底層實(shí)現(xiàn),Go語言的所有源碼/標(biāo)準(zhǔn)庫/編譯器都在src目錄下:https://github.com/golang/go/tree/master/src,想看什么庫的源碼任君選擇;

          觀看Go標(biāo)準(zhǔn)庫 and Go底層實(shí)現(xiàn)的源代碼難易度也是不一樣的,我們一般也可以先從標(biāo)準(zhǔn)庫入手,挑選你感興趣的模塊,把它吃透,有了這個(gè)基礎(chǔ)后,我們在看Go語言底層實(shí)現(xiàn)的源代碼會(huì)稍微輕松一些;下面就針對我個(gè)人的一點(diǎn)學(xué)習(xí)心得分享一下如何查看Go源碼;

          查看標(biāo)準(zhǔn)庫源代碼

          標(biāo)準(zhǔn)庫的源代碼看起來稍容易些,因?yàn)闃?biāo)準(zhǔn)庫也屬于上層應(yīng)用,我們可以借助IDE的幫忙,其在IDE上就可以跳轉(zhuǎn)到源代碼包,我們只需要不斷來回跳轉(zhuǎn)查看各個(gè)函數(shù)實(shí)現(xiàn)做好筆記即可,因?yàn)橐恍┰创a設(shè)計(jì)的比較復(fù)雜,大家在看時(shí)最好通過畫圖輔助一下,個(gè)人覺得畫UML是最有助于理解的,能更清晰的理清各個(gè)實(shí)體的關(guān)系;

          有些時(shí)候只看代碼是很難理解的,這時(shí)我們使用在線調(diào)試輔助我們理解,使用IDE提供的調(diào)試器或者GDB都可以達(dá)到目的,寫一個(gè)簡單的demo,斷點(diǎn)一打,單步調(diào)試走起來,比如你要查看fmt.Println的源代碼,開局一個(gè)小紅點(diǎn),然后就是點(diǎn)點(diǎn)點(diǎn);

          查看Go語言底層實(shí)現(xiàn)

          人都是會(huì)對未知領(lǐng)域充滿好奇,當(dāng)使用一段時(shí)間Go語言后,就想更深入的搞明白一些事情,例如:Go程序的啟動(dòng)過程是怎樣的,goroutine是怎么調(diào)度的,map是怎么實(shí)現(xiàn)的等等一些Go底層的實(shí)現(xiàn),這種直接依靠IDE跳轉(zhuǎn)追溯代碼是辦不到的,這些都屬于Go語言的內(nèi)部實(shí)現(xiàn),大都在src目錄下的runtime包內(nèi)實(shí)現(xiàn),其實(shí)現(xiàn)了垃圾回收,并發(fā)控制, 棧管理以及其他一些 Go 語言的關(guān)鍵特性,在編譯Go代碼為機(jī)器代碼時(shí)也會(huì)將其也編譯進(jìn)來,runtime就是Go程序執(zhí)行時(shí)候使用的庫,所以一些Go底層原理都在這個(gè)包內(nèi),我們需要借助一些方式才能查看到Go程序執(zhí)行時(shí)的代碼,這里分享兩種方式:分析匯編代碼、dlv調(diào)試;

          分析匯編代碼

          前面我們已經(jīng)介紹了Go語言實(shí)現(xiàn)了runtime庫,我們想看到一些Go語言關(guān)鍵字特性對應(yīng)runtime里的那個(gè)函數(shù),可以查看匯編代碼,Go語言的匯編使用的plan9,與x86匯編差別還是很大,很多朋友都不熟悉plan9的匯編,但是要想看懂Go源碼還是要對plan9匯編有一個(gè)基本的了解的,這里推薦曹大的文章:plan9 assembly 完全解析,會(huì)一點(diǎn)匯編我們就可以看源代碼了,比如想在我們想看make是怎么初始化slice的,這時(shí)我們可以先寫一個(gè)簡單的demo

          //?main.go
          import?"fmt"

          func?main()?{
          ?s?:=?make([]int,?10,?20)
          ?fmt.Println(s)
          }

          有兩種方式可以查看匯編代碼:

          1.?go?tool?compile?-S?-N?-l?main.go
          2.?go?build?main.go?&&?go?tool?objdump?./main

          方式一是將源代碼編譯成.o文件,并輸出匯編代碼,方式二是反匯編,這里推薦使用方式一,執(zhí)行方式一命令后,我們可以看到對應(yīng)的匯編代碼如下:

          s := make([]int, 10, 20)對應(yīng)的源代碼就是 runtime.makeslice(SB),這時(shí)候我們就去runtime包下找makeslice函數(shù),不斷追蹤下去就可查看源碼實(shí)現(xiàn)了,可在runtime/slice.go中找到:

          在線調(diào)試

          雖然上面的方法可以幫助我們定位到源代碼,但是后續(xù)的操作全靠review還是難于理解的,如果能在線調(diào)試跟蹤代碼可以更好助于我們理解,目前Go語言支持GDBLLDBDelve調(diào)試器,但只有Delve是專門為Go語言設(shè)計(jì)開發(fā)的調(diào)試工具,所以使用Delve可以輕松調(diào)試Go匯編程序,Delve的入門文章有很多,這篇就不在介紹Delve的詳細(xì)使用方法,入門大家可以看曹大的文章:https://chai2010.cn/advanced-go-programming-book/ch3-asm/ch3-09-debug.html,本文就使用一個(gè)小例子帶大家來看一看dlv如何調(diào)試Go源碼,大家都知道向一個(gè)nil的切片追加元素,不會(huì)有任何問題,在源碼中是怎么實(shí)現(xiàn)的呢?接下老我們使用dlv調(diào)試跟蹤一下,先寫一個(gè)小demo

          import?"fmt"

          func?main()?{
          ?var?s?[]int
          ?s?=?append(s,?1)
          ?fmt.Println(s)
          }

          進(jìn)入命令行包目錄,然后輸入dlv debug進(jìn)入調(diào)試

          $?dlv?debug
          Type?'help'?for?list?of?commands.
          (dlv)

          因?yàn)檫@里我們想看到append的內(nèi)部實(shí)現(xiàn),所以在append那行加上斷點(diǎn),執(zhí)行如下命令:

          (dlv)?break?main.go:7
          Breakpoint?1?set?at?0x10aba57?for?main.main()?./main.go:7

          執(zhí)行continue命令,運(yùn)行到斷點(diǎn)處:

          (dlv)?continue
          >?main.main()?./main.go:7?(hits?goroutine(1):1?total:1)?(PC:?0x10aba57)
          ?????2:?
          ?????3:?import?"fmt"
          ?????4:?
          ?????5:?func?main()?{
          ?????6:?????????var?s?[]int
          =>???7:?????????s?=?append(s,?1)
          ?????8:?????????fmt.Println(s)
          ?????9:?}

          接下來我們執(zhí)行disassemble反匯編命令查看main函數(shù)對應(yīng)的匯編代碼:

          (dlv)?disassemble
          TEXT?main.main(SB)?/Users/go/src/asong.cloud/Golang_Dream/code_demo/src_code/main.go
          ????????main.go:5???????0x10aba20???????4c8d6424e8??????????????????????lea?r12,?ptr?[rsp-0x18]
          ????????main.go:5???????0x10aba25???????4d3b6610????????????????????????cmp?r12,?qword?ptr?[r14+0x10]
          ????????main.go:5???????0x10aba29???????0f86f6000000????????????????????jbe?0x10abb25
          ????????main.go:5???????0x10aba2f???????4881ec98000000??????????????????sub?rsp,?0x98
          ????????main.go:5???????0x10aba36???????4889ac2490000000????????????????mov?qword?ptr?[rsp+0x90],?rbp
          ????????main.go:5???????0x10aba3e???????488dac2490000000????????????????lea?rbp,?ptr?[rsp+0x90]
          ????????main.go:6???????0x10aba46???????48c744246000000000??????????????mov?qword?ptr?[rsp+0x60],?0x0
          ????????main.go:6???????0x10aba4f???????440f117c2468????????????????????movups?xmmword?ptr?[rsp+0x68],?xmm15
          ????????main.go:7???????0x10aba55???????eb00????????????????????????????jmp?0x10aba57
          =>??????main.go:7???????0x10aba57*??????488d05a2740000??????????????????lea?rax,?ptr?[rip+0x74a2]
          ????????main.go:7???????0x10aba5e???????31db????????????????????????????xor?ebx,?ebx
          ????????main.go:7???????0x10aba60???????31c9????????????????????????????xor?ecx,?ecx
          ????????main.go:7???????0x10aba62???????4889cf??????????????????????????mov?rdi,?rcx
          ????????main.go:7???????0x10aba65???????be01000000??????????????????????mov?esi,?0x1
          ????????main.go:7???????0x10aba6a???????e871c3f9ff??????????????????????call?$runtime.growslice
          ????????main.go:7???????0x10aba6f???????488d5301????????????????????????lea?rdx,?ptr?[rbx+0x1]
          ????????main.go:7???????0x10aba73???????eb00????????????????????????????jmp?0x10aba75
          ????????main.go:7???????0x10aba75???????48c70001000000??????????????????mov?qword?ptr?[rax],?0x1
          ????????main.go:7???????0x10aba7c???????4889442460??????????????????????mov?qword?ptr?[rsp+0x60],?rax
          ????????main.go:7???????0x10aba81???????4889542468??????????????????????mov?qword?ptr?[rsp+0x68],?rdx
          ????????main.go:7???????0x10aba86???????48894c2470??????????????????????mov?qword?ptr?[rsp+0x70],?rcx
          ????????main.go:8???????0x10aba8b???????440f117c2450????????????????????movups?xmmword?ptr?[rsp+0x50],?xmm15
          ????????main.go:8???????0x10aba91???????488d542450??????????????????????lea?rdx,?ptr?[rsp+0x50]
          ????????main.go:8???????0x10aba96???????4889542448??????????????????????mov?qword?ptr?[rsp+0x48],?rdx
          ????????main.go:8???????0x10aba9b???????488b442460??????????????????????mov?rax,?qword?ptr?[rsp+0x60]
          ????????main.go:8???????0x10abaa0???????488b5c2468??????????????????????mov?rbx,?qword?ptr?[rsp+0x68]
          ????????main.go:8???????0x10abaa5???????488b4c2470??????????????????????mov?rcx,?qword?ptr?[rsp+0x70]
          ????????main.go:8???????0x10abaaa???????e8f1dff5ff??????????????????????call?$runtime.convTslice
          ????????main.go:8???????0x10abaaf???????4889442440??????????????????????mov?qword?ptr?[rsp+0x40],?rax
          ????????main.go:8???????0x10abab4???????488b542448??????????????????????mov?rdx,?qword?ptr?[rsp+0x48]
          ????????main.go:8???????0x10abab9???????8402????????????????????????????test?byte?ptr?[rdx],?al
          ????????main.go:8???????0x10ababb???????488d35be640000??????????????????lea?rsi,?ptr?[rip+0x64be]
          ????????main.go:8???????0x10abac2???????488932??????????????????????????mov?qword?ptr?[rdx],?rsi
          ????????main.go:8???????0x10abac5???????488d7a08????????????????????????lea?rdi,?ptr?[rdx+0x8]
          ????????main.go:8???????0x10abac9???????833d30540d0000??????????????????cmp?dword?ptr?[runtime.writeBarrier],?0x0
          ????????main.go:8???????0x10abad0???????7402????????????????????????????jz?0x10abad4
          ????????main.go:8???????0x10abad2???????eb06????????????????????????????jmp?0x10abada
          ????????main.go:8???????0x10abad4???????48894208????????????????????????mov?qword?ptr?[rdx+0x8],?rax
          ????????main.go:8???????0x10abad8???????eb08????????????????????????????jmp?0x10abae2
          ????????main.go:8???????0x10abada???????e8213ffbff??????????????????????call?$runtime.gcWriteBarrier
          ????????main.go:8???????0x10abadf???????90??????????????????????????????nop
          ????????main.go:8???????0x10abae0???????eb00????????????????????????????jmp?0x10abae2
          ????????main.go:8???????0x10abae2???????488b442448??????????????????????mov?rax,?qword?ptr?[rsp+0x48]
          ????????main.go:8???????0x10abae7???????8400????????????????????????????test?byte?ptr?[rax],?al
          ????????main.go:8???????0x10abae9???????eb00????????????????????????????jmp?0x10abaeb
          ????????main.go:8???????0x10abaeb???????4889442478??????????????????????mov?qword?ptr?[rsp+0x78],?rax
          ????????main.go:8???????0x10abaf0???????48c784248000000001000000????????mov?qword?ptr?[rsp+0x80],?0x1
          ????????main.go:8???????0x10abafc???????48c784248800000001000000????????mov?qword?ptr?[rsp+0x88],?0x1
          ????????main.go:8???????0x10abb08???????bb01000000??????????????????????mov?ebx,?0x1
          ????????main.go:8???????0x10abb0d???????4889d9??????????????????????????mov?rcx,?rbx
          ????????main.go:8???????0x10abb10???????e8aba8ffff??????????????????????call?$fmt.Println
          ????????main.go:9???????0x10abb15???????488bac2490000000????????????????mov?rbp,?qword?ptr?[rsp+0x90]
          ????????main.go:9???????0x10abb1d???????4881c498000000??????????????????add?rsp,?0x98
          ????????main.go:9???????0x10abb24???????c3??????????????????????????????ret
          ????????main.go:5???????0x10abb25???????e8f61efbff??????????????????????call?$runtime.morestack_noctxt
          ????????.:0?????????????0x10abb2a???????e9f1feffff??????????????????????jmp?$main.main

          從以上內(nèi)容我們看到調(diào)用了runtime.growslice方法,我們在這里加一個(gè)斷點(diǎn):

          (dlv)?break?runtime.growslice
          Breakpoint?2?set?at?0x1047dea?for?runtime.growslice()?/usr/local/opt/go/libexec/src/runtime/slice.go:162

          之后我們再次執(zhí)行continue執(zhí)行到該斷點(diǎn)處:

          (dlv)?continue
          >?runtime.growslice()?/usr/local/opt/go/libexec/src/runtime/slice.go:162?(hits?goroutine(1):1?total:1)?(PC:?0x1047dea)
          Warning:?debugging?optimized?function
          ???157:?//?NOT?to?the?new?requested?capacity.
          ???158:?//?This?is?for?codegen?convenience.?The?old?slice's?length?is?used?immediately
          ???159:?//?to?calculate?where?to?write?new?values?during?an?append.
          ???160:?//?TODO:?When?the?old?backend?is?gone,?reconsider?this?decision.
          ???161:?//?The?SSA?backend?might?prefer?the?new?length?or?to?return?only?ptr/cap?and?save?stack?space.
          =>?162:?func?growslice(et?*_type,?old?slice,?cap?int)?slice?{
          ???163:?????????if?raceenabled?{
          ???164:?????????????????callerpc?:=?getcallerpc()
          ???165:?????????????????racereadrangepc(old.array,?uintptr(old.len*int(et.size)),?callerpc,?funcPC(growslice))
          ???166:?????????}
          ???167:?????????if?msanenabled?{

          之后就是不斷的單步調(diào)試可以看出來切片的擴(kuò)容策略;到這里大家也就明白了為啥向nil的切片追加數(shù)據(jù)不會(huì)有問題了,因?yàn)樵谌萘坎粔驎r(shí)會(huì)調(diào)用growslice函數(shù)進(jìn)行擴(kuò)容,具體擴(kuò)容規(guī)則大家可以繼續(xù)追蹤,打臉網(wǎng)上那些瞎寫的文章。

          上文我們介紹調(diào)試匯編的一個(gè)基本流程,下面在介紹兩個(gè)我在看源代碼時(shí)經(jīng)常使用的命令;

          • goroutines命令:通過goroutines命令(簡寫grs),我們可以查看所goroutine,通過goroutine (alias: gr)命令可以查看當(dāng)前的gourtine
          (dlv)?grs
          *?Goroutine?1?-?User:?./main.go:7?main.main?(0x10aba6f)?(thread?218565)
          ??Goroutine?2?-?User:?/usr/local/opt/go/libexec/src/runtime/proc.go:367?runtime.gopark?(0x1035232)?[force?gc?(idle)]
          ??Goroutine?3?-?User:?/usr/local/opt/go/libexec/src/runtime/proc.go:367?runtime.gopark?(0x1035232)?[GC?sweep?wait]
          ??Goroutine?4?-?User:?/usr/local/opt/go/libexec/src/runtime/proc.go:367?runtime.gopark?(0x1035232)?[GC?scavenge?wait]
          ??Goroutine?5?-?User:?/usr/local/opt/go/libexec/src/runtime/proc.go:367?runtime.gopark?(0x1035232)?[finalizer?wait]
          • stack命令:通過stack命令(簡寫bt),我們可查看當(dāng)前函數(shù)調(diào)用棧信息:
          (dlv)?bt
          0??0x0000000001047e15?in?runtime.growslice
          ???at?/usr/local/opt/go/libexec/src/runtime/slice.go:183
          1??0x00000000010aba6f?in?main.main
          ???at?./main.go:7
          2??0x0000000001034e13?in?runtime.main
          ???at?/usr/local/opt/go/libexec/src/runtime/proc.go:255
          3??0x000000000105f9c1?in?runtime.goexit
          ???at?/usr/local/opt/go/libexec/src/runtime/asm_amd64.s:1581
          • regs命令:通過regs命令可以查看全部的寄存器狀態(tài),可以通過單步執(zhí)行來觀察寄存器的變化:
          (dlv)?regs
          ???Rip?=?0x0000000001047e15
          ???Rsp?=?0x000000c00010de68
          ???Rax?=?0x00000000010b2f00
          ???Rbx?=?0x0000000000000000
          ???Rcx?=?0x0000000000000000
          ???Rdx?=?0x0000000000000008
          ???Rsi?=?0x0000000000000001
          ???Rdi?=?0x0000000000000000
          ???Rbp?=?0x000000c00010ded0
          ????R8?=?0x0000000000000000
          ????R9?=?0x0000000000000008
          ???R10?=?0x0000000001088c40
          ???R11?=?0x0000000000000246
          ???R12?=?0x000000c00010df60
          ???R13?=?0x0000000000000000
          ???R14?=?0x000000c0000001a0
          ???R15?=?0x00000000000000c8
          Rflags?=?0x0000000000000202?????[IF?IOPL=0]
          ????Cs?=?0x000000000000002b
          ????Fs?=?0x0000000000000000
          ????Gs?=?0x0000000000000000
          • locals命令:通過locals命令,可以查看當(dāng)前函數(shù)所有變量值:
          (dlv)?locals
          newcap?=?1
          doublecap?=?0

          總結(jié)

          看源代碼的過程是沒有捷徑可走的,如果說有,那就是可以先看一些大佬輸出的底層原理的文章,然后參照其文章一步步入門源碼閱讀,最終還是要自己去克服這個(gè)困難,本文介紹了我自己查看源碼的一些方式,你是否有更簡便的方式呢?歡迎評論區(qū)分享出來~。

          好啦,本文到這里就結(jié)束了,我是asong,我們下期見。

          瀏覽 28
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  毛片A片| 天干天干天夜夜 | 91人人撸 | 日本天堂视频 | 青青草视频首页 |