主頁 >  其他 > 詳解 Go 程式的啟動流程,你知道 g0,m0 是什么嗎?

詳解 Go 程式的啟動流程,你知道 g0,m0 是什么嗎?

2021-04-17 10:35:19 其他

大家好,我是煎魚,

自古應用程式均從 Hello World 開始,你我所寫的 Go 語言亦然:

import "fmt"

func main() {
 fmt.Println("hello world.")
}

這段程式的輸出結果為 hello world.,就是這么的簡單又直接,但這時候又不禁思考了起來,這個 hello world. 是怎么輸出來,經歷了什么程序,

真是非常的好奇,今天我們就一起來探一探 Go 程式的啟動流程,其中涉及到 Go Runtime 的調度器啟動,g0,m0 又是什么?

車門焊死,正式開始吸魚之路,

Go 引導階段

查找入口

首先編譯上文提到的示例程式:

$ GOFLAGS="-ldflags=-compressdwarf=false" go build 

在命令中指定了 GOFLAGS 引數,這是因為在 Go1.11 起,為了減少二進制檔案大小,除錯資訊會被壓縮,導致在 MacOS 上使用 gdb 時無法理解壓縮的 DWARF 的含義是什么(而我恰恰就是用的 MacOS),

因此需要在本次除錯中將其關閉,再使用 gdb 進行除錯,以此達到觀察的目的:

$ gdb awesomeProject 
(gdb) info files
Symbols from "/Users/eddycjy/go-application/awesomeProject/awesomeProject".
Local exec file:
 `/Users/eddycjy/go-application/awesomeProject/awesomeProject', file type mach-o-x86-64.
 Entry point: 0x1063c80
 0x0000000001001000 - 0x00000000010a6aca is .text
 ...
(gdb) b *0x1063c80
Breakpoint 1 at 0x1063c80: file /usr/local/Cellar/go/1.15/libexec/src/runtime/rt0_darwin_amd64.s, line 8.

通過 Entry point 的除錯,可看到真正的程式入口在 runtime 包中,不同的計算機架構指向不同,例如:

  • MacOS 在 src/runtime/rt0_darwin_amd64.s

  • Linux 在 src/runtime/rt0_linux_amd64.s

其最終指向了 rt0_darwin_amd64.s 檔案,這個檔案名稱非常的直觀:

Breakpoint 1 at 0x1063c80: file /usr/local/Cellar/go/1.15/libexec/src/runtime/rt0_darwin_amd64.s, line 8.

rt0 代表 runtime0 的縮寫,指代運行時的創世,超級奶爸:

  • darwin 代表目標作業系統(GOOS),

  • amd64 代表目標作業系統架構(GOHOSTARCH),

同時 Go 語言還支持更多的目標系統架構,例如:AMD64、AMR、MIPS、WASM 等:

原始碼目錄

若有興趣可到 src/runtime 目錄下進一步查看,這里就不一一介紹了,

入口方法

在 rt0_linux_amd64.s 檔案中,可發現 _rt0_amd64_darwin JMP 跳轉到了 _rt0_amd64 方法:

TEXT _rt0_amd64_darwin(SB),NOSPLIT,$-8
 JMP _rt0_amd64(SB)
...

緊接著又跳轉到 runtime·rt0_go 方法:

TEXT _rt0_amd64(SB),NOSPLIT,$-8
 MOVQ 0(SP), DI // argc
 LEAQ 8(SP), SI // argv
 JMP runtime·rt0_go(SB)

該方法將程式輸入的 argc 和 argv 從記憶體移動到暫存器中,

堆疊指標(SP)的前兩個值分別是 argc 和 argv,其對應引數的數量和具體各引數的值,

開啟主線

程式引數準備就緒后,正式初始化的方法落在 runtime·rt0_go 方法中:

TEXT runtime·rt0_go(SB),NOSPLIT,$0
 ...
 CALL runtime·check(SB)
 MOVL 16(SP), AX  // copy argc
 MOVL AX, 0(SP)
 MOVQ 24(SP), AX  // copy argv
 MOVQ AX, 8(SP)
 CALL runtime·args(SB)
 CALL runtime·osinit(SB)
 CALL runtime·schedinit(SB)

 // create a new goroutine to start program
 MOVQ $runtime·mainPC(SB), AX  // entry
 PUSHQ AX
 PUSHQ $0   // arg size
 CALL runtime·newproc(SB)
 POPQ AX
 POPQ AX

 // start this M
 CALL runtime·mstart(SB)
 ...
  • runtime.check:運行時型別檢查,主要是校驗編譯器的翻譯作業是否正確,是否有 “坑”,基本代碼均為檢查 int8unsafe.Sizeof 方法下是否等于 1 這類動作,

  • runtime.args:系統引數傳遞,主要是將系統引數轉換傳遞給程式使用,

  • runtime.osinit:系統基本引數設定,主要是獲取 CPU 核心數和記憶體物理頁大小,

  • runtime.schedinit:進行各種運行時組件的初始化,包含調度器、記憶體分配器、堆、堆疊、GC 等一大堆初始化作業,會進行 p 的初始化,并將 m0 和某一個 p 進行系結,

  • runtime.main:主要作業是運行 main goroutine,雖然在runtime·rt0_go 中指向的是$runtime·mainPC,但實質指向的是 runtime.main

  • runtime.newproc:創建一個新的 goroutine,且系結 runtime.main 方法(也就是應用程式中的入口 main 方法),并將其放入 m0 系結的p的本地佇列中去,以便后續調度,

  • runtime.mstart:啟動 m,調度器開始進行回圈調度,

runtime·rt0_go 方法中,其主要是完成各類運行時的檢查,系統引數設定和獲取,并進行大量的 Go 基礎組件初始化,

初始化完畢后進行主協程(main goroutine)的運行,并放入等待佇列(GMP 模型),最后調度器開始進行回圈調度,

小結

根據上述原始碼剖析,可以得出如下 Go 應用程式引導的流程圖:

Go 程式引導程序

在 Go 語言中,實際的運行入口并不是用戶日常所寫的 main func,更不是 runtime.main 方法,而是從 rt0_*_amd64.s 開始,最終再一路 JMP 到 runtime·rt0_go 里去,再在該方法里完成一系列 Go 自身所需要完成的絕大部分初始化動作,

其中整體包括:

  • 運行時型別檢查、系統引數傳遞、CPU 核數獲取及設定、運行時組件的初始化(調度器、記憶體分配器、堆、堆疊、GC 等),

  • 運行 main goroutine,

  • 運行相應的 GMP 等大量預設行為,

  • 涉及到調度器相關的大量知識,

后續將會繼續剖析將進一步剖析 runtime·rt0_go 里的愛與恨,尤其像是 runtime.mainruntime.schedinit 等調度方法,都有非常大的學習價值,有興趣的小伙伴可以持續關注,

Go 調度器初始化

知道了 Go 程式是怎么引導起來的之后,我們需要了解 Go Runtime 中調度器是怎么流轉的,

runtime.mstart

這里主要關注 runtime.mstart 方法:

func mstart() {
 // 獲取 g0
 _g_ := getg()

 // 確定堆疊邊界
 osStack := _g_.stack.lo == 0
 if osStack {
  size := _g_.stack.hi
  if size == 0 {
   size = 8192 * sys.StackGuardMultiplier
  }
  _g_.stack.hi = uintptr(noescape(unsafe.Pointer(&size)))
  _g_.stack.lo = _g_.stack.hi - size + 1024
 }
 _g_.stackguard0 = _g_.stack.lo + _StackGuard
 _g_.stackguard1 = _g_.stackguard0
  
  // 啟動 m,進行調度器回圈調度
 mstart1()

 // 退出執行緒
 if mStackIsSystemAllocated() {
  osStack = true
 }
 mexit(osStack)
}
  • 呼叫 getg 方法獲取 GMP 模型中的 g,此處獲取的是 g0,

  • 通過檢查 g 的執行堆疊 _g_.stack 的邊界(堆疊的邊界正好是 lo, hi)來確定是否為系統堆疊,若是,則根據系統堆疊初始化 g 執行堆疊的邊界,

  • 呼叫 mstart1 方法啟動系統執行緒 m,進行調度器回圈調度,

  • 呼叫 mexit 方法退出系統執行緒 m,

runtime.mstart1

這么看來其實質邏輯在 mstart1 方法,我們繼續往下剖析:

func mstart1() {
 // 獲取 g,并判斷是否為 g0
 _g_ := getg()
 if _g_ != _g_.m.g0 {
  throw("bad runtime·mstart")
 }

 // 初始化 m 并記錄呼叫方 pc、sp
 save(getcallerpc(), getcallersp())
 asminit()
 minit()

 // 設定信號 handler
 if _g_.m == &m0 {
  mstartm0()
 }
 // 運行啟動函式
 if fn := _g_.m.mstartfn; fn != nil {
  fn()
 }

 if _g_.m != &m0 {
  acquirep(_g_.m.nextp.ptr())
  _g_.m.nextp = 0
 }
 schedule()
}
  • 呼叫 getg 方法獲取 g,并且通過前面系結的 _g_.m.g0 判斷所獲取的 g 是否 g0,若不是,則直接拋出致命錯誤,因為調度器僅在 g0 上運行,

  • 呼叫 minit 方法初始化 m,并記錄呼叫方的 PC、SP,便于后續 schedule 階段時的復用,

  • 若確定當前的 g 所系結的 m 是 m0,則呼叫 mstartm0 方法,設定信號 handler,該動作必須在 minit 方法之后,這樣 minit 方法可以提前準備好執行緒,以便能夠處理信號,

  • 若當前 g 所系結的 m 有啟動函式,則運行,否則跳過,

  • 若當前 g 所系結的 m 不是 m0,則需要呼叫 acquirep 方法獲取并系結 p,也就是 m 與 p 系結,

  • 呼叫 schedule 方法進行正式調度,

忙活了一大圈,終于進入到開題的主菜了,原來潛伏的很深的 schedule 方法才是真正做調度的方法,其他都是前置處理和準備資料,

由于篇幅問題,schedule 方法會放到下篇再繼續剖析,我們先聚焦本篇的一些細節點,

問題深剖

不過到這里篇幅也已經比較長了,積累了不少問題,我們針對在 Runtime 中出鏡率最高的兩個元素進行剖析:

  1. m0 是什么,作用是?

  2. g0 是什么,作用是?

m0

m0 是 Go Runtime 所創建的第一個系統執行緒,一個 Go 行程只有一個 m0,也叫主執行緒,

從多個方面來看:

  • 資料結構:m0 和其他創建的 m 沒有任何區別,

  • 創建程序:m0 是行程在啟動時應該匯編直接復制給 m0 的,其他后續的 m 則都是 Go Runtime 內自行創建的,

  • 變數宣告:m0 和常規 m 一樣,m0 的定義就是 var m0 m,沒什么特別之處,

g0

g 一般分為三種,分別是:

  • 執行用戶任務的叫做 g,

  • 執行 runtime.main 的 main goroutine,

  • 執行調度任務的叫 g0,,

g0 比較特殊,每一個 m 都只有一個 g0(僅此只有一個 g0),且每個 m 都只會系結一個 g0,在 g0 的賦值上也是通過匯編賦值的,其余后續所創建的都是常規的 g,

從多個方面來看:

  • 資料結構:g0 和其他創建的 g 在資料結構上是一樣的,但是存在堆疊的差別,在 g0 上的堆疊分配的是系統堆疊,在 Linux 上堆疊大小默認固定 8MB,不能擴縮容,而常規的 g 起始只有 2KB,可擴容,

  • 運行狀態:g0 和常規的 g 不一樣,沒有那么多種運行狀態,也不會被調度程式搶占,調度本身就是在 g0 上運行的,

  • 變數宣告:g0 和常規 g,g0 的定義就是 var g0 g,沒什么特別之處,

小結

在本章節中我們講解了 Go 調度器初始化的一個程序,分別涉及:

  • runtime.mstart,

  • runtime.mstart1,

基于此也了解到了在調度器初始化程序中,需要準備什么,初始化什么,另外針對調度程序中最常提到的 m0、g0 的概念我們進行了梳理和說明,

總結

在今天這篇文章中,我們詳細的介紹了 Go 語言的引導啟動程序中的所有流程和初始化動作,

同時針對調度器的初始化進行了初步分析,詳細介紹了 m0、g0 的用途和區別,在下一篇文章中我們將進一步對真正調度的 schedule 方法進行詳解,這塊也是個硬骨頭了,

關注煎魚,吸取他的知識 ????

你好,我是煎魚,高一折騰過前端,參加過國賽拿了獎,大學搞過 PHP,現在整 Go,在公司負責微服務架構等相關作業推進和研發,

從大學開始靠自己賺生活費和學費,到出版 Go 暢銷書《Go 語言編程之旅》,再到獲得 GOP(Go 領域最有觀點專家)榮譽,點擊藍字查看我的出書之路

日常分享高質量文章,輸出 Go 面試、作業經驗、架構設計,歡迎加我微信:cJY0728,替你連接數千位 Go 開發愛好者,記得點贊,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/276963.html

標籤:AI

上一篇:13個你一定要知道的PyTorch特性

下一篇:我國首個 JS 語言提案在 ECMA 進入 Stage 3

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more