Linux syscalls
A user-space program does nothing directly: when it reads a file, allocates memory, or sends a network packet, it asks the kernel to do it. This request is a syscall (system call): the program executes a special instruction (int 0x80 or sysenter on x86), the kernel takes over, executes the request and returns control to the program. It is the boundary between user space (ring 3) and kernel space (ring 0).
The base syscalls
Linux exposes about 300 syscalls. The most frequent:
| syscall | what it does |
|---|---|
read | read from an fd |
write | write to an fd |
open | open a file |
close | close an fd |
stat | a file’s metadata |
mmap | map memory |
fork/clone | create a process |
execve | load a binary |
socket | create a socket |
connect | connect to a remote socket |
bind | attach a socket to a port |
listen | listen on a socket |
accept | accept a connection |
Each syscall has a number (the offset in the sys_call_table) and arguments passed in registers (eax = number, ebx/ecx/edx/esi/edi = arguments on x86 32-bit).
strace: observing syscalls
strace intercepts the syscalls of a process (via ptrace) and displays them:
strace ls
execve("/bin/ls", ["ls"], [/* 24 vars */]) = 0
brk(0) = 0x805c000
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7f7a000
open("/etc/ld.so.preload", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=62350, ...}) = 0
mmap2(NULL, 62350, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7f6b000
close(3) = 0
...
Each line is a syscall: the name, the arguments and the return value (= 0 means success, -1 means error with errno). The ENOENT after open("/etc/ld.so.preload") means the file does not exist, which is normal (ld.so.preload is optional).
strace on an existing process
strace -p 12345
Attaches strace to a running process (via ptrace, so requires root or being the owner).
The options that change everything
strace -f ls # follow child processes too
strace -e trace=open,read ls # only open and read
strace -e trace=network wget example.com # only network syscalls
strace -c ls # count syscalls instead of displaying them
strace -t ls # display timestamps
strace -c is the most instructive:
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
39.87 0.000257 257 1 execve
28.87 0.000186 3 62 13 open
15.78 0.000102 2 55 2 read
...
62 calls to open to list a directory: that is the loading of shared libraries (each library is searched in each directory of the LD_LIBRARY_PATH). The errors shows 13 opens that failed: the directories where the file does not exist. This is the ld.so search mechanism.
What strace reveals
strace reveals what the program actually does, not what it claims to do. A program that claims to read a config file reveals the directories where it searches, the search order and the errno at each attempt. A program that “does not work” often reveals an open on a non-existent file with an ENOENT. It is the most under-rated debugging tool in Linux.
[edit: 2026] strace has not changed. Syscalls have increased (about 450 in 2026, versus 300 in 2008, with io_uring, bpf, clone3, openat2). ltrace (tracing library calls) complements strace (tracing syscalls). strace -c remains the first diagnostic tool and strace -f remains the only way to know what a program really does without reading its source code. ptrace is still the mechanism and ptrace is still the target of security hardening (Yama LSM).