# Syscall (Systems Call) A **syscall** is the mechanism a program uses to request a service from the operating system's kernel--things a normal program isn't allowed to do directly, like reading a file, sending network data, allocating memory, or creating a new process Modern operating systems separate memory and privilege levels into two modes: - **User mode** — where your application code runs, with restricted access to hardware and system resources - **Kernel mode** — where the OS core runs, with full access to hardware, memory management, and other processes ## How it Works, Roughly 1. Your program calls a function like `read()`, `write()`, `open()`, or `fork()` 2. That function triggers a special CPU instruction (like `syscall` on x86-64 or `svc` on ARM) that switches the processor from **user mode** to **kernel mode** 3. The kernel looks up which syscall was requested (usually via a syscall number) and executes the corresponding code 4. Control returns to your program in **user mode** with a result ## Classic Example in C on Linux ```c #include <unistd.h> write(1, "Hello\n", 6); // write() is a thin wrapped around the write syscall ``` The `write()` isn't doing the actual I/O itself, it's asking the kernel to do it, because only the kernel has permission to talk to the file descriptor/hardware directly ## Some Common Linux Syscalls | Syscall | Purpose | | --------------------------- | ---------------------------------------- | | `read`/`write` | Read/write data to a file descriptor | | `open`/`close` | Open/close a file | | `fork`/`execve` | Create a new process/run a program | | `mmap` | Map memory (e.g. for dynamic allocation) | | `socket`, `connect`, `send` | Networking | | `exit` | Terminate a process | ## A Subtlety You rarely call syscalls directly in high-level code. Instead, you call a library function (like `fopen()` in C, or `open()` in Python), and that library wraps the actual syscall along with extra bookkeeping (buffering, error handling, etc.). The syscall itself is the raw, minimal request to the kernel. ## Why This Matters for Perfomance Syscalls aren't free - switching between user and kernel mode has real overhead (context switching, CPU pipeline flushes). This is why things like buffered I/O exist: batching many small writes into fewer syscalls is much faster than making a syscall per byte. It's also why techniques like `io_uring` (Linux) exist - to reduce syscall overhead for high-throughput I/O. - `io_uring` is a Linux kernel interface for doing I/O asynchronously and efficiently, designed to fix the performance problems of older async I/O mechanisms - mainly by drastically cutting downon syscall overhead - The core idea is **shared ring buffers between user space and kernel space**, so both sides can communicate without a syscall for every operation. Both sides are memory-mapped via `mmap` so user and kernel space see the same memory. ### CPU Pipeline Flush Explained A **CPU pipeline flush** is what happens when a modern processor has to throw away work it already started, because it turns out that work was based on a wrong assumption about what instruction should run next A CPU doesn't execute one instruction completely before starting the next. It breaks execution into stages - roughly: fetch -> decode -> execute -> memory access -> write-back - then overlaps them, so while one instruction is being decoded, the next is already being fetched, and the one before is executing. This is a **pipeline**, and it's one of the main reasons CPUs are as fast as they are: multiple instructions are "in flight" at once, like an assembly line **Pipleline**: ```markdown Cycle: 1 2 3 4 5 Instr A: Fetch Decode Exec Mem Write Instr B: Fetch Decode Exec Mem Instr C: Fetch Decode Exec ``` #### The Problem: Branches Pipe-lining works great for straight-line code. But programs are full of branches - `if` statements, loops, function calls - where the CPI doesn't actually know which instruction comes next until it finishes evaluating a condition. If it just stalled and waiting every time, it would lose most of pipelining benefit. Instead, CPU's use **branch prediction**: they guess which way a branch will go (based on past behavior, heuristics, etc.) and speculatively start fetching and executing instructions down that predicted path - before the branch condition is actually resolved. #### The Flush If the guess was right, great - the CPU saved time. But if the guess was *wrong*, all those speculatively-executed instructions in the pipeline are invalid. The CPU has to: 1. Discard (flush) all the in-flight instructions from the wrong path 2. Reset to pipeline state 3. Start over fetching from the *correct* instruction address *This* is a **pipeline flush** (also called a *pipeline stall* or a *branch misprediction penalty*). ## Connecting Back to Syscalls A syscall forces a *mode switch* (user -> kernel -> user), which involves: - Saving/restoring registers and CPU state - Often invalidating cached predictions because you're jumping into completely different code (kernel code) that the CPU's predictions have no relevant history for - Sometimes flushing TLB (translation lookaside buffer) entires too, depending on the OS/CPU So the "overhead" of a syscall isn't just the kernel work itself - it's also this disruption to CPU's pipelining and prediction machinery, which is part of why batching operations (fewer, larger syscalls instead of many small ones) is faster. ## A Quick Analogy >Think of the pipeline like a factory assembly line that's building product designs A, B, C in parallel stages, betting that a decision earlier in the line ("customer wants blue") will hold. If that guess turns out wrong ("actually, customer wants red"), every half-built blue unit already on the line has to be scrapped, and the line has to restart with the red spec. That scrapped work is the flush.