仅仅只是会用是不行的，那样只能是一个 “调包侠”，连开 “面试 JVM，上岗 JSP” 这种玩笑的资格都没有。
…….represents an OS thread. It's the thread of execution managed by the OS and works pretty much like your standard POSIX thread. In the runtime code, it's called M for machine.
…….represents a context for scheduling. You can look at it as a localized version of the scheduler which runs Go code on a single thread. It's the important part that lets us go from a N:1 scheduler to a M:N scheduler. In the runtime code, it's called P for processor.
…….represents a goroutine. It includes the stack, the instruction pointer and other information important for scheduling goroutines, like any channel it might be blocked on. In the runtime code, it's called a G.
- M 是 OS Thread。由操作系统管理（分配 CPU，内存，以及调度）
- G 就是一个一个的
- P 是每一个 M 所持有的 Context，用于调度
- 每一个 M 持有一个 P，并保持一个
When a Go program executes what looks like a blocking I/O operation, the Go runtime actually suspends the goroutine and resumes it when an event indicates that some result is available. In the meantime other goroutines have been scheduled for execution. We therefore have the scalability benefits of asynchronous programming with a synchronous programming model.
啧，还记得当初刚在操作系统中学了这玩意儿之后，马上就是 Meltdown and Spectre 的发现。估计乱序处理器可真是让 NSA 乐坏了。
那么，如果按照第一小节中的模型，如何进行 syscall？还有为什么我们需要 P？3
You might wonder now, why have contexts at all? Can't we just put the runqueues on the threads and get rid of contexts? Not really. The reason we have contexts is so that we can hand them off to other threads if the running thread needs to block for some reason.
An example of when we need to block, is when we call into a syscall. Since a thread cannot both be executing code and be blocked on a syscall, we need to hand off the context so it can keep scheduling.