Skip to content
Preptima

CS fundamentals

What the operating system, the network stack and the number line are actually doing underneath your code, plus the cryptography vocabulary a screen uses as a filter. Fifty-nine items, fourteen worked through with the bits, a trace or a diagram.

59 questions

Go deeper on CS Fundamentals
Standard Sheet View

Operating systems and processes

What is the difference between a process and a thread?

A process owns an address space, file descriptors and kernel bookkeeping; a thread is a schedulable stream of execution inside one. So threads of a process share the heap, the code and the open files, and hold only their own stack, registers and program counter privately. That sharing is the whole trade: a thread switch is cheaper than a process switch and communication is a pointer rather than a pipe, but one thread corrupting shared memory takes the process down with it, and every shared structure now needs synchronisation. Processes buy isolation at the price of explicit inter-process communication, which is why a browser puts each tab in its own process and a web server puts each request on a thread.

Show me what a context switch actually costs.

The number people quote is the register save, which is the cheapest part of it.

what the kernel must do             what it really costs
----------------------------------  ------------------------------------------
save registers, switch stacks       ~100 ns of direct instructions
switch page tables on a process     TLB entries invalidated, tens of
switch                              microseconds of refill misses afterwards
scheduler bookkeeping               the new task's working set is cold in L1/L2

typical, one modern core
  plain function call               ~1 ns
  thread switch, same address space  1-5 us including cache effects
  process switch                     3-10 us

The indirect cost dominates, and it is invisible to a microbenchmark that switches between two tiny loops with no working set. Real code touches memory, so after a switch the caches hold the previous task's data and the incoming task takes thousands of misses before it runs at full speed.

That arithmetic is what decides thread-pool sizing. A thread that blocks on a lock or a socket a hundred thousand times a second spends the majority of its wall clock inside the scheduler rather than in your code, which is why the fix for a slow, heavily contended service is usually fewer threads doing longer units of work rather than more threads.

It is also the argument for asynchronous I/O and for user-space scheduling — green threads, goroutines, virtual threads. They do not make the kernel switch faster; they avoid needing one, because a task that blocks is parked by the runtime while the operating-system thread underneath it keeps running. The cost that remains is that any genuinely blocking call inside such a task pins the carrier thread and quietly removes the benefit.

What is a system call, and why is it more expensive than a function call?

It is a request for the kernel to do something your process is not privileged to do — read a file, send a packet, map memory. It costs more than a function call because it crosses a protection boundary: the CPU changes privilege level, arguments are validated because the kernel cannot trust your pointers, and speculative-execution mitigations flush or restrict predictors on the way in and out. That is hundreds of nanoseconds rather than one. The practical consequence is batching. Reading a file one byte at a time is thousands of times slower than reading it in 64 KB chunks, and the difference is almost entirely the boundary crossings rather than the disk.

What does the scheduler actually decide?

Which runnable thread gets a core next, and for how long. A general-purpose scheduler is optimising for two things in tension: throughput, which favours long time slices and few switches, and responsiveness, which favours short slices so an interactive task is not stuck behind a batch job. Modern Linux uses a fair-share scheme that tracks how much CPU time each task has received and runs the one furthest behind, with priority expressed as a weight on that accounting. The consequence worth knowing is that priority is relative, not absolute: a low-priority task still runs, so a busy loop at low priority still burns a core and still evicts your caches.

What is the difference between concurrency and parallelism?

Concurrency is a structural property — the program is composed of tasks whose execution can overlap in time — while parallelism is an execution property, tasks actually running at the same instant on different cores. A single-core machine runs concurrent programs without any parallelism by interleaving them. The distinction matters because it tells you what a change will buy: making an I/O bound service concurrent helps enormously on one core, since the waiting overlaps, while making a CPU-bound computation concurrent buys nothing until there are cores to run it on. Concurrency is about dealing with many things at once, parallelism about doing many things at once.

What is a zombie process, and what is an orphan?

A zombie has exited but its parent has not yet collected its exit status, so the kernel keeps a minimal entry alive to report it. It consumes no memory or CPU, only a process-table slot, and the bug is always in the parent: it never called wait. An orphan is the reverse — the parent died first — and it is re-parented to init, which reaps it correctly, so orphans are harmless. This matters in containers, because the process running as PID 1 in a container is your application rather than init, and if it does not reap adopted children the table fills with zombies until nothing can fork.