gVisor - Scheduler
Read more: Linux - Scheduler.
gVisor implements its own internal scheduler to manage the application processes running inside its sandbox.
However, it is a two-tier scheduling system. To understand how gVisor handles scheduling, you have to look at the relationship between the sandboxed application, the gVisor "Sentry," and the host Linux kernel.
The Two-Tier Architecture
Because gVisor is a "user-space kernel," it sits between the application and the actual host operating system.
- Tier 1: The Host Scheduler (Linux): The host Linux kernel sees gVisor (the Sentry process) as a standard multi-threaded application. The host scheduler (CFS/EEVDF) decides when the Sentry's threads get to run on the physical CPUs.
- Tier 2: The gVisor Scheduler (Internal): Inside the sandbox, the Sentry must decide which application thread (the "Guest" threads) should run on the available "Host" threads.
How gVisor’s Internal Scheduler Works
gVisor is written in Go, and its internal scheduling logic is deeply integrated with the Go Runtime Scheduler.
- Tasks as Goroutines: In gVisor, every thread in the sandboxed application is mapped to a Go goroutine.
- The Go Scheduler Influence: gVisor relies heavily on the Go scheduler ( model) to multiplex these application "tasks" onto host threads. When an application thread performs a system call or hits a blocking point, gVisor uses Go’s channel and network poller logic to park that task and switch to another one.
- User-Space Context Switching: By managing tasks as goroutines, gVisor can perform "context switches" between application threads entirely in user space. This is often faster than performing a full context switch through the host Linux kernel.
Why does gVisor need its own scheduler?
If the host Linux kernel already has a great scheduler, why does gVisor bother?
- Isolation (The Primary Goal): gVisor’s purpose is to provide a security boundary. If every application thread were mapped 1 to a host thread, a malicious application might be able to exploit side-channel attacks or certain kernel vulnerabilities related to thread management.
- Efficiency in Ptrace/KVM Modes: gVisor uses "Platforms" (like Ptrace or KVM) to intercept instructions. Context switching at the host level is very expensive in these modes. By scheduling tasks internally as goroutines, gVisor reduces the number of times it has to "exit" to the host kernel.
- Linux Emulation: gVisor must emulate Linux signal delivery, thread groups, and futexes (fast user-space mutexes). To make these work exactly like Linux, gVisor needs to control the lifecycle and state of the threads itself.
The Performance Trade-off
This internal scheduling is one of the reasons gVisor has higher overhead than standard containers (like Docker/runC):
- The "Double Scheduling" Penalty: Sometimes the gVisor scheduler wants to run a task, but the host Linux scheduler has de-scheduled the Sentry thread that was supposed to run it. This leads to latency.
- System Call Overhead: Every time a sandboxed application makes a system call, it must be "caught" by the gVisor scheduler, processed, and (if necessary) passed to the host kernel.
Summary
gVisor does implement its own scheduler by mapping application threads to Go goroutines. It manages the state, blocking, and execution of these threads within the Sentry, while the host Linux kernel remains responsible for scheduling the Sentry process itself on the physical hardware.