A strong answer
Look at what happens inside the Linux kernel when runc creates a container: (1) Linux Namespaces (task_struct->nsproxy): Calling clone() or unshare() with CLONE_NEWPID | CLONE_NEWNET | CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWUSER gives the process a private view of PIDs (where the first process in the container sees itself as PID 1 inside the namespace, while the host kernel still sees it as e.g. PID 48291), a private network stack (lo, eth0, routing tables, iptables), and a private mount table! (2) Why Container PID 1 Is Special in Linux: In the Linux kernel, Process ID 1 (init) has two unique kernel-enforced rules: First, the kernel ignores any signal (SIGTERM / SIGINT) sent to PID 1 if PID 1 has not explicitly registered a custom sigaction signal handler for that signal (so a shell wrapper ENTRYPOINT ["/bin/sh", "-c", "node app.js"] or an app without a SIGTERM handler ignores docker stop / Kubernetes graceful termination until SIGKILL!). Second, whenever any parent process exits before its child, its orphaned children are reparented to PID 1 inside that PID namespace—if PID 1 does not call waitpid(-1, ..., WNOHANG) on SIGCHLD, dead children remain <defunct> Zombie entries in the kernel PID table forever! (3) pivot_root vs chroot: chroot only changes a process's path resolution pointer (easily escaped by root via fchdir); runc uses pivot_root(new_root, put_old) + umount2(put_old, MNT_DETACH) inside a Mount Namespace (CLONE_NEWNS) to completely unhook and detach the host root filesystem!
Inside any Linux PID Namespace (CLONE_NEWPID), the initial process (ns_pid == 1) is the designated reaper for all orphaned descendant processes in that namespace and will not be terminated by default SIGTERM/SIGINT delivery unless it installs an explicit user-space signal handler (or runs under an init binary like tini / dumb-init). In the Linux kernel's sig_task_ignored() (kernel/signal.c), if SIGNAL_UNKILLABLE is set on the namespace's init task (pid == 1) and the handler is SIG_DFL, the kernel drops the signal (except SIGKILL/SIGSTOP from an ancestor namespace). In exit_notify() (kernel/exit.c), orphaned children reparent to find_child_reaper(task) (the PID namespace's PID 1), remaining in EXIT_ZOMBIE state until PID 1 invokes wait4/waitpid.
Creating a container via clone() namespaces + pivot_root takes ~5–20 milliseconds and 0 MB of hypervisor kernel overhead (compared to 2–10 seconds and 512 MB+ guest OS kernel RAM for a traditional hardware VM). In production systems (OCI runc / crun, containerd, Docker --init (tini), Kubernetes shareProcessNamespace: true, and AWS Firecracker / gVisor sandboxes.), engineers monitor these exact trade-offs to keep p99 latency predictable and prevent resource exhaustion under peak load.
What the interviewer is listening for
- Traces how Linux Namespaces (
nsproxy),pivot_rootvschroot, and ContainerPID 1Signal & Zombie Reaping Semantics operates in memory step by step rather than giving a vague textbook definition. - States the core correctness invariant and explains why edge cases do not violate it.
- Quantifies time, space, and hardware costs (Linux Namespace (
clone/unshare),tini/dumb-initasPID 1) with concrete numbers. - Identifies real-world failure modes (Using shell-form
ENTRYPOINT node server.jsorCMD npm startin aDockerfile) and explains how production systems mitigate them.
Common mistakes
- Treating Linux Namespaces (
nsproxy),pivot_rootvschroot, and ContainerPID 1Signal & Zombie Reaping Semantics as a black box without explaining the underlying memory or state transitions. - Ignoring worst-case tail latency or memory overhead (Running worker containers that spawn short-lived CLI tools (
git,ffmpeg,libreoffice,chromium) without an init reaper (tini) asPID 1). - Applying the pattern without enforcing explicit capacity bounds or validation (Running containers as
UID 0 (root)without a User Namespace (CLONE_NEWUSER) orsecurityContext.runAsNonRoot: true).