回首第一篇規劃這個系列方向時,我一股腦列出許多主題,現在看來當然是像是螞蟻要對抗巨人一般可笑。事實上,認真要追蹤那些主題的話,都可以寫成多於一整個系列的鐵人文篇幅吧。當時沒有想到這麼多,這是時間軸上由前往後、由後往前之間的差異。
最後一章名為開始,是因為後面的路還很長。筆者日常工作完全摸不到 GO 語言,此前完全只能說事業餘愛好者,這次鐵人賽雖然也算從頭到尾走過了 Hello World 的啟動,但對比已獲得的與已知不足的,這顯然也是走馬看花的小小郊遊罷了。
仍然充滿感謝。
感謝 GO 語言社群與前人的努力、
感謝親友的支持、感謝讀者的監督、
感謝一起並肩齊行的 IT 鐵人們每日發文的激勵作用 ... 而最感謝的還是 ITHome 願意主辦這樣的活動。讓有時覺得自己如同螺絲釘一般渺小的 IT 人有一個只跟自己比耐力的舞台。去年因故無法參賽,今年終於再回來了。
我對於 GO 語言仍然充滿好奇。固然,若說當今世上偉大的系統專案是巍峨的建築,C 語言仍是它們的紅磚:讓人信賴、習慣、有效率。但是進入到雲端時代的系統已經有了新的風貌:Docker、Kubernetes 等大型分散式系統及其基石,所使用的建築基本材料已經是 GO 而非 C。在語言設計上的優勢使得它編譯快速、就算與 C 語言對接也能呈現良好功能,但開發的方便性與易維護性使它在這類專案中廣為使用。
撰寫此篇的同時,GO 1.14 剛釋出,筆者日常業務的一部分的 CPU 架構 RISC-V 也進入了上游支援。GO 語言未來的延展性仍然是令人期待的。接下來列出系列文中留下的未解問題,當作之後繼續在學習 GO 語言的旅途中的指引。他們有些是關於 GO 語言的文化,有些則關於技術本身;有些系統問題龐大的可以專書探討,有些則是一些小觀念尚未打通。無論如何都是這系列中最真實的另外一面:系列文記載我所理解的部份,而這些問題代表我尚未了解的。
netpoll
系統是指什麼?顯然在創建檔案的時候很重要。runtime.SetFinalizer
是什麼?在整個 GO 語言 runtime 中扮演何種角色?arg.(type)
這種功能被稱作 reflect。GO 語言的 reflect 是怎麼做的?internal/race
是怎麼樣的函式庫?功能?sync
是怎樣的函式庫?功能?runtime.KeepAlive
大致上可以顧名思義。但為什麼它出現在讀寫之後?讀寫之前難道就沒有被 runtime 影響的危險嗎?sigpipe
,GO 語言如何處理 signal?.gopclntab
區段是什麼?import
關鍵字有時會引用多層結構,為什麼要這樣作?internal
什麼什麼。內部的這個關鍵字的差異是什麼?這些函式庫不都是內部的的嗎?unsafe
的用途。sched_getaffinity
並沒有像之前 write
那樣最終導到 Syscall
去。g0
和 gsignal
分別是怎麼來的?如何生成或指派的?skipPC
的具體用途?PC
、SP
、FP
、LR
等關鍵抽象暫存器,這些對於整個 stack trace 功能的具體實作為何?g0
和 gsignal
分別是怎麼來的?如何生成或指派的?heap.go
裡面看到很多 heap 的管理都有強調不能使用 heap 來管理 heap,這如何作到?fastrand
的用意為何? 為何選定特殊的魔術數字 1597334677
?m
與 g
的運作?g
和 p
之間的關係?alginit
之後才能用?//go:nosplit
這種實際上類似給編譯器的 hint,運作機制是?gostringnocopy
函式裡面有一些魔幻的手法在轉換結構體與 string
型別變數,後面的指標機制怎麼實作?gostringnocopy
?schedtrace
真的有實際用途嗎?用在何處?workbuf
的大小綁定 2K 呢?allp
的處理是看到了,那 allm
和 allg
呢?rbp
在剛進入 newproc
函式時是 0,合理嗎?func1
就好呢?這樣不是還能省一個記憶體的存取嗎?newproc
的回傳位址給將由無名函式呼叫的 newproc1
呢?acquirem
的註解為何是與 p
是否被存取有關?心理需要更好的 model 來理解這些 GO 語言的抽象物件了...acquirem
和 releasem
的語意應該要有 atomic 的感覺,為何這裡不需要呢?GO 語言有什麼確保不會發生 race condition 的假設?gfget
到底有沒有可能挨餓?gfget
之中,從 gFree.stack 拿到 G 的情況下,那兩種不同的 flag 是什麼?什麼時候可以使用相關功能?malg
使用 new
關鍵字配置所需的記憶體,相關機制為何?所取得的的記憶體應該會在 heap 上。mcache
,為何註解說是 per-P 結構,這裡卻是由 M 來提取呢?stackGuard
分別有什麼用呢?註解中是有解釋,但是還是有點抽象。runtime·goexit
)是不是這種函式就無法在 gdb 裡面定位?labels
成員代表的意義?profiling 的使用方法?runtime.main
會有特殊的待遇,不被算在系統 goroutine 裡面?go:nosplit
具體來說是在哪些條件下必須要下?getg
、getcaller*
函式好像都沒有本體,所以應該是 compiler 生成的?相關的程式在哪裡呢?minit
函式前得住解說不能配置記憶體是什麼意思?getg().m.procid
賦值自 gettid
,為什麼說是為了 debugger 的?wakep
函式與 newm
函式。xxx1
這種函式命名法?runtime.g0
和 runtime.m0
嗎?defer
和 go
都是很常用的非同步關鍵字。它們生效的機制是什麼?(使用 gdb 已知 go
可能會觸發 runtime.newproc
)main.main
會沒辦法被 linker 定位?還是說 GO 的連結方式有順序性,所以才強調 when laying down the runtime?這是第三次參賽了。有別於 2016 的跨界的追尋:trace 30個基本 Linux 系統呼叫與 2017 的與妖精共舞:在 RISC-V 架構上使用 GO 語言實作 binutils 工具包,當時的每一日就像是用競走甚至慢跑一樣的速度前進,非常疲累;這次更多的是帶著自助旅遊的心情,對於能夠理解的部份就深入一些去追蹤,搜索過資料之後無法理解的部份也不強求,相對之下是比較輕鬆的。所以一開始擬的龐大計畫看起來也無須羞赧,有些在這一趟獲得的,就會有一些在下一趟獲得。
整體來說,GO 語言的 Hello World 程式與所有其他程式一樣得從 CPU/作業系統相依的部份開始,由於內建 goroutine 這種輕量級的抽象執行單位而必需要精確控管 context,為了控管 context 又必須在仍非常早期的時候注意記憶體的狀況,比方說我們四處看到的那些編譯器選項: stack 是否可以被分割、該函式是否不能使用 write barrier 等等。之後的初始化,大多還是 G-M-P 三項之力在協調的。單就 Hello World,我們看到的還不夠多,還有許多其他的特色完全沒有觸碰到。
但那豈不是讓人更興奮嗎?我想,每一年度的鐵人賽不只是這 30 天的衝刺而已;它理所當然地包含事前充滿期待的計畫,以及事後的滿足、懊悔(XD)與不甘心等等混雜的情感,以及對於明年的自己再度興起的期待。連結起來,就是身為技術人穿越時間與自己對話、學習的過程吧。
各位讀者,祝福你們都能夠在這個系列中獲得自己想要的東西,若是有不足的部份也歡迎你們批評指教。無論如何,我們明年再見!