OS Internals & Processes
Operating-system internals for Go backend engineers — virtual memory, syscalls, processes, file descriptors, signals, the OOM killer, and CPU utilization.
9 questions
JuniorTheoryCommonHow does an OS process differ from an OS thread?
How does an OS process differ from an OS thread?
Threads inside one process share the same address space; separate processes do not share memory by default. In the Linux kernel both are the same primitive — a Task — differing mainly in what they share. Processes can still exchange data through sockets, pipes, or signals, or share memory explicitly via shm_open or mmap.
Common mistakes
- ✗Reversing it — claiming processes share memory and threads do not
- ✗Not knowing both are a Task in the Linux kernel
- ✗Forgetting processes can share memory via
shm_openormmap
Follow-up questions
- →How does a goroutine relate to an OS thread in this picture?
- →What are the common ways two processes communicate?
JuniorTheoryOccasionaltop shows a process at 146% CPU. Is that real, and should you worry?
top shows a process at 146% CPU. Is that real, and should you worry?
Yes, it is real. top reports per-process CPU as a sum across all cores, so 146% means the process is using roughly 1.46 cores at once — entirely normal for a multi-threaded program on a multi-core box. Without more context you cannot say it is a problem; in the general case it is fine. Treating any value over 100% as impossible is the red-flag answer.
Common mistakes
- ✗Believing per-process CPU cannot exceed 100%
- ✗Assuming any reading over 100% signals a fault needing action
- ✗Confusing summed-across-cores percentage with swap or memory pressure
Follow-up questions
- →How does the Linux Completely Fair Scheduler divide CPU time across tasks?
- →What would 146% CPU mean for a process inside a 1-core cgroup quota?
JuniorTheoryOccasionalWhat is a file descriptor, and what do descriptors 0, 1, and 2 mean?
What is a file descriptor, and what do descriptors 0, 1, and 2 mean?
A file descriptor is a small integer the OS hands back when you open a resource, used for later reads, writes, and other operations. It identifies not just disk files but also pipes, sockets, devices, and memory-mapped files. By convention 0 is stdin, 1 is stdout, and 2 is stderr — every process starts with those three open.
Common mistakes
- ✗Thinking descriptors apply only to regular disk files
- ✗Misnumbering the standard streams instead of 0/1/2
- ✗Confusing a descriptor integer with the file's path or contents
Follow-up questions
- →Why can
dfshow free space while writes fail withno space left? - →Why does deleting an open file not immediately free its disk space?
JuniorTheoryOccasionalHow do you kill a Linux process, and why is kill -9 a poor first choice?
How do you kill a Linux process, and why is kill -9 a poor first choice?
Find the PID with ps aux | grep or top, then send a signal with kill <pid>. Plain kill sends SIGTERM (15), asking the process to shut down gracefully and release resources. kill -9 sends SIGKILL, which the process cannot catch or handle — it dies instantly with no cleanup, so you use it only when SIGTERM is ignored.
Common mistakes
- ✗Reaching for
-9before trying a graceful SIGTERM - ✗Thinking SIGKILL can be caught and handled by the process
- ✗Not knowing how to find the PID before sending a signal
Follow-up questions
- →Why does a Go service want to trap SIGTERM for graceful shutdown?
- →What happens to a process's open file descriptors after SIGKILL?
JuniorTheoryOccasionalWhat is the difference between user space and kernel space, and why does the split exist?
What is the difference between user space and kernel space, and why does the split exist?
Application code runs in user space — a restricted CPU mode that sees only the process's own virtual memory and cannot run privileged instructions. The kernel runs in kernel space with full access to devices, memory management, and the whole instruction set. The split isolates the OS so a buggy or malicious app cannot corrupt the system; crossing it requires a syscall.
Common mistakes
- ✗Thinking the split is about RAM regions rather than CPU privilege modes
- ✗Believing an app can enter kernel space without a syscall or trap
- ✗Confusing privilege level with on-disk versus in-memory layout
Follow-up questions
- →What CPU mechanism actually performs the transition into kernel space?
- →Why is the user-to-kernel transition relatively expensive?
JuniorTheoryOccasionalWhat is a memory page in a virtual-memory system?
What is a memory page in a virtual-memory system?
A page is the fixed-size unit (commonly 4 KB) the OS uses to manage virtual memory. The kernel maps each virtual page to a physical frame, to a file, or to swap, and per-page permission bits protect regions from being written or executed. Paging lets processes share physical RAM safely and gives each one its own isolated address space.
Common mistakes
- ✗Confusing a page with a whole segment or the entire process image
- ✗Forgetting that pages can map to a file or swap, not only physical RAM
- ✗Overlooking per-page permission bits that enforce protection
Follow-up questions
- →If every memory access needs a virtual-to-physical lookup, how does the structuring cache
TLBkeep that fast? - →What are hugepages and which workloads benefit from them?
MiddleTheoryOccasionalA service with a memory leak runs on a bare server. What happens when RAM runs out?
A service with a memory leak runs on a bare server. What happens when RAM runs out?
It depends on swap. With swap enabled, the system can limp along for a long time, paging memory out to disk. With swap off, the kernel's OOM killer starts terminating processes to reclaim memory — and not necessarily yours. The OOM killer decides based on resident memory (RSS, what is actually mapped to physical RAM), not the larger virtual size.
Common mistakes
- ✗Thinking the leaking process is always the one killed
- ✗Believing the OOM killer decides on virtual size rather than RSS
- ✗Ignoring that swap can delay OOM for a long time
Follow-up questions
- →Why does the kernel use RSS rather than virtual size to pick a victim?
- →How does a cgroup memory limit change this behaviour inside a container?
MiddleTheoryOccasionalWhat is a syscall, and what happens to the CPU when one is made?
What is a syscall, and what happens to the CPU when one is made?
A syscall is a program's request for an OS service it cannot do itself — reading a file, opening a socket, forking. Making one traps the CPU from user mode into kernel mode: it saves user state, switches to the kernel's privileged context, runs the handler, then returns. For example fork clones a process and epoll lets one goroutine wait on many file descriptors becoming readable.
Common mistakes
- ✗Thinking a syscall is just a normal library function with no mode switch
- ✗Forgetting that the user→kernel transition saves and restores CPU state
- ✗Believing
forkandepollare language features rather than OS syscalls
Follow-up questions
- →Why can a blocking syscall tie up an entire OS thread in a Go program?
- →How does
epolldiffer fromselectfor watching many descriptors?
SeniorTheoryOccasionalWhat synchronization primitives does an operating system offer, and how do they differ?
What synchronization primitives does an operating system offer, and how do they differ?
A mutex grants exclusive access, blocking waiters (the OS deschedules them). A semaphore allows up to N holders, generalizing a mutex. A spinlock busy-waits without sleeping — cheap only for very short critical sections on multiple cores. Advisory file locks (flock) coordinate across processes. On Linux a futex is the fast userspace primitive most of these are built on.
Common mistakes
- ✗Conflating a mutex and a semaphore instead of seeing the mutex as the
N=1case - ✗Thinking a spinlock is always faster, ignoring that it wastes CPU while waiting
- ✗Not knowing user-space mutexes are built on the kernel
futexon Linux
Follow-up questions
- →Why is a spinlock a bad choice on a single-core machine?
- →How does a
futexavoid a kernel call on the uncontended fast path?