[go]go如何把代碼運行起來的?


參考

代碼在硬盤上是一堆二進制

  • 弄清楚文件在硬盤/內存中的存儲值
package main

import "fmt"

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

vim查看 :%!xxd

在終端里執行 man ascii

觀察發現, 中間列和最右列 是一一對應的。
也就是說,剛剛寫完的 hello.go 文件都是由 ASCII 字符表示的(文本文件)

  • 匯編轉換位機器指令

  • go語句轉換為機器指令過程

Go 程序並不能直接運行,每條 Go 語句必須轉化為一系列的低級機器語言指令,將這些指令打包到一起,
並以二進制磁盤文件的形式存儲起來,也就是可執行目標文件。

探索編譯和運行的過程

通常將編譯和鏈接合並到一起的過程稱為構建(Build)。

編譯過程就是對源文件進行詞法分析、語法分析、語義分析、優化,最后生成匯編代碼文件,以 .s 作為文件后綴的匯編指令。
匯編器會將匯編代碼轉變成機器可以執行的指令。
由於每一條匯編語句幾乎都與一條機器指令相對應,所以只是一個簡單的一一對應,比較簡單,沒有語法、語義分析,也沒有優化這些步驟。
  • 編譯器的作用: 將高級語言翻譯成機器語言

6個階段

  • 詞法分析

使用一種有限狀態機的算法

有限自動機是有限個狀態的自動機器。
我們可以拿抽水馬桶舉例,它分為兩個狀態:“注水”和“水滿”。
摁下沖馬桶的按鈕,它轉到“注水”的狀態,而浮球上升到一定高度,就會把注水閥門關閉,它轉到“水滿”狀態。

我們會識別出if、else、int這樣的關鍵字,main、printf、age這樣的標識符,+、-、=這樣的操作符號,還有花括號、圓括號、分號這樣的符號,以及數字字面量、字符串字面量等。這些都是Token。

token一般分為這幾類:關鍵字、標識符、字面量(包含數字、字符串)、特殊符號(如加號、等號)。

slice[i] = i * (2 + 6)

總共包含 16 個非空字符,經過掃描(對那棵樹 遞歸下降算法(Recursive Descent Parsing)構建一棵完整的樹)后,

- src/cmd/compile/internal/syntax/scanner.go
最關鍵的函數就是 next 函數,它不斷地讀取下一個字符(go utf8以字符為單位)
func (s *scanner) next() {


- src/cmd/compile/internal/syntax/token.go

var tokstrings = [...]string{
    // source control
    _EOF: "EOF",

    // names and literals
    _Name:    "name",
    _Literal: "literal",

    // operators and operations
    _Operator: "op",
    _AssignOp: "op=",
    _IncOp:    "opop",
    _Assign:   "=",
    _Define:   ":=",
    _Arrow:    "<-",
    _Star:     "*",

    // delimitors
    _Lparen:    "(",
    _Lbrack:    "[",
    _Lbrace:    "{",
    _Rparen:    ")",
    _Rbrack:    "]",
    _Rbrace:    "}",
    _Comma:     ",",
    _Semi:      ";",
    _Colon:     ":",
    _Dot:       ".",
    _DotDotDot: "...",

    // keywords
    _Break:       "break",
    _Case:        "case",
    _Chan:        "chan",
    _Const:       "const",
    _Continue:    "continue",
    _Default:     "default",
    _Defer:       "defer",
    _Else:        "else",
    _Fallthrough: "fallthrough",
    _For:         "for",
    _Func:        "func",
    _Go:          "go",
    _Goto:        "goto",
    _If:          "if",
    _Import:      "import",
    _Interface:   "interface",
    _Map:         "map",
    _Package:     "package",
    _Range:       "range",
    _Return:      "return",
    _Select:      "select",
    _Struct:      "struct",
    _Switch:      "switch",
    _Type:        "type",
    _Var:         "var",
}

  • 語法分析
    例如“2+3*5”,你會得到一棵類似下圖的AST。

  • 語義分析

以“You can never drink too much water.” 這句話為例。它的確切含義是什么?
是“你不能喝太多水”,
還是“你喝多少水都不嫌多”?

實際上,這兩種解釋都是可以的,我們只有聯系上下文才能知道它的准確含義。

詞法分析是把程序分割成一個個Token的過程,可以通過構造有限自動機來實現。

語法分析是把程序的結構識別出來,並形成一棵便於由計算機處理的抽象語法樹。可以用遞歸下降的算法來實現。

語義分析是消除語義模糊,生成一些屬性信息,讓計算機能夠依據這些信息生成目標代碼。
  • 目標代碼生成與優化
    不同機器的機器字長、寄存器等等都不一樣,意味着在不同機器上跑的機器碼是不一樣的。最后一步的目的就是要生成能在不同 CPU 架構上運行的代碼。
    為了榨干機器的每一滴油水,目標代碼優化器會對一些指令進行優化,例如使用移位指令代替乘法指令等。

  • 鏈接
    鏈接過程就是要把編譯器生成的一個個目標文件鏈接成可執行文件。最終得到的文件是分成各種段的,比如數據段、代碼段、BSS段等等,運行時會被裝載到內存中。各個段具有不同的讀寫、執行屬性,保護了程序的安全運行。

從上圖: 將編寫的一個c程序(源代碼 )轉換成可以在硬件上運行的程序(可執行代碼 ),需要進行
    編譯階段
        先通過“編譯器 “把一個 .c/.cpp 源代碼 編譯成 .s的匯編代碼;
        再經過“匯編器 ” 把這 個.s的匯編代碼匯編成 .o 的 目標代碼
    鏈接階段
        通過連接其他 .o 代碼(如果需要的話) 庫文件 和 1 中的.o 目標代碼生成可執行文件

該文件流被這三種程序(紅色)的加工,分別表現出四種形式(藍色),這就是c程序的編譯和鏈接過程。

如果再詳細的話,編譯器在將源文件編譯成匯編文件的過程又分為:預處理階段(生成 .i 代碼) 和  優化階段

go編譯流程查看


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM