Linux syscalls
What is a syscall?
System calls allow the kernel to carefully expose certain key pieces of functionality to user programs, such as accessing the file system, creating and destroying processes, communicating with other processes, and allocating more memory.
A system call is a C procedure call, the change of mode is achieved by special instructions hidden inside:
trapinstruction: enter kernel mode.return-from-trapinstruction: back to user program in user mode.
Commonly Used Syscalls
Early Unix systems exposed around twenty calls, but there are over 400+ system calls in the modern Linux kernel. The exact number varies by architecture (x86_64 has about 350).
Tier 1: The "Everyday" Calls (Highest Frequency)
These syscalls account for 90% of total system activity. Most are called thousands of times per second in nearly every application.
| Category | High-Frequency Syscalls | Description |
|---|---|---|
| File I/O | read, write, close, fstat, lseek |
The core of all data movement and file descriptor management. |
| Memory | mmap, munmap, mprotect, brk |
Handling process memory, loading libraries, and heap allocation. |
| Polling | poll, select, epoll_wait, futex |
Waiting for events on file descriptors or managing thread synchronization. |
| Signals | rt_sigprocmask, rt_sigaction, rt_sigreturn |
Managing software interrupts and signal handling. |
| Info | getpid, gettimeofday, clock_gettime |
Fetching basic process IDs and timestamps. |
Note: gettimeofday and clock_gettime are often optimized via vDSO, allowing them to run in user-space without a full kernel transition.
Tier 2: The "Setup & Control" Calls (Medium Frequency)
These are called frequently but usually during initialization, periodic checks, or process transitions.
| Category | Common Syscalls | Description |
|---|---|---|
| Process | execve, clone (fork), wait4, exit_group |
Starting, managing, and terminating processes/threads. |
| File Meta | openat, stat, access, getdents64 |
Opening files and listing directory contents. |
| Network | socket, connect, sendto, recvfrom, bind |
Standard BSD socket operations for internet/local communication. |
| Descriptor | fcntl, ioctl, dup2, pipe |
Fine-tuning file descriptors and inter-process communication. |
| Permissions | getuid, geteuid, getgid, capget |
Checking user identities and process capabilities. |
Tier 3: The "Utility" Calls (Lower Frequency)
These are used by specific tools (like ls, chmod, or top) or during specific system events.
| Category | Utility Syscalls | Description |
|---|---|---|
| FileSystem | mkdir, chdir, rename, unlink, chmod, mount |
Altering the file system structure or permissions. |
| Time | nanosleep, alarm, setitimer |
Putting processes to sleep for specific intervals. |
| System Info | uname, sysinfo, getrusage |
Retrieving kernel versions and resource usage stats. |
| Identity | setuid, setgid, setpriority, setsid |
Changing process priority or user ownership. |
Tier 4: The "Niche" Calls (Rare Frequency)
These are highly specialized and rarely seen in standard application code.
| Category | Specialized Syscalls | Description |
|---|---|---|
| Security | ptrace, seccomp, bpf, keyctl |
Debugging (strace/gdb), sandboxing, and eBPF tracing. |
| Modules | init_module, delete_module, finit_module |
Loading and unloading Linux kernel drivers. |
| System | reboot, swapon, kexec_load, quotactl |
Low-level system maintenance and power management. |
| IPC | shmget, semop, msgget |
Legacy System V Inter-Process Communication. |
Summary: The Overall "Top 10" Ranking
While rankings fluctuate, research (e.g., SysPop) and profiling tools consistently show this top-heavy distribution for general-purpose computing:
read/write: (The absolute kings of syscalls)futex: (Constant thread synchronization in modern apps)mmap/munmap: (Memory allocation happens constantly)fstat: (Apps check file sizes/types before doing anything)poll/epoll_wait: (The heartbeat of all networked apps/web servers)rt_sigprocmask: (Standard overhead for signal safety)close: (Every open must eventually close)getpid: (Extremely cheap, used for logging and identification)ioctl: (The "junk drawer" for device-specific commands)lseek: (Moving the cursor inside files)
How to see this on your own machine
You can see exactly which syscalls your system is using right now by running this command:
# Traces the entire system for 10 seconds and summarizes frequency
sudo perf stat -e 'syscalls:sys_enter_*' -a sleep 10
Or for a specific program:
# Count syscalls for the 'ls' command
strace -c ls
seccomp
Secure computing mode (seccomp): Any system calls not on the list are disallowed.
It can be used to sandbox the privileges of a process, restricting the calls it is able to make from userspace into the kernel.
Are all Linux syscalls aligned with POSIX?
No. Many Linux system calls are Linux-specific extensions or variations that are not defined by the POSIX standard.
Linux implements POSIX system calls to ensure compatibility with Unix-like operating systems, but kernel developers frequently introduce non-POSIX syscalls to optimize performance, expose Linux-specific hardware/kernel features, or handle specialized system administration tasks.
Non-POSIX Compliant Linux Syscalls vs. POSIX Alternatives
| Non-POSIX Linux Syscall | POSIX Equivalent / Standard Alternative | Primary Purpose / Advantage over POSIX |
|---|---|---|
epoll_create / epoll_wait |
select(), poll() |
Scalable O(1) event notifications for high-concurrency network servers (POSIX select/poll are O(N)). |
io_uring_setup / enter |
aio_read(), aio_write() |
High-performance, zero-copy, asynchronous I/O submission ring buffers. |
clone() |
fork(), pthread_create() |
Low-level execution context creation that allows precise sharing of memory, namespace, file descriptors, and IPC state (used to build Linux containers). |
unshare() / setns() |
(No POSIX equivalent) | Manipulates Linux process namespaces (PID, mount, network, user namespaces) for container sandboxing (Docker, LXC). |
pivot_root() |
chroot() |
Changes the root mount point of a mount namespace securely for containers. |
eventfd() / signalfd() |
Pipes / sigaction() |
Exposes OS event notifications and signals directly as readable file descriptors for integration into event loops. |
memfd_create() |
shm_open() |
Creates anonymous, memory-backed file descriptors directly in RAM without mounting a tmpfs file system. |
copy_file_range() / sendfile() |
read() / write() loops |
Zero-copy kernel-space file copying without transferring data through user space buffers. |
bpf() |
(No POSIX equivalent) | Loads and executes eBPF bytecode in the kernel for network tracing, security filtering, and observability. |
getrandom() |
/dev/urandom / getentropy() |
Obtains high-entropy random bytes directly from the kernel RNG without opening file descriptors. |
Why Linux Has Non-POSIX Syscalls
- Performance & Scalability: Standard POSIX APIs (like
select()) were designed decades ago and do not scale efficiently to modern multi-threaded applications handling hundreds of thousands of concurrent connections or high-throughput I/O. - Containerization & Isolation: Features like Linux Namespaces, Cgroups, and
pivot_root()are fundamental to how Linux isolates processes, but they fall outside standard POSIX process boundaries. - Hardware & Kernel Features: Linux innovations such as eBPF,
io_uring, landlock security, andseccomprequire custom syscall entrypoints to interact directly with internal kernel subsystems.
Standard Compliance in Practice
To write portable code across Unix systems (Linux, macOS, FreeBSD), developers restrict system call usage to POSIX APIs (often using the standard C library, glibc).
When targeting maximum performance on Linux, software often uses feature macros (#ifdef __linux__) or wrapper libraries (like libuv) to automatically prefer Linux-native syscalls (epoll, io_uring, getrandom) while falling back to POSIX equivalents on non-Linux platforms.
Learning Roadmap
To build a well-rounded foundation in POSIX systems programming, you should focus on the core system calls that manage process lifecycles, memory, file descriptors, multiplexing I/O, and inter-process communication (IPC).
Process Lifecycle & Execution
These calls form the backbone of modern Unix process management.
-
fork()- What it does: Clones the calling process to create a child process. The child inherits a copy of the parent’s address space (via Copy-On-Write), file descriptor table, environment, and execution context.
- Key detail: It returns twice:
0in the child process, and the child's Process ID (PID) in the parent process.
-
execve()(and theexec(3)wrapper family likeexecvp,execl)- What it does: Replaces the current process image with a new program binary.
- Key detail: It does not return on success because the original code, stack, and heap are completely overwritten by the new program. File descriptors without
O_CLOEXECremain open across anexecve.
-
waitpid()(andwait())- What it does: Suspends the parent process until a specific child process changes state (terminates, stops, or resumes).
- Key detail: Prevents "zombie" processes by reaping the child's exit status code (
WEXITSTATUS) from the kernel table.
-
exit()/_exit()- What it does: Terminates the calling process and flushes/releases allocated resources.
_exit()is the raw system call (bypassing C library cleanup handlers likeatexit()).
- What it does: Terminates the calling process and flushes/releases allocated resources.
File Descriptor & I/O Manipulation
These calls operate directly on open file descriptors (fd), regardless of whether they represent regular files, sockets, pipes, or devices.
-
read()/write()- What it does: Reads bytes from or writes bytes to a file descriptor starting at the current offset.
- Key detail: Unbuffered I/O. They do not guarantee reading or writing the full requested byte count in a single call (handling "short counts" is standard practice).
-
dup()/dup2()/dup3()- What it does: Duplicates an open file descriptor so that two descriptors share the exact same kernel Open File Description (sharing file position offset and flags).
- Key detail:
dup2(oldfd, newfd)is the fundamental mechanism used by shells to perform I/O redirection (e.g., redirecting standard output1to a log file).
-
lseek()- What it does: Repositions the file offset of an open descriptor (
SEEK_SET,SEEK_CUR,SEEK_END). - Key detail: Works on seeking files; fails with
ESPIPEwhen called on pipes or sockets.
- What it does: Repositions the file offset of an open descriptor (
-
fcntl()- What it does: Performs miscellaneous control operations on open file descriptors (e.g., querying or modifying flags like
O_NONBLOCKviaF_GETFL/F_SETFL, applying advisory locks, or duplicating descriptors withF_DUPFD_CLOEXEC).
- What it does: Performs miscellaneous control operations on open file descriptors (e.g., querying or modifying flags like
-
close()- What it does: Decrements the reference count of the underlying open file description. When the reference count reaches zero, the underlying resource (file, socket, pipe) is freed by the kernel.
I/O Multiplexing & Event Systems
When dealing with multiple non-blocking sockets or pipes simultaneously, you need system calls that can wait on multiple file descriptors at once.
-
poll()(POSIX standard)- What it does: Blocks until one or more file descriptors in an array of
struct pollfdbecome ready for I/O (reading, writing, or errors). - Key detail: Replaced the older POSIX
select()API by removing file descriptor numerical limits (FD_SETSIZE = 1024).
- What it does: Blocks until one or more file descriptors in an array of
-
Linux Event Notification Extensions (
epoll_*):- While
poll()is POSIX-standard, modern high-performance Linux servers useepoll_create1(),epoll_ctl(), andepoll_wait(). Unlikepoll()'s O(N) linear scanning complexity,epollprovides O(1) scaling when monitoring tens of thousands of idle descriptors.
- While
Memory Management & Mapping
-
mmap()/munmap()- What it does: Maps files or anonymous kernel memory pages directly into the process's virtual address space.
- Key detail: Used for memory-mapped file I/O (avoiding redundant kernel-to-userspace buffer copying) and as the foundation for modern heap allocators (e.g.,
mallocallocates large blocks viammapusingMAP_ANONYMOUS).
-
mprotect()- What it does: Changes protection permissions (
PROT_READ,PROT_WRITE,PROT_EXEC) on regions of virtual memory pages. Essential for JIT compilers and memory protection features.
- What it does: Changes protection permissions (
Inter-Process Communication (IPC)
-
pipe()/pipe2()- What it does: Creates a unidirectional data channel (returning an array of two descriptors:
fd[0]for reading,fd[1]for writing). - Key detail: The primary mechanism behind shell pipelines (e.g.,
ls | grep).
- What it does: Creates a unidirectional data channel (returning an array of two descriptors:
-
socket()/bind()/listen()/accept()/connect()- What it does: The POSIX Sockets API used for networking (TCP/UDP) and Unix Domain Sockets (
AF_UNIX) for fast local IPC.
- What it does: The POSIX Sockets API used for networking (TCP/UDP) and Unix Domain Sockets (