I might tell students step by step:
machine code is understood and executed by a machine.
-> the intel instruction to increment a register is decoded and executed by the CPU's hardware.
virtual code is understood and executed by a program that pretends to be some virtual CPU.
-> a Java VM might run on an intel CPU and understand and execute Java virtual machine instructions.
-> a Java VM might run on an intel VM that actually runs on an ARM CPU...
-> ...like a Russian matryoshka doll, there can be any number of outer VMs before the innermost real CPU.
operating systems provide protection, resource allocation, and services.
most languages offer programs at least some operating system like services via a runtime service layer
-> in C, this was initially "crt0" the thin c runtime
-> in Go, the service layer is richer, offering thread management and goroutine multiplexing, garbage collection, and more.
the Go runtime is written in Go mostly and uses C or assembler to connect to the host operating system.
the Go runtime is linked with the developer's Go code, and the two of them are constitute the output of go build or go install