the cost of a system call
Think of working from home versus walking to a government office to get a form stamped. Anything you can do at your own desk is nearly instant. But every trip to the office - even for a trivial stamp - costs you the walk there, the security check, the wait, and the walk back. The walk is the overhead, and it dwarfs the actual stamping. A system call has exactly this shape: the boundary crossing costs far more than the small operation often does.
Concretely, a system call is much more expensive than an ordinary function call because of what the boundary crossing requires. The CPU must switch from user mode to kernel mode and back, save and restore registers and state, and run through the kernel's entry and validation code before it even starts the real work. There are also subtler costs: the switch can disturb the CPU's caches and pipelines, and modern security mitigations have made the crossing more expensive than it used to be. A plain function call might take a handful of CPU cycles; a system call typically costs on the order of hundreds to thousands of cycles in pure overhead, before counting the work it asks for. The exact number varies by CPU and OS - the point is the order of magnitude, not a precise figure.
Why it matters: this cost is why high-performance systems code tries to make fewer, bigger system calls rather than many tiny ones. Reading a file one byte at a time with a million read() calls is dramatically slower than reading it in large chunks, even though the total bytes are identical - because each call pays the crossing toll. This single fact explains why buffering exists (the C library batches your small writes into one big system call), why bulk and vectored I/O calls exist, and why a profiler that shows a program drowning in system calls is pointing at a real, fixable performance problem.
Copying a megabyte by calling read() and write() one byte at a time makes about two million system calls; copying it in 64 KiB chunks makes only about thirty. Same bytes moved, but the byte-at-a-time version can be tens of times slower purely from boundary-crossing overhead.
Fewer, larger calls beat many tiny ones - the toll is per crossing, not per byte.
Costly does not mean slow in absolute terms - one system call is still fast for a human. The cost only bites when you make many of them in a tight loop. The fix is usually batching (read or write more per call) or buffering, not avoiding system calls altogether.