当前位置:网站首页>Go program lifecycle
Go program lifecycle
2022-06-24 03:05:00 【wish42】
This article is about (WHAT)
This article hopes to make it clear that Go What happens between the time the program starts writing the first line of code and the time the program exits completely , Of course, the execution logic of each program is very different , But what I want to make clear here is that all programs have something in common , The processing logic that all programs need to go through .
Why are you talking about this (WHY)
For other students ,
- Understand compilation from the code level , link , Loading process ;
- Understand the runtime from the code level ;
- Understand what compile time decisions are , What is determined by the runtime ; For me personally , Read a lot of books and materials , Want to digest and understand , And how can it be digested , Ruminate the concepts and knowledge you read and output them in your own language and words , This is a very important milestone . How to say it (HOW) Try to understand the same thing from multiple angles according to the author's own understanding , I hope different angles can confirm each other , The end result is the same goal , understand , Digest the things we don't understand .
Next, let's enter the text .
From the simplest Hello Golang Speaking of .
package main
import "fmt"
func main() {
fmt.Printf("Hello Golang!")
}We usually have one go build To compile and generate a ELF Executable file .
So let's see go build What they have done for us .
> # go build -a -n # import config packagefile fmt=$WORK/b002/_pkg_.a packagefile runtime=$WORK/b007/_pkg_.a EOF /usr/local/go/pkg/tool/linux_amd64/compile -o $WORK/b001/_pkg_.a -trimpath "$WORK/b001=>" -p main -lang=go1.17 -complete -buildid z0ktmI1YTOzCHQM1j7-R/z0ktmI1YTOzCHQM1j7-R -goversion go1.17.2 -importcfg $WORK/b001/importcfg -pack -c=4 ./hello.go $WORK/b001/_gomod_.go /usr/local/go/pkg/tool/linux_amd64/buildid -w $WORK/b001/_pkg_.a # internal cat >$WORK/b001/importcfg.link << 'EOF' # internal packagefile git.code.oa.com/laboratory/hello=$WORK/b001/_pkg_.a packagefile fmt=$WORK/b002/_pkg_.a packagefile runtime=$WORK/b007/_pkg_.a packagefile errors=$WORK/b003/_pkg_.a packagefile internal/fmtsort=$WORK/b015/_pkg_.a packagefile io=$WORK/b027/_pkg_.a packagefile math=$WORK/b018/_pkg_.a packagefile os=$WORK/b028/_pkg_.a packagefile reflect=$WORK/b016/_pkg_.a packagefile strconv=$WORK/b020/_pkg_.a packagefile sync=$WORK/b022/_pkg_.a packagefile unicode/utf8=$WORK/b021/_pkg_.a packagefile internal/abi=$WORK/b008/_pkg_.a packagefile internal/bytealg=$WORK/b009/_pkg_.a packagefile internal/cpu=$WORK/b010/_pkg_.a packagefile internal/goexperiment=$WORK/b011/_pkg_.a packagefile runtime/internal/atomic=$WORK/b012/_pkg_.a packagefile runtime/internal/math=$WORK/b013/_pkg_.a packagefile runtime/internal/sys=$WORK/b014/_pkg_.a packagefile internal/reflectlite=$WORK/b004/_pkg_.a packagefile sort=$WORK/b026/_pkg_.a packagefile math/bits=$WORK/b019/_pkg_.a packagefile internal/itoa=$WORK/b017/_pkg_.a packagefile internal/oserror=$WORK/b029/_pkg_.a packagefile internal/poll=$WORK/b030/_pkg_.a packagefile internal/syscall/execenv=$WORK/b034/_pkg_.a packagefile internal/syscall/unix=$WORK/b031/_pkg_.a packagefile internal/testlog=$WORK/b035/_pkg_.a packagefile internal/unsafeheader=$WORK/b005/_pkg_.a packagefile io/fs=$WORK/b036/_pkg_.a packagefile sync/atomic=$WORK/b024/_pkg_.a packagefile syscall=$WORK/b032/_pkg_.a packagefile time=$WORK/b033/_pkg_.a packagefile unicode=$WORK/b025/_pkg_.a packagefile internal/race=$WORK/b023/_pkg_.a packagefile path=$WORK/b037/_pkg_.a EOF mkdir -p $WORK/b001/exe/ cd . /usr/local/go/pkg/tool/linux_amd64/link -o $WORK/b001/exe/a.out -importcfg $WORK/b001/importcfg.link -buildmode=exe -buildid=yUoLSlHsIhJ4gtJbQMNi/z0ktmI1YTOzCHQM1j7-R/z0ktmI1YTOzCHQM1j7-R/yUoLSlHsIhJ4gtJbQMNi -extld=gcc $WORK/b001/_pkg_.a /usr/local/go/pkg/tool/linux_amd64/buildid -w $WORK/b001/exe/a.out # internal mv $WORK/b001/exe/a.out hello
From here we can see that from a macro point of view ELF The birth experience of documents compile,link,mv These processes ;
- compile Will go File compiled into $WORK/b001/pkg.a file ;
- link Will $WORK/b001/pkg.a Link generation $WORK/b001/exe/a.out ;
- mv The generated $WORK/b001/exe/a.out It is amended as follows go.mod File name specified in ; Of course , The process here is simplified Billion points details , Let's go a little deeper , Back to class , What does a complete compilation process have :
Seeing this, I believe most people may want to ctrl+w 了 , But not yet . Try to follow my train of thought and read on .
Lexical analysis
Let's use the code to simulate the process and results of lexical analysis :
var src = `
package main
import "fmt"
func main() {
fmt.Println("Hello Golang!")
}
`
func main() {
fset := token.NewFileSet() // positions are relative to fset
tokenfile := fset.AddFile("hello.go", fset.Base(), len(src))
// Lexical analysis
var s scanner.Scanner
s.Init(tokenfile, []byte(src), nil, scanner.ScanComments)
for {
pos, tok, lit := s.Scan()
if tok == token.EOF {
break
}
fmt.Printf("%s\t%s\t%q\n", fset.Position(pos), tok, lit)
}
}Generate results :
The process of lexical analysis is actually to convert the code file into a string slice The process of . namely []byte -> tokens.
token It can be roughly divided into the following categories :
- special token. for example ,ILLEGAL( illegal TOKEN)、EOF( end of file )、COMMENT( notes )
- Literally token. for example , identifier IDENT、 Numbers INT、 character string STRING wait .
- The operator token.+ - * / , . ; ( ) wait .
- keyword token.var,select,chan etc. .
Lexical analysis stage , A semicolon will be added at the end of each line of the source code ;. This is it. go The reason for not adding a semicolon at the end of each line of code .
Syntax analysis
The result of lexical analysis is token Sequence , And code location and other descriptive information . Similarly, we use code to simulate the process of parsing :
var src = `
package main
import "fmt"
func main() {
fmt.Println("Hello Golang!")
}
`
func main() {
fset := token.NewFileSet() // positions are relative to fset
tokenfile := fset.AddFile("hello.go", fset.Base(), len(src))
// Lexical analysis
var s scanner.Scanner
s.Init(tokenfile, []byte(src), nil, scanner.ScanComments)
for {
pos, tok, lit := s.Scan()
if tok == token.EOF {
break
}
fmt.Printf("%s\t%s\t%q\n", fset.Position(pos), tok, lit)
}
// Syntax analysis
astfile, err := parser.ParseFile(fset, "", src, 0)
if err != nil {
panic(err)
}
// Print the AST.
ast.Print(fset, astfile)
}The process of grammar analysis is actually to combine the above token The sequence builds a AST( Abstract syntax tree ) As for the abstract grammar tree, I think many students can only hear its sound but not see its body , Fortunately, we are not here Print Did you come out :
0 *ast.File {
1 . Package: 2:2
2 . Name: *ast.Ident {
3 . . NamePos: 2:10
4 . . Name: "main"
5 . }
6 . Decls: []ast.Decl (len = 2) { // Statement list
7 . . 0: *ast.GenDecl { // generic declaration node, Used to represent import,const,type,variable Statement statement
8 . . . TokPos: 3:2
9 . . . Tok: import
10 . . . Lparen: -
11 . . . Specs: []ast.Spec (len = 1) {
12 . . . . 0: *ast.ImportSpec { //
13 . . . . . Path: *ast.BasicLit { // A BasicLit node represents a literal of basic type.( This is used in the source code comment Information , I really didn't think how to translate this )
14 . . . . . . ValuePos: 3:9
15 . . . . . . Kind: STRING
16 . . . . . . Value: "\"fmt\""
17 . . . . . }
18 . . . . . EndPos: -
19 . . . . }
20 . . . }
21 . . . Rparen: -
22 . . }
23 . . 1: *ast.FuncDecl { // Function declaration , Function is defined by Name Type Body form
24 . . . Name: *ast.Ident { // Function name Description
25 . . . . NamePos: 4:7
26 . . . . Name: "main"
27 . . . . Obj: *ast.Object {
28 . . . . . Kind: func
29 . . . . . Name: "main"
30 . . . . . Decl: *(obj @ 23)
31 . . . . }
32 . . . }
33 . . . Type: *ast.FuncType { // Function type information
34 . . . . Func: 4:2
35 . . . . Params: *ast.FieldList { // parameter list
36 . . . . . Opening: 4:11 // Start position of parameter list
37 . . . . . Closing: 4:12 // End position of parameter list
38 . . . . }
39 . . . }
40 . . . Body: *ast.BlockStmt { // Function body description
41 . . . . Lbrace: 4:14 // Position of the left curly bracket of the function body
42 . . . . List: []ast.Stmt (len = 1) {
43 . . . . . 0: *ast.ExprStmt { // The first element is an expression
44 . . . . . . X: *ast.CallExpr { // Function call expression
45 . . . . . . . Fun: *ast.SelectorExpr { // Selector expressions
46 . . . . . . . . X: *ast.Ident {
47 . . . . . . . . . NamePos: 5:6
48 . . . . . . . . . Name: "fmt"
49 . . . . . . . . }
50 . . . . . . . . Sel: *ast.Ident { // Selector field description
51 . . . . . . . . . NamePos: 5:10
52 . . . . . . . . . Name: "Println"
53 . . . . . . . . }
54 . . . . . . . }
55 . . . . . . . Lparen: 5:17 // Left parenthesis (
56 . . . . . . . Args: []ast.Expr (len = 1) { // parameter list
57 . . . . . . . . 0: *ast.BasicLit {
58 . . . . . . . . . ValuePos: 5:18
59 . . . . . . . . . Kind: STRING
60 . . . . . . . . . Value: "\"Hello, Golang!\""
61 . . . . . . . . }
62 . . . . . . . }
63 . . . . . . . Ellipsis: - // "..." The location of
64 . . . . . . . Rparen: 5:34 // Right bracket )
65 . . . . . . }
66 . . . . . }
67 . . . . }
68 . . . . Rbrace: 6:2 // Position of the right curly bracket in the function body
69 . . . }
70 . . }
71 . }
72 . Scope: *ast.Scope { // There is only one scope information here main
73 . . Objects: map[string]*ast.Object (len = 1) {
74 . . . "main": *(obj @ 27)
75 . . }
76 . }
77 . Imports: []*ast.ImportSpec (len = 1) {
78 . . 0: *(obj @ 12)
79 . }
80 . Unresolved: []*ast.Ident (len = 1) {
81 . . 0: *(obj @ 46)
82 . }
83 }Above AST In the result, I added some notes according to my own understanding for your reference .
Go Medium AST Structure is defined as :
// A File node represents a Go source file.
//
// The Comments list contains all comments in the source file in order of
// appearance, including the comments that are pointed to from other nodes
// via Doc and Comment fields.
//
// For correct printing of source code containing comments (using packages
// go/format and go/printer), special care must be taken to update comments
// when a File's syntax tree is modified: For printing, comments are interspersed
// between tokens based on their position. If syntax tree nodes are
// removed or moved, relevant comments in their vicinity must also be removed
// (from the File.Comments list) or moved accordingly (by updating their
// positions). A CommentMap may be used to facilitate some of these operations.
//
// Whether and how a comment is associated with a node depends on the
// interpretation of the syntax tree by the manipulating program: Except for Doc
// and Comment comments directly associated with nodes, the remaining comments
// are "free-floating" (see also issues #18593, #20744).
//
type File struct {
Doc *CommentGroup // associated documentation; or nil
Package token.Pos // position of "package" keyword
Name *Ident // package name
Decls []Decl // top-level declarations; or nil
Scope *Scope // package scope (this file only)
Imports []*ImportSpec // imports in this file
Unresolved []*Ident // unresolved identifiers in this file
Comments []*CommentGroup // list of all comments in the source file
}One go The file will be parsed into a in the parsing phase ast.File structure .
Semantic analysis
Similarly, we use code to simulate the semantic analysis process :
var src = `
package main
import "fmt"
func main() {
b = "a" + 1 // Here is a syntax error sentence intentionally written
fmt.Println("Hello, Golang!")
}
`
func main() {
fset := token.NewFileSet() // positions are relative to fset
tokenfile := fset.AddFile("hello.go", fset.Base(), len(src))
// Lexical analysis
var s scanner.Scanner
s.Init(tokenfile, []byte(src), nil, scanner.ScanComments)
for {
pos, tok, lit := s.Scan()
if tok == token.EOF {
break
}
fmt.Printf("%s\t%s\t%q\n", fset.Position(pos), tok, lit)
}
// Syntax analysis
astfile, err := parser.ParseFile(fset, "", src, 0)
if err != nil {
panic(err)
}
// Print the AST.
ast.Print(fset, astfile)
// Semantic analysis
typeconfig := &types.Config{Importer: importer.Default()}
pkg, err := typeconfig.Check("hello.go", fset, []*ast.File{astfile}, nil)
if err != nil {
fmt.Printf("%v\n", err)
}
_ = pkg
}In the code above typeconfig.Check Will return a error, Indicates that there is a syntax error in the above program :
5:8: cannot convert "a" (untyped string constant) to untyped int
The process of semantic analysis actually includes :
- Type checking
- Keyword expansion
- Escape analysis
- Variable capture
- Function inlining
- Closure processing intermediate code generation needs to introduce a concept at this stage ,SSA(static single assignment) Static single assignment , The main purpose of this stage is to analyze and process the AST Convert to SSA. That is, there is only one assignment process description for each variable at compile time . for example , Here's what I want to talk about Why? Such a layer of intermediate code generation is required , Why not put AST Direct conversion to assembly code or machine code . We will encounter various problems in the computer field , When encountering problems, it is usually the ideal difference between the current situation and the target state , Often this ideal difference seems to be an insurmountable gap , But there is a classic solution to these problems , Is to add a layer of agency between our seemingly unattainable goals and the status quo , It is also called intermediate state . And then, let's take the intermediate state something To our target state . The gap between high-level language and machine language encountered here is so ,Go Co process scheduler in GMP The same is true of the model . If one intermediate state is not enough, add several more layers . Use GOSSAFUNC=main go build hello.go Commands can generate hello.go The visual generation process during the compilation of files ssa.html:
You can see from the source code and AST To the final generated genssa During the process, it has gone through dozens of rounds of code conversion , Each step is the process of converting the code to something closer to assembly code , This process is called SSA Downgrade . Finally generated genssa The code is actually very close to assembly code .
Machine code generation
The process of machine code generation is actually aimed at different hardware platforms SSA Degraded replacement by intermediate code , Replace with the instruction set expansion of the target platform .Golang The reason why cross platform or cross compilation can be achieved is that the back-end part of the compiler is continuously iteratively degraded for different platforms and different instruction sets .
The interaction between computer hardware and software is accomplished through instruction set , The most common instruction set architecture classification method is to divide instructions into complex instruction sets according to their complexity (CISC) And reduced instruction set (RISC).
According to my personal understanding ,
Complex instruction set (CISC) In fact, our idea is very similar to the encapsulation of various underlying interfaces that we are familiar with in business development , There is a new demand , If you need a new ability that you haven't had before, I'll add an instruction , Constantly add new instructions to the instruction set to complete various things , With the addition of various directives, the binding force on the directive specifications can only decline day by day , As a result, the length of each instruction is different , And it's getting more and more complicated ;
Reduced instruction set (RISC) The idea of is a bit layered , In the instruction set, I only provide atomic operations that can accomplish various things , If you need any new capabilities or functions, you can encapsulate the various instructions provided by me on the upper layer , But don't expect to add new things to the instruction set , Of course, the reduced instruction set has a good ability to regulate all instructions , It can keep the length of each instruction equal , Relatively simple .
For the current software developers do not need direct access to assembly code , Instead, instructions are generated through compilers and assemblers , Complex machine instructions also increase the complexity and scalability of instruction generation for compilers , So almost all the instructions generated by the compiler now use the reduced instruction set .
The final generated in the intermediate code generation phase genssa The file will be converted into assembly code by the assembler in this step :
package main
import "fmt"
func main() {
fmt.Printf("hello")
}
$ go tool compile -N -l -S hello.go
"".main STEXT size=96 args=0x0 locals=0x48 funcid=0x0
0x0000 00000 (hello.go:5) TEXT "".main(SB), ABIInternal, $80-0 // TEXT Pseudo operators , Code snippet id ;"". Represents a namespace ;SB Pseudo register ;static base Static base address pointer ;80 Indicates the function stack frame length ;0 Indicates the parameter length
0x0000 00000 (hello.go:5) MOVD 16(g), R1
0x0004 00004 (hello.go:5) PCDATA $0, $-2 // take PC register 0 The offset address is assigned as -2 The official statement is that the directive contains gc Information has nothing to do with the main process
0x0004 00004 (hello.go:5) MOVD RSP, R2
0x0008 00008 (hello.go:5) CMP R1, R2 // Compare stack space , Determine whether capacity expansion is needed
0x000c 00012 (hello.go:5) BLS 72 // If it needs to be expanded , Jump to 72 Address instruction , That is to say NOP That line
0x0010 00016 (hello.go:5) PCDATA $0, $-1
0x0010 00016 (hello.go:5) MOVD.W R30, -80(RSP)
0x0014 00020 (hello.go:5) MOVD R29, -8(RSP)
0x0018 00024 (hello.go:5) SUB $8, RSP, R29
0x001c 00028 (hello.go:5) FUNCDATA ZR, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) // With the above PCDATA The instructions are the same , yes gc Instructions to be used
0x001c 00028 (hello.go:5) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x001c 00028 (hello.go:6) MOVD $go.string."hello"(SB), R0
0x0024 00036 (hello.go:6) MOVD R0, 8(RSP)
0x0028 00040 (hello.go:6) MOVD $5, R0
0x002c 00044 (hello.go:6) MOVD R0, 16(RSP)
0x0030 00048 (hello.go:6) STP (ZR, ZR), 24(RSP)
0x0034 00052 (hello.go:6) MOVD ZR, 40(RSP)
0x0038 00056 (hello.go:6) PCDATA $1, ZR
0x0038 00056 (hello.go:6) CALL fmt.Printf(SB) // Function call instructions
0x003c 00060 (hello.go:7) MOVD -8(RSP), R29
0x0040 00064 (hello.go:7) MOVD.P 80(RSP), R30
0x0044 00068 (hello.go:7) RET (R30)
0x0048 00072 (hello.go:7) NOP
0x0048 00072 (hello.go:5) PCDATA $1, $-1
0x0048 00072 (hello.go:5) PCDATA $0, $-2
0x0048 00072 (hello.go:5) MOVD R30, R3
0x004c 00076 (hello.go:5) CALL runtime.morestack_noctxt(SB) // Perform stack expansion
0x0050 00080 (hello.go:5) PCDATA $0, $-1
0x0050 00080 (hello.go:5) JMP 0 // Jump to 0 Address re execution
0x0000 81 0b 40 f9 e2 03 00 91 5f 00 01 eb e9 01 00 54 [email protected]_......T
0x0010 fe 0f 1b f8 fd 83 1f f8 fd 23 00 d1 00 00 00 90 .........#......
0x0020 00 00 00 91 e0 07 00 f9 a0 00 80 d2 e0 0b 00 f9 ................
0x0030 ff ff 01 a9 ff 17 00 f9 00 00 00 94 fd 83 5f f8 .............._.
0x0040 fe 07 45 f8 c0 03 5f d6 e3 03 1e aa 00 00 00 94 ..E..._.........
0x0050 ec ff ff 17 00 00 00 00 00 00 00 00 00 00 00 00 ................
rel 28+8 t=3 go.string."hello"+0
rel 56+4 t=10 fmt.Printf+0
rel 76+4 t=10 runtime.morestack_noctxt+0
go.cuinfo.packagename. SDWARFCUINFO dupok size=0
0x0000 6d 61 69 6e main
""..inittask SNOPTRDATA size=32
0x0000 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 ................
0x0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
rel 24+8 t=1 fmt..inittask+0
go.string."hello" SRODATA dupok size=5
0x0000 68 65 6c 6c 6f hello
type..importpath.fmt. SRODATA dupok size=6
0x0000 00 00 03 66 6d 74 ...fmt
gclocals·33cdeccccebe80329f1fdbee7f5874cb SRODATA dupok size=8
0x0000 01 00 00 00 00 00 00 00 ........Add some notes for personal understanding where I think it is not easy to understand , You may have a problem understanding , Welcome to your advice .
link
The purpose of linking process is to link the target file or static library file compiled from multiple files to generate the final executable file . With my superficial understanding of linker , The main work of the link is actually the process of address relocation .
such as , When A.go Accessed in file B.go A variable defined in the file Ver,
// A.go Ver = 42
Compile A The assembly code generated when the file is :
movl $0x2a, Ver
But at this time, the object code doesn't know ver The address of the variable , therefore A In the compiled object file Ver The address of will be set to 0. Only when the linker Links Ver The address of , It will be revised at this time A The variable address in the target file of . This process is called address relocation .
load
When we hit... On the command line ./hello And return to what happened in the middle ? The operating system helps us put our elf The file is loaded into memory and pc Register points to program entry address ,cpu Start a series of instructions for executing this program . In this process, first shell Will help us fork A subprocess , And call execv Load the executable file from disk to memory for execution . The whole calling process is :
sys_execve/sys_execveat | |--> do_execve |--> do_execveat_common |--> _do_execve_file |--> exec_binprm |--> search_binary_handler |--> load_elf_binary
After entering the program entry point , because go The boot process before the program is mostly assembly code , Let's read go In fact, I don't know where to start when I start the source code of , So you need to know where the program entry point is ( This article concerns go Source code to 1.17 The version shall prevail ):
gdb ./hello // gdb load elf file
then info file View program entry points ,
(gdb) info file
Symbols from "/data/git/workspace/src/laboratory/hello/hello".
Local exec file:
`/data/git/workspace/src/laboratory/hello/hello', file type elf64-x86-64.
Entry point: 0x45c200
0x0000000000401000 - 0x000000000047f7ac is .text
0x0000000000480000 - 0x00000000004b51c4 is .rodata
0x00000000004b5360 - 0x00000000004b5838 is .typelink
0x00000000004b5840 - 0x00000000004b5898 is .itablink
0x00000000004b5898 - 0x00000000004b5898 is .gosymtab
0x00000000004b58a0 - 0x000000000050ea38 is .gopclntab
0x000000000050f000 - 0x000000000050f020 is .go.buildinfo
0x000000000050f020 - 0x000000000051f5e0 is .noptrdata
0x000000000051f5e0 - 0x0000000000526df0 is .data
0x0000000000526e00 - 0x0000000000555d08 is .bss
0x0000000000555d20 - 0x000000000055b080 is .noptrbss
0x0000000000400f9c - 0x0000000000401000 is .note.go.buildidstay Entry point Add a breakpoint at , And then start the program :
(gdb) b *0x45c200 Breakpoint 1 at 0x45c200: file /usr/local/go/src/runtime/rt0_linux_amd64.s, line 8. (gdb) r Starting program: /data/git/workspace/src/laboratory/hello/./hello Breakpoint 1, _rt0_amd64_linux () at /usr/local/go/src/runtime/rt0_linux_amd64.s:8 8 JMP _rt0_amd64(SB)
You can see that the program is suspended /usr/local/go/src/runtime/rt0_linux_amd64.s:8 here , Here is the go The entry in the source code . If we want to see go Source words , From here, it seems most appropriate .
#include "textflag.h" TEXT _rt0_amd64_linux(SB),NOSPLIT,$-8 JMP _rt0_amd64(SB)
Actually go Program boot , Scheduling ,gc And so on runtime In the bag so everyone is reading go Source code time runtime Package is the study of go Operating mechanism , A very important part of running logic package.
// _rt0_amd64 is common startup code for most amd64 systems when using // internal linking. This is the entry point for the program from the // kernel for an ordinary -buildmode=exe program. The stack holds the // number of arguments and the C-style argv. TEXT _rt0_amd64(SB),NOSPLIT,$-8 MOVQ 0(SP), DI // argc LEAQ 8(SP), SI // argv JMP runtime·rt0_go(SB)
_rt0_amd64 It has been clearly stated in the function comments. In fact, the main program parameters are stored separately here DI and SI In two registers .
rt0_go This function does more , The most important lines :
MOVQ $runtime·mainPC(SB), AX // runtime.main Put the function address in AX register
PUSHQ AX
PUSHQ $0 // arg size
CALL runtime·newproc(SB) // Create a new one goroutine, The goroutine binding runtime.main, Put it in P Local queues for , Waiting for dispatch
POPQ AX
POPQ AX
// start this M
// start-up M, Start scheduling goroutine
CALL runtime·mstart(SB)This is the first in the program g and m The creation process of . be-all g All pass runtime.newproc Function creation ,
// Create a new g running fn.
// Put it on the queue of g's waiting to run.
// The compiler turns a go statement into a call to this.
func newproc(fn *funcval) {
gp := getg()
pc := getcallerpc()
systemstack(func() {
newg := newproc1(fn, gp, pc)
_p_ := getg().m.p.ptr()
runqput(_p_, newg, true)
if mainStarted {
wakep()
}
})
}newproc The logic of the function is not complicated , Use newproc1 Create a g Structure , And put g Put it in the waiting queue , If m0 If it has been started, it will wake up one p Get up and do this g.
go back to rt0_go This function , It turns on m0 And g0 To carry out runtime.mainPC function , The definition of this function is here :
// 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<ABIInternal>(SB) GLOBL runtime·mainPC(SB),RODATA,$8
Here it is transferred to runtime.main In the method , The definition of this method is a bit long, so the code is not listed here . It's mainly about N Multiple initialization operations , Include runtime Open background task ,gc,package Various... Introduced in package Of init Functions, etc ; The most important thing is to call main In bag main Method to enter the user-defined logic .
function
runtime It is a piece of content that can be taken out to do several special projects , I understand. runtime In fact, the language starts the background process of helping users do things in the background , Because I came from c++ The language has changed, so I want to compare the previous company in c++ How did the times do such things .
Embrace in the company golang Before , stay c++ Time , We often use spp The framework provides a set of user logic scheduling mechanism called micro threads , It depends on the active surrender of user level micro threads , Compared to the current go The coordination scheduling mechanism is simple and free , Users can control whether to give up at the right place cpu For other micro threads . at present rust Of tokio The scheduling mechanism of the co process implementation library is almost the same as that of the micro thread , Because neither language has runtime Runtime ,rust The most important feature of memory security is also built at compile time trait Upper , This design also led to rust Have higher requirements for the written code , There's only one way. Just walk over , No, I'll show you . This is what I understand runtime.
sign out
The last few lines of the bootstrap are like this :
fn := main_main // make an indirect call, as the linker doesn't know the address of the main package when laying down the runtime
fn()
if raceenabled {
racefini()
}
// Make racy client program work: if panicking on
// another goroutine at the same time as main returns,
// let the other goroutine finish printing the panic trace.
// Once it does, it will exit. See issues 3934 and 20018.
if atomic.Load(&runningPanicDefers) != 0 {
// Running deferred functions should not take long.
for c := 0; c < 1000; c++ {
if atomic.Load(&runningPanicDefers) == 0 {
break
}
Gosched()
}
}
if atomic.Load(&panicking) != 0 {
gopark(nil, nil, waitReasonPanicWait, traceEvGoStop, 1)
}
exit(0)main.main The program return After that, if there is still no implementation defer Of panic Information will be dispatched at most 1000 Execution times defer function .
summary
thus , One Go The program ended its vigorous and unrestrained life . The phases that appear in this article , Just take it out and make a special topic , Some places were not discussed in depth , Not fully unfolded ( Here is a metaphor , But I can't think of a suitable comparison object , Think about it and delete it ). I hope this article can help you sort out some concepts , Further, I hope to give you some inspiration , Further, I hope that others can extract some concepts from them to talk about . I love you so much to see here , thank !!!
Reference material
https://www.cnblogs.com/qcrao-2018/p/11124360.html
https://segmentfault.com/a/1190000040181868
https://segmentfault.com/a/1190000020996545
https://golang.design/under-the-hood/
Self cultivation of programmers -- link 、 Loading and storage
边栏推荐
- Gin framework: add Prometheus monitoring
- Visual AI, first!
- New Google brain research: how does reinforcement learning learn to observe with sound?
- The cost of on-site development of software talent outsourcing is higher than that of software project outsourcing. Why
- How is intelligent character recognition realized? Is the rate of intelligent character recognition high?
- VNC enters the password and goes around for a long time before entering the desktop. Use procmon to locate the reason
- Gartner released the magic quadrant of enterprise low code platform in 2021. Low code integrated platform becomes a trend!
- 2022-2028 global cancer biopsy instrument and kit industry research and trend analysis report
- Implementing an ORM framework against SQL injection with builder mode
- How to build a shopping website? What problems should be paid attention to in the construction of shopping websites?
猜你喜欢
![[51nod] 3395 n-bit gray code](/img/b5/2c072a11601de82cb92ade94672ecd.jpg)
[51nod] 3395 n-bit gray code
![[summary of interview questions] zj5](/img/d8/ece82f8b2479adb948ba706f6f5039.jpg)
[summary of interview questions] zj5

2022-2028 global aircraft audio control panel system industry research and trend analysis report
![[51nod] 2653 section XOR](/img/2d/cb4bf4e14939ce432cac6d35b6a41b.jpg)
[51nod] 2653 section XOR

2022-2028 global high tibial osteotomy plate industry research and trend analysis report

2022-2028 global portable two-way radio equipment industry research and trend analysis report

2022-2028 global cancer biopsy instrument and kit industry research and trend analysis report

2022-2028 global cell-based seafood industry research and trend analysis report

2022-2028 global marine clutch industry research and trend analysis report

2022-2028 global aircraft front wheel steering system industry research and trend analysis report
随机推荐
[51nod] 3395 n-bit gray code
How much does it cost to rent a cloud game server? Which cloud game server is more reliable?
Is the cloud game edge computing server highly required? What problems will occur during the use of cloud game edge computing server?
Vscode common shortcut keys, updating
Tencent dongzhiqiang: network security construction needs to change from product driven to service driven
Disaster recovery series (V) -- database disaster recovery construction
Tencent cloud CIF engineering efficiency summit ends perfectly
How to select a cloud game server? Which cloud game server recommends?
What is cloud desktop and how to connect to the server? What does the mainstream architecture of cloud desktop include?
Instructions for performance pressure test tool
Cloud call: one line of code is directly connected to wechat open interface capability
What is the role of the distributed configuration center? What are the advantages of a distributed configuration center?
Hook principle
What are the benefits of rack servers for cloud desktops? What can cloud desktop do?
PHP verify mailbox format
[51nod] 2106 an odd number times
Wkwebview audio and video media playback processing
UI automation based on Selenium
Which domestic cloud desktop server is good? What are the security guarantees for cloud desktop servers?
Disk and file system (Simplified)