From command to running code: the fork+exec idiom, what execve does (binfmt/ELF, tearing down & rebuilding the address space, the stack with argv/envp/auxv, jump to entry), static vs dynamic linking and ld.so with PLT/GOT lazy binding, shebang scripts, O_CLOEXEC, setuid and ASLR/PIE.
What happens between typing a command and its code running — how the kernel replaces a process's memory with a new program and hands control to it. Original, interview-focused notes; builds on Processes and the Address Space.
The Unix idiom is fork + exec:fork()makes a copy of the calling process, thenexec()replaces that copy's address space with a new program.forkcreates the process;execloads the code. Same PID, brand-new memory.
fork()-ing itself, then the child calls execve() — so the
parent (the shell) survives and can wait() for the child.exec does not return on success — the old program is gone, so there's nothing to
return to. It returns only on failure (e.g. file not found).O_CLOEXEC — how the shell wires up
pipes/redirections before exec, and why you should set close-on-exec on fds a child shouldn't inherit.binfmt handlers — ELF for binaries, script for
#! files, misc for others (Java, Wine via binfmt_misc).argv, envp, and the
auxiliary vector (auxv) — kernel-supplied info (page size, entry point, the vDSO address, ASLR
random bytes) the loader needs.ld.so). The kernel maps the
dynamic linker and jumps to it first; ld.so loads the shared libraries
(from the auxv/ELF), resolves symbols, and then calls the program's entry.ldd
shows the libraries; LD_PRELOAD/LD_LIBRARY_PATH influence loading.)#! (shebang): the kernel reads the interpreter line and effectively execs
that interpreter with the script as an argument — scripts aren't run by the kernel directly.exec return? When? (only on failure)execve: binfmt → new address space → stack (argv/envp/auxv) → entry point.#! script get executed?