Dominik Honnef

Go syscalls

Last modified:
by

Syscalls in Go need to work with its M:N scheduler. When invoking a syscall, Go first does nothing special: it runs the syscall on the M that’s running the G, blocking the G and the M, making the M unavailable for running other Ps. If the syscall returns quickly, we’re happy; we haven’t had to involve the scheduler, and the goroutine can resume where it left off right away.

At the same time, the sysmon process looks for Ms that are currently blocked in syscalls, and takes their P away. If necessary, it also spawns a new M, so that there are enough non-blocked Ms to run GOMAXPROCS Ps. This way, syscalls that take a long time don’t reduce the amount of Gs that can run at the same time.

Sysmon can at most run every 20 μs, but may run as slowly as every 10 ms if it notices that there isn’t much work to do. This means that what’s considered a blocking syscall is variable and depends on how often sysmon runs, i.e. the load of the system.

In testing, even in busy programs (with GOMAXPROCS restricted to low values), sysmon sometimes decides to sleep for 5 ms. A loop calling syscall.Nanosleep, sleeping 10 ms each time, will take anywhere from an insignificant amount of time to 5 ms to detect the syscall.