go run main.go a go program starts. However, how does the operating system execute the go code behind this? What does go do to run the user's main function?
One compilation
- go build main.go
The go code we write is compiled into an executable file to be directly executed on the machine. It is on the linux platform ELF Format executable file, linux can directly execute this file.
- Compiler: generate. s assembly code from go code. plan9 assembly is used in go
- Assembly: convert assembly code into machine code, i.e. object program. o file
- Linker: merge and link multiple. o files to get the final executable
graph LR 0(Write code)--go program--> 1(compiler)--Assembly code --> 2(Assembler)--.o Target program-->3(Linker)--Executable file-->4(end)
II. Operating system loading
- ./main
After the executable file is generated through the above steps, the binary file will go through the following stages when it is loaded and run by the operating system:
- Read the executable program into memory from disk;
- Create process and main thread;
- Allocate stack space for the main thread;
- Copy the parameters entered by the user on the command line to the stack of the main thread;
- Put the main thread into the running queue of the operating system and wait for the scheduled execution to run;
START_ Thread (elf_ex, regs, elf_entry, bprm - > P) the starting thread passed elf_ Entry parameter, which is the entry address of the program.
This Elf_ The entry is written in the header of the elf executable
$ readelf -h main ELF Header: Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class: ELF64 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: EXEC (Executable file) Machine: Advanced Micro Devices X86-64 Version: 0x1 Entry point address: 0x45d430 Start of program headers: 64 (bytes into file) Start of section headers: 456 (bytes into file) Flags: 0x0 Size of this header: 64 (bytes) Size of program headers: 56 (bytes) Number of program headers: 7 Size of section headers: 64 (bytes) Number of section headers: 25 Section header string table index: 3
And by decompiling the executable file, you can see that the address corresponds to_ rt0_amd64_linux.
$ objdump ./main -D > tmp $ grep tmp '45d430' 000000000045d430 <_rt0_amd64_linux>: 45d430: e9 2b c4 ff ff jmpq 459860 <_rt0_amd64>
From then on, we entered the start-up process of Go program
Go program start
Go program Start position , store the entry parameters on the stack in the register, and then jump to rt0_go startup function
TEXT _rt0_amd64(SB),NOSPLIT,$-8 MOVQ 0(SP), DI // argc LEAQ 8(SP), SI // argv JMP runtime·rt0_go(SB)
rt0_ The go code is relatively long and can be divided into two parts. The first part is system parameter acquisition and runtime check. The second part is the core of Go program startup. Only the second part is introduced in detail here. The overall startup process is as follows
go runtime core:
- schedinit: initialize various runtime components, including the initialization of our scheduler, memory allocator and collector
- newproc: it is responsible for creating execution units that can be scheduled at runtime according to the main goroutine (i.e. main) entry address. Here, main is not the user's main function, but runtime.main
- mstart: start the scheduling cycle of the scheduler, and the entry method in the execution queue is G of runtime.main
TEXT runtime·rt0_go(SB),NOSPLIT,$0 (...) // Scheduler initialization CALL runtime·schedinit(SB) // Create a new goroutine to start the program MOVQ $runtime·mainPC(SB), AX PUSHQ AX PUSHQ $0 // Parameter size CALL runtime·newproc(SB) POPQ AX POPQ AX // Start this M, mstart should never return CALL runtime·mstart(SB) (...) RET
shedinit includes the initialization of all core components
// src/runtime/proc.go func schedinit() { _g_ := getg() (...) // Stack, memory allocator, scheduler related initialization sched.maxmcount = 10000 // Limit the maximum number of system threads stackinit() // Initialize execution stack mallocinit() // Initialize memory allocator mcommoninit(_g_.m) // Initializes the current system thread (...) gcinit() // Garbage collector initialization (...) // Create P // The number of P is determined by the number of CPU cores and GOMAXPROCS environment variable procs := ncpu if n, ok := atoi32(gogetenv("GOMAXPROCS")); ok && n > 0 { procs = n } procresize(procs) (...) }
Execute runtime.main, mainly
- Start the sysmon thread for system background monitoring
- Execute init in runtime package
- Start gc
- The user package depends on the execution of init
- Execute the user main.mian method
// The main goroutine. func main() { g := getg() ... // Maximum limit of execution stack: 1GB (64 bit system) or 250MB (32-bit system) if sys.PtrSize == 8 { maxstacksize = 1000000000 } else { maxstacksize = 250000000 } ... // Start system background monitoring (regular garbage collection, preemption scheduling, etc.) systemstack(func() { newm(sysmon, nil) }) ... // Let goroute monopolize the current thread, // For the usage of runtime.lockOSThread, see http://xiaorui.cc/archives/5320 lockOSThread() ... // The init function inside the runtime package executes runtime_init() // must be before defer // Defer unlock so that runtime.Goexit during init does the unlock too. needUnlock := true defer func() { if needUnlock { unlockOSThread() } }() // Start GC gcenable() ... // init execution of user package main_init() ... needUnlock = false unlockOSThread() ... // Execute the main function of the user main_main() ... // sign out exit(0) for { var x *int32 *x = 0 } }
summary
When starting a go program, first load the operating system and find the Go program startup entry through the address recorded in the Entry point address in the executable file:_ rt0_ amd64 -> rt0_go. rt0_ In go, the runtime of Go program is initialized first, including scheduler, stack, heap memory space initialization and garbage collector initialization. Finally, runtime.main is executed through newproc and mstart scheduling to complete a series of initialization processes, and then the user's main function is executed.