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.
| Architecture | Entry instruction | Number register on Linux |
|---|---|---|
| x86-64 | syscall | rax |
| 32-bit x86 (legacy) | int 0x80 or sysenter | eax |
| ARM64 (AArch64) | svc #0 | x8 |
| 32-bit ARM | svc #0 | r7 |
| RISC-V | ecall | a7 |
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.
| Category | Typical calls | Kernel subsystem |
|---|---|---|
| Process control | fork, execve, exit, wait4 | Process scheduler and loader |
| File and device I/O | open, read, write, close, ioctl | Virtual file system and drivers |
| Memory management | mmap, munmap, brk, mprotect | Virtual memory manager |
| Networking | socket, connect, sendmsg, recvmsg | Network stack |
| Inter-process communication | pipe, futex, shmget, eventfd | IPC and synchronization |
| Information and time | getpid, clock_gettime, uname | Various |
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.
| Technique | How it helps | Typical use |
|---|---|---|
| Buffered I/O | Many small writes become one call | Standard library streams |
| Vectored I/O | Several buffers in one call | Network servers |
| Memory mapping | File pages accessed without read calls | Databases, large files |
| io_uring batching | Many operations per kernel entry | High-performance storage and networking |
| vDSO functions | No kernel transition needed | Time queries |
System calls compared with related mechanisms
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.
| Mechanism | Triggered by | Purpose |
|---|---|---|
| System call | Deliberate instruction in a program | Request a kernel service |
| Hardware interrupt | External device signal | Report device events |
| Exception or fault | Error during an instruction | Handle page faults, division by zero |
| Library call | Normal function call | Stays in user space unless it wraps a system call |
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
writemay 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.