Lesson 1 of 9 · 30m
What the Kernel Does When You Run a Program
fork, execve, ELF loading, and the syscall boundary — the machinery behind `./server` actually starting.
The two-syscall birth
You deploy a Go binary, SSH in, and run ./server. Between your shell printing a prompt and your main() executing, the kernel does a remarkable amount of work on your behalf — and understanding it is the difference between guessing and knowing when startup misbehaves.
A new process is never "created" directly. Your shell calls fork(), which clones the calling process: same memory (copy-on-write), same file descriptors, same credentials. The child then calls execve("./server", argv, envp), which is the syscall that actually throws away that copied address space and loads your program image. This fork-plus-exec split is why you can do things between the two calls — the shell sets up redirections, pipes, and environment there.
The kernel then parses the ELF header (peek at yours with readelf -h ./server), finds the PT_INTERP program header naming the dynamic loader — usually /lib64/ld-linux-x86-64.so.2 — and maps the loader plus your executable's segments into the new address space. Nothing is loaded yet: the pages are mapped but empty, and the CPU demand-faults them in from disk as they're touched. This is why a 500 MB binary starts instantly.
Try this
Watch the whole birth with strace:
strace -f -e trace=execve,mmap,openat /bin/ls 2>&1 | head -40
readelf -l /bin/ls | grep INTERP -A1
cat /proc/self/maps | head -20The first line of the maps output shows your own address space layout: text, heap, stack, mapped libraries, and the [vdso] region — a kernel-provided library mapped into every process that implements gettimeofday and friends without a context switch into ring 0.
The syscall boundary
Everything a process asks the kernel to do — open a file, allocate memory, send a packet — crosses one boundary: the syscall instruction. Your process runs in ring 3 (user mode); the kernel runs in ring 0. There is no other way in. Every permission check, every cgroup limit, every audit hook lives on that boundary, which is why it's the foundation of Linux security: a compromised userspace process can only ever ask for things the kernel is willing to grant.
Field notes
file: cannot execute: No such file or directoryon a binary that clearly exists almost always means thePT_INTERPloader is missing — classic on Alpine containers running glibc binaries.LD_PRELOADandLD_LIBRARY_PATHare honored by the loader, not the kernel — which is why they don't work on setuid binaries (the loader drops them for security).- Go binaries don't use libc by default, so
lddsays "not a dynamic executable". That's normal, not broken.