One-line Summary
This paper analyzes (duh) the benefits and costs of writing a POSIX kernel in a high-level language, Go.
Paper Structure Outline
Avoiding heap exhaustion
Static analysis to find s
Evaluation
Biscuit's use of HLL features
Handling kernel heap exhaustion
Discussion and future work
Background & Motivation
The main reason for using low level languages like C to implement a kernel is that C supports low-level techniques that can help performance (pointer arithmetic, explicit memory allocation, etc.).
High level languages (HLL), on the other hand, have some potential advantages compared to C:
Automatic memory management: reduces programmer effort and use-after-free bugs
Type-safety: detects bugs
Runtime typing and method dispatch: helps with abstraction
Language support for threads and synchronization eases concurrent programming
With the idea of exploring the possibility of using a HLL to implement a monolithic POSIX-style kernel in mind, the authors present Biscuit, a kernel written in Go, which has good performance.
Design and Implementation
The Biscuit kernel is written using 27583 lines of Go, 1546 lines of assembly, and no C. Biscuit provides 58 syscalls and it has enough POSIX compatibility to run some existing server programs (NGINX, Redic, etc.).
Garbage collection
Biscuit uses Go's collector, which suspends ordinary execution on all cores ("stop-the-world" pause of ~10μs) twice during a collection. This hurts tail latency the most, and it's especially bad for machines that are dependent on pauses (e.g., datacenters).
Avoiding heap exhaustion
Heap exhaustion refers to live kernel data completely filling the RAM allocated for the heap. Waiting for memory in allocator might lead to deadlocks; Checking and handling allocation failure (like C kernels) is difficult to get right, and Go does not expose failed allocations. Biscuit uses reservations as a solution:
A syscall does not start until either it can reserve enough heap memory or a killer thread frees up some memory. To execute a syscall,
Some missing features of Biscuit:
Scheduling priority (relies on Go runtime scheduler)
Does not handle large multicore machines or NUMA
Does not swap or page out to disk
Does not implement reverse page mappings (revoke shared pages)
Security features (Users, access control lists, address space randomization)
58 out of 300-400 syscalls
The experiments used three kernel-intensive applications: CMailbench, NGINX, and Redis.
The use of Go improved the outcome of 40 out of 65 Linux execute-code bugs in the CVE database: 8 won't happen at all and 32 will cause runtime error + panic. The performance of Biscuit is in the same league as Linux Measurement of HLL tax. Prologue cycles are the most expensive. The cost of the GC cycles increases with the size of live kernel heap. The performance of code paths are compared using two benchmarks: ping-pong and page-faults. Go has 5% - 15% performance tax. CVE: Common Vulnerabilities and Exposures
Code path: the set of specific instructions that are actually executed during a single run of a program or program fragment.