For the general case, not Go specific, there's a scheduler that tracks what processes (in the CSP sense, e.g. goroutine) are waiting on what channels. Whenever a channel becomes ready, e.g. something is available to read from it, one of the processes waiting to read from it is non-deterministically (that's important) chosen and control (the CPU's program counter, still in user-space) jumps to where it returns from the `read'. It then has the CPU all to itself until it gives control back to the scheduler by needing another particular channel-state. I'm omitting sleeping, etc., for simplicity. The switching of control is all in user-space so quite lightweight.
For a background in the CSP languages have a browse of Russ Cox's http://swtch.com/~rsc/thread/ The reference [4] to Squeak is broken, it's now at http://research.microsoft.com/pubs/67508/squeak.pdf and well worth a read.
For the general case, not Go specific, there's a scheduler that tracks what processes (in the CSP sense, e.g. goroutine) are waiting on what channels. Whenever a channel becomes ready, e.g. something is available to read from it, one of the processes waiting to read from it is non-deterministically (that's important) chosen and control (the CPU's program counter, still in user-space) jumps to where it returns from the `read'. It then has the CPU all to itself until it gives control back to the scheduler by needing another particular channel-state. I'm omitting sleeping, etc., for simplicity. The switching of control is all in user-space so quite lightweight.