The Need for Speed
System calls are the mechanism by which user-space programs ask the kernel to do privileged work — read a file, allocate memory, get the current time. Each system call requires a context switch: the CPU saves all user-space registers, switches from user mode (ring 3) to kernel mode (ring 0), executes the kernel code, then switches back. This round trip costs hundreds to thousands of nanoseconds and flushes CPU caches and branch predictors.
For most operations, this cost is acceptable. But some calls are made at extreme frequency. A high-frequency trading system might call clock_gettime() millions of times per second to timestamp order events. A web server might call gettimeofday() on every request. At that scale, syscall overhead becomes a measurable bottleneck.
The Solution: vDSO (Virtual Dynamic Shared Object)
The kernel solves this by mapping a small region of its own memory directly into the virtual address space of every user process. This region — the vDSO — contains:
- A small amount of kernel-maintained data (like the current time, updated by the kernel on each timer tick).
- Fast user-space code to read that data safely without a syscall.
When your program calls clock_gettime(), the C library checks whether the vDSO is available. If it is, it calls the vDSO's implementation, which reads the time from the shared memory page — entirely in user space, no context switch, no mode change. The speedup is 10–20x compared to a real syscall.
The vDSO gives itself away as a "ghost" library: dependency-listing tools report it as linux-vdso.so.1 with no resolved path on disk, because there is no file to resolve — the kernel synthesizes it directly into each process's memory map at load time, distinct from every other shared object the loader has to go find.
The vDSO is read-only and execute-only from the process's perspective — the kernel updates the data, the process only reads it. Functions commonly accelerated by vDSO include clock_gettime, gettimeofday, time, and getcpu.
Key Point: The vDSO is also a security boundary. Because it is mapped by the kernel, it participates in ASLR (Address Space Layout Randomization) — its address changes on every process launch, making it harder to exploit. The address shown in /proc/self/maps is randomized per-process.