System call

On this page 7

A system call is the controlled interface through which a program running in user space requests a service from the kernel, such as reading a file, allocating memory, creating a process or sending data over a network. In operating systems, it is the only legitimate way for ordinary code to cross the boundary into kernel space, where hardware access and protected resources live.

System calls exist because modern processors separate privilege levels. Application code runs with restricted rights, so it cannot touch devices or other processes directly; instead it places a request in registers and executes a special instruction that transfers control to a fixed kernel entry point, which validates the request and returns a result.

How a system call crosses into the kernel

A system call follows a fixed calling convention defined by the operating system and processor architecture. The program, usually through a C library wrapper such as read() in glibc, loads a system call number into a designated register, places up to about six arguments in other registers and executes a trap instruction. The processor switches to kernel mode and jumps to an entry point configured by the kernel at boot.

The kernel saves user registers, looks up the handler in the system call table, checks every argument, copies data across the boundary with dedicated routines and performs the work. It then places a return value or error code in a register and returns to user mode. The table below shows the trap mechanism on common architectures.

ArchitectureEntry instructionNumber register on Linux
x86-64syscallrax
32-bit x86 (legacy)int 0x80 or sysentereax
ARM64 (AArch64)svc #0x8
32-bit ARMsvc #0r7
RISC-Vecalla7
System call entry instructions by architecture

On Linux, a failed call returns a negative error value, which the C library converts into -1 and sets errno to codes such as ENOENT or EACCES. Windows uses a different convention: documented Win32 functions call into ntdll.dll, which issues the actual system call to the NT kernel.

Main categories of system calls

A general-purpose kernel exposes a few hundred system calls; Linux on x86-64 has more than 350, and the exact set grows slowly over time. They fall into a small number of functional groups, each tied to a core kernel subsystem.

CategoryTypical callsKernel subsystem
Process controlfork, execve, exit, wait4Process scheduler and loader
File and device I/Oopen, read, write, close, ioctlVirtual file system and drivers
Memory managementmmap, munmap, brk, mprotectVirtual memory manager
Networkingsocket, connect, sendmsg, recvmsgNetwork stack
Inter-process communicationpipe, futex, shmget, eventfdIPC and synchronization
Information and timegetpid, clock_gettime, unameVarious
System call categories with typical POSIX examples

Some frequently used calls, such as clock_gettime, are accelerated through the vDSO, a small kernel-provided library mapped into each process, so they complete without entering the kernel at all.

Cost and performance of system calls

A system call is far more expensive than an ordinary function call. A trivial call on a modern x86-64 processor takes on the order of one hundred nanoseconds to a few hundred nanoseconds, depending on the processor, kernel version and security mitigations, compared with a few nanoseconds for a normal function call. Mitigations for speculative execution attacks, such as kernel page table isolation, increased this cost noticeably on affected hardware.

Because of this overhead, efficient software reduces the number of crossings rather than the work per call. Common techniques include reading large buffers instead of single bytes, using vectored calls such as readv, and submitting batches of operations through io_uring on Linux, which shares ring buffers between user space and kernel.

TechniqueHow it helpsTypical use
Buffered I/OMany small writes become one callStandard library streams
Vectored I/OSeveral buffers in one callNetwork servers
Memory mappingFile pages accessed without read callsDatabases, large files
io_uring batchingMany operations per kernel entryHigh-performance storage and networking
vDSO functionsNo kernel transition neededTime queries
Ways to reduce system call overhead

A system call is one of several ways control can enter the kernel. Distinguishing them matters when profiling, debugging or reading crash reports, because each has a different trigger and purpose.

MechanismTriggered byPurpose
System callDeliberate instruction in a programRequest a kernel service
Hardware interruptExternal device signalReport device events
Exception or faultError during an instructionHandle page faults, division by zero
Library callNormal function callStays in user space unless it wraps a system call
Kernel entry mechanisms

A page fault is often confused with a system call because both enter the kernel, but a page fault is involuntary: the program did not ask for it, and the kernel resolves it transparently. A context switch may follow a system call that blocks, for example when read waits for disk data and the scheduler runs another process.

Security and common pitfalls

The system call interface is the primary attack surface of a kernel, because it is where untrusted input reaches privileged code. Kernels therefore validate every pointer and length, and sandboxing systems such as seccomp on Linux restrict which calls a process may make. Container runtimes and browsers use such filters to block rarely needed calls.

  • Ignoring return values hides failures; a write may complete only partially and must be repeated.
  • Not handling EINTR causes spurious errors when a signal interrupts a blocking call.
  • Assuming portability of call numbers fails, because numbers differ between architectures and operating systems.
  • Calling the kernel in tight loops wastes time that buffering would save.

In plain words

A program on your computer is like a customer at a bank. It cannot walk into the vault and take money itself. Instead it goes to the counter and fills out a request. A system call is that request at the counter.

The operating system checks whether the request is allowed, does the job, such as opening a file or sending data, and hands back the result. This keeps programs from damaging each other or the computer, at the price of a short wait at the counter each time.

A real-world example

A team running a log-processing service notices that it uses far more processor time than expected. Tracing the process with a system call tracer such as strace shows millions of write calls per minute, each writing a single log line of about 100 bytes.

The developers switch the logger to buffered output that flushes every 64 KB or every second. The number of write calls drops by more than two orders of magnitude, kernel time falls sharply, and the same server now handles noticeably more requests without any change to the business logic.