OS Internals & Processes
Go hides the operating system behind a convenient runtime: you write os.Open, go func(), read a file — and see no system calls, no kernel threads, no memory pages. But in production that abstraction leaks. A service catches a SIGKILL from the OOM killer, hits a file-descriptor limit, top paints an "impossible" 146% CPU, and a slow socket holds a kernel thread. The explanation for each of these is at the OS level — and that is exactly where a backend engineer is tested in an interview.
The topics cluster around one boundary — between user space and kernel space. Your code runs in a restricted mode and cannot open a file itself; it asks the kernel via a syscall, and that switch to privileged mode is expensive. Your memory is virtual, split into pages the kernel maps onto physical frames. You hold kernel resources by descriptors and steer a process with signals. And when RAM runs out, the OOM killer steps in. This topic dissects the layer beneath the runtime piece by piece.
Topic Map
- User space and kernel space — why application code runs in a restricted mode and what the privilege boundary is for.
- System calls — how a request for an OS service switches the CPU from user mode into kernel mode and why it is costly.
- Virtual memory and pages — the page as the unit of memory management, mapping virtual pages to physical frames, and the page fault.
- Process vs thread — threads share an address space, processes are isolated; in Linux both are scheduler tasks.
- File descriptors — an integer the OS gives for an open resource, descriptors 0/1/2, and leaks through unclosed resources.
- Process signals —
SIGTERMfor a graceful shutdown vsSIGKILL, which the kernel does not let you catch. - The OOM killer — what happens when RAM runs out: swap and thrashing, or the kernel forcibly killing a process.
- Reading CPU utilization — why
topshows more than 100%: it is a sum across all cores, not a share of one.
Common Mistakes and Traps
| Mistake | Consequence |
|---|---|
Treating top over 100% as an error | It is a sum across cores; 146% is ~1.46 cores, normal for a multithreaded process |
Reaching for kill -9 first | SIGKILL denies the process a chance to close resources and save data — try SIGTERM first |
Not closing resources (files, resp.Body) | A file-descriptor leak and "too many open files" under load |
| Thinking processes share memory like threads | Processes are isolated; sharing needs IPC |
| Treating a syscall as an ordinary function call | It is a switch into kernel mode with a real cost; in Go it detaches the P from a thread |
| Assuming the OOM killer kills the "culprit" | It chooses by oom_score; it may kill the wrong process, especially without limits |
| Confusing a virtual page with a physical one | A virtual page is mapped to a physical frame through page tables |
Interview Relevance
OS internals is a mandatory section of a Go backend interview. They test not command knowledge but a model: where the kernel boundary lies, what is behind a syscall, and how a service interacts with the OS under load.
What interviewers usually check:
- Why the user/kernel space boundary exists and what crosses it.
- What a syscall is and what happens to the CPU when one is made.
- How a process differs from a thread in memory and isolation.
- What a file descriptor is and how "too many open files" leaks happen.
- The
SIGTERM/SIGKILLdifference and whykill -9is a poor first choice. - How to read CPU utilization above 100% and what the OOM killer chooses.
A typical wrong answer: "146% CPU in top is a bug, a process cannot use more than one core." That opens a discussion of how top sums utilization across all cores, so 146% means about one and a half cores at once — normal for a process with several threads or an active Go runtime.