An operating system (OS) is the layer of software that sits between your programs and the hardware. When your Python script opens a file, when your browser plays a video, or when two programs run "at the same time" on a laptop with a handful of CPU cores, the OS is the thing making it work. This lesson builds the mental model every later lesson depends on: what the OS is responsible for, how it protects itself from your code, how your code asks it for help, how the hardware gets its attention, how kernels are organized, and what happens between pressing the power button and seeing a login screen.
Interviewers rarely ask "what is an OS?" on its own. They probe the mechanics underneath: What actually happens when you call printf? Why can't a user program disable interrupts? What is the difference between a trap and an interrupt? Why is Linux called monolithic if it loads modules at runtime? By the end of this lesson you should be able to answer each of those in two or three confident sentences.
What an operating system does
The easiest way to understand the OS is to imagine a computer without one. Every program would need its own disk driver, its own code to talk to the network card, its own way of sharing memory with other programs, and nothing would stop a buggy program from overwriting another program's data. The OS solves these problems once, for everyone.
It helps to think of the OS in two roles.
- Resource manager. The hardware has a fixed amount of CPU time, memory, disk space and device bandwidth. Many programs want them at once. The OS decides who gets what, for how long, and enforces those decisions.
- Extended machine (abstraction provider). Raw hardware is ugly: disks are arrays of numbered blocks, network cards deal in frames, and memory is a single flat array of bytes. The OS hides this behind clean abstractions such as files, sockets, processes and virtual address spaces.
Here are the main jobs, each of which gets its own lesson later in this track:
| Job | What the OS provides | Example you already use |
|---|---|---|
| Process management | Create, schedule, pause and kill running programs | Running a browser and an editor together |
| Memory management | Give each program its own private address space | One crashing tab does not corrupt another program |
| File systems | Named files and folders on top of raw disk blocks | open("notes.txt") instead of "read block 81,920" |
| Device management (I/O) | Drivers and a uniform interface to hardware | The same write call works for a file, a pipe or a terminal |
| Protection and security | Users, permissions, isolation between programs | You cannot read another user's home folder |
| Networking | Protocol stacks and the socket abstraction | connect() to a server without touching the network card |
The kernel versus "the OS"
People use "operating system" loosely. Strictly, the kernel is the core part that runs with full hardware privileges and is always resident in memory. Everything else you might call the OS, such as the shell, the window system, system utilities like ls, and libraries like the C standard library (libc), runs as ordinary programs on top of the kernel.
+-----------------------------------------------+
| Applications: browser, editor, your program |
+-----------------------------------------------+
| Libraries: libc, libpthread, language runtimes|
+-----------------------------------------------+ user mode
| System utilities: shell, ls, systemd |
+================ system call interface =========+
| Kernel: scheduler, memory manager, file | kernel mode
| systems, network stack, device drivers |
+-----------------------------------------------+
| Hardware: CPU, RAM, disk, NIC, timer |
+-----------------------------------------------+
When an interviewer says "Linux", they usually mean the Linux kernel. A full system like Ubuntu combines that kernel with GNU tools, systemd, a package manager and a desktop.
User mode and kernel mode
If any program could talk directly to the disk controller or change the memory map, one bug (or one attacker) could wipe the whole machine. Modern CPUs prevent this with privilege levels, also called CPU modes.
- Kernel mode (also called supervisor mode or privileged mode): the CPU allows every instruction, including ones that change memory mappings, talk to devices or disable interrupts. Only the kernel runs here.
- User mode: the CPU refuses privileged instructions. If a user program tries one, the CPU raises an exception and hands control to the kernel, which usually kills the program.
The CPU tracks the current mode with a hardware mode bit (on x86 this is the current privilege level, stored in the low bits of the code segment register).
On x86 there are four "rings" numbered 0 to 3. In practice, Linux and Windows use only ring 0 for the kernel and ring 3 for user programs. On 64-bit ARM, the same idea uses exception levels: EL0 for applications, EL1 for the kernel, EL2 for a hypervisor and EL3 for secure firmware.
x86 rings (used in practice) ARMv8 exception levels
+-----------------------------+ +---------------------------+
| Ring 3: user programs | | EL0: applications |
+-----------------------------+ | EL1: OS kernel |
| Rings 1-2: mostly unused | | EL2: hypervisor |
+-----------------------------+ | EL3: secure monitor |
| Ring 0: kernel | +---------------------------+
+-----------------------------+
What counts as a privileged instruction?
A privileged instruction is one that could break isolation or crash the machine if misused. Typical examples:
- Changing the page-table base register (on x86, writing
CR3), which controls which memory a program can see. - Disabling or enabling interrupts (
cli/stion x86). - Executing
hltto halt the CPU. - Direct port I/O to devices (
in/outon x86, unless the kernel explicitly allows it). - Loading the interrupt descriptor table.
Notice what is not privileged: arithmetic, loading and storing to your own memory, and jumping around your own code. User programs do those billions of times per second with no kernel involvement. The kernel only steps in at the boundaries.
How does the mode switch?
The CPU moves from user mode to kernel mode only through controlled entry points that the kernel set up in advance:
- A system call instruction (
syscallon x86-64,svcon ARM64). - A hardware interrupt (for example, the timer fires).
- An exception (for example, a page fault or divide by zero).
In all three cases the CPU saves enough state to resume the interrupted code, switches to kernel mode, switches to the kernel's own stack and jumps to an address the kernel registered. The user program cannot choose an arbitrary kernel address to jump to. That is the whole trick: entry is only through doors the kernel built.
Going back is done with a special return instruction (sysret or iret on x86, eret on ARM), which restores the saved state and drops the privilege level.
Interview tip
If asked "why do we need two modes?", answer with protection: user mode stops a buggy or malicious program from touching hardware or other programs' memory directly, so every sensitive action has to go through the kernel, which can check permissions. Then mention that the switch happens only through system calls, interrupts and exceptions.
System calls
A system call (syscall) is the official way a user program asks the kernel to do something it is not allowed to do itself: open a file, allocate memory from the OS, create a process, send a packet, read the clock.
The life of a system call
Take this one line of C:
write(1, "hi\n", 3);
File descriptor 1 is standard output. Here is what happens on x86-64 Linux, step by step.
- Your code calls the
writefunction in libc. This is an ordinary function call; you are still in user mode. - The libc wrapper puts the system call number for
write(which is1on x86-64 Linux) into theraxregister and the arguments intordi,rsiandrdx. - It executes the
syscallinstruction. The CPU switches to kernel mode and jumps to the kernel's system-call entry point. - The kernel saves the user registers, looks up entry 1 in its system call table (an array of function pointers) and calls the kernel function that implements
write. - That function checks the arguments. Is descriptor 1 open? Is the buffer address really inside this process's memory? Is the caller allowed to write here?
- It does the work, here by copying the 3 bytes into the terminal driver's buffer.
- It puts the return value (bytes written, or a negative error code) in
raxand executessysret. The CPU drops back to user mode. - The libc wrapper sees the result. If it is negative, libc stores the error in
errnoand returns-1to you.
user mode kernel mode
--------- -----------
main()
| write(1,"hi\n",3)
v
libc write():
rax=1 rdi=1 rsi=buf rdx=3
syscall ---------------------> entry: save registers
table[rax] -> ksys_write()
validate fd, buffer
copy bytes to terminal
<--------------------------------- sysret (rax = 3)
returns 3 to main()
You can call a system call without the friendly wrapper, which makes the mechanism visible. This compiles on Linux:
#define _GNU_SOURCE
#include <sys/syscall.h>
#include <unistd.h>
int main(void) {
/* Same effect as write(1, "hi\n", 3), but skipping the libc wrapper. */
syscall(SYS_write, 1, "hi\n", 3);
return 0;
}
The numbers differ between architectures (on 32-bit x86 Linux, write is 4) and between operating systems, which is one reason programs use libc instead of raw numbers.
Watching system calls with strace
On Linux, strace shows every system call a program makes. It is one of the best ways to learn what the OS really does for you. Running cat on a small file gives output like this (trimmed to the interesting lines; exact flags, buffer sizes and descriptor numbers vary by version):
$ strace -e trace=openat,read,write,close cat notes.txt
openat(AT_FDCWD, "notes.txt", O_RDONLY) = 3
read(3, "hello\n", 131072) = 6
write(1, "hello\n", 6) = 6
hello
read(3, "", 131072) = 0
close(3) = 0
+++ exited with 0 +++
Read it line by line:
openatasks the kernel to opennotes.txtread-only. The kernel returns file descriptor 3 (0, 1 and 2 are already standard input, output and error). A file descriptor is just a small integer that indexes the process's table of open files inside the kernel.read(3, ...)copies up to 131,072 bytes from the file intocat's buffer. Only 6 bytes exist, so it returns 6.write(1, ...)sends those 6 bytes to standard output, sohelloappears.- The next
readreturns 0, which means end of file. close(3)releases the descriptor.
The untrimmed output also shows the dynamic loader opening shared libraries with openat and mapping them with mmap before main even runs. That is a useful thing to mention: even a "trivial" program makes dozens of system calls at startup. On macOS the equivalent tool is dtruss; on Windows, Process Monitor shows similar activity.
Categories of system calls
| Category | Linux / POSIX examples | Windows examples |
|---|---|---|
| Process control | fork, execve, exit, wait4, kill | CreateProcess, ExitProcess, WaitForSingleObject |
| File management | openat, read, write, close, lseek | CreateFile, ReadFile, WriteFile, CloseHandle |
| Device management | ioctl, read, write | DeviceIoControl |
| Information | getpid, uname, clock_gettime | GetCurrentProcessId, GetSystemTime |
| Communication | pipe, socket, mmap, shmget | CreatePipe, CreateFileMapping |
| Protection | chmod, chown, setuid | SetFileSecurity |
On Windows, the documented CreateFile and friends are library functions in kernel32.dll; they call less documented "native" functions in ntdll.dll, which then perform the real system calls. The layering idea is the same as libc on Linux.
Library call versus system call
A library call is a normal function call into code in your own address space. A system call crosses into the kernel. strlen is a pure library call; it never needs the kernel. printf is a library call that eventually makes a write system call, but usually not on every call: libc collects output in a buffer and flushes it when the buffer fills, when a newline is printed to a terminal, or when the program exits. That buffering exists precisely because system calls are more expensive than function calls.
Why system calls are expensive
A system call costs far more than a function call, typically somewhere in the range of tens of nanoseconds to a microsecond or more depending on the CPU and the call. The cost comes from:
- The mode switch itself and saving and restoring registers.
- Argument validation and copying data between user and kernel memory.
- Side effects on CPU caches and the TLB, and on some CPUs, mitigations for speculative-execution attacks (such as kernel page-table isolation) that add extra work on every entry.
This is why high-performance code batches work: reading 64 KB per read call instead of 1 byte at a time, or using interfaces like io_uring on Linux that submit many operations at once. Linux also exposes a few very frequent, read-only calls such as clock_gettime through the vDSO (virtual dynamic shared object), a small piece of kernel-provided code mapped into every process so the call can complete in user mode with no mode switch at all.
Common mistake
Saying "printf is a system call." It is a library function. The system call underneath is write, and because of buffering, ten printf calls may produce only one write.
Interrupts, traps and exceptions
The CPU executes instructions one after another. Three kinds of events can break that flow and send it into the kernel. Textbooks and CPU vendors use the words a little differently, so the safest approach in an interview is to define your terms and then give examples.
Hardware interrupts
An interrupt is a signal from a hardware device saying "I need attention". It is asynchronous: it has nothing to do with the instruction currently executing. A key press, a packet arriving at the network card, a disk finishing a read and the periodic timer tick are all interrupts.
How it works:
- The device raises a signal on an interrupt line. An interrupt controller (the APIC on modern x86) turns this into an interrupt number, also called a vector.
- At the next instruction boundary, the CPU checks for pending interrupts. If interrupts are enabled, it saves the current state and switches to kernel mode.
- It looks up the vector in the interrupt descriptor table (IDT) on x86, or the vector table on ARM: a table the kernel filled in at boot that maps each number to a handler address.
- The interrupt service routine (ISR), also called an interrupt handler, runs. It acknowledges the device and does the minimum urgent work.
- The CPU returns to whatever it was doing, or the kernel decides to run a different process instead.
Interrupt handlers must be fast, because while they run other interrupts may be blocked. Linux therefore splits handling into a top half (the ISR itself: acknowledge the device, grab the data, schedule further work) and a bottom half (softirqs, tasklets or workqueues that do the slower processing later with interrupts enabled). Network packet processing is the classic example.
The timer interrupt deserves special mention. It is how the OS takes the CPU back from a program that never makes a system call. Without it, while (1) {} would freeze a single-core machine forever. With it, the kernel regains control every few milliseconds and can switch to another process. This is the foundation of preemptive scheduling, covered in the CPU scheduling lesson.
Traps
A trap is a deliberate, synchronous transfer into the kernel caused by an instruction the program executed on purpose. The syscall instruction is the main example. A debugger breakpoint (int3 on x86) is another. "Synchronous" means it happens at a predictable point in the instruction stream: run the same program with the same input and the trap happens at the same instruction.
Exceptions (faults and aborts)
An exception is a synchronous event caused by an instruction that could not complete normally. Intel divides them into three groups:
- Faults can be fixed, and the faulting instruction is then re-executed. The page fault is the big one: the program touched a memory page that is not currently in RAM, the kernel loads it from disk, and the same instruction runs again as if nothing happened. (Details in virtual memory.)
- Traps (in Intel's sense) are reported after the instruction finishes, so execution continues with the next instruction. Breakpoints work this way.
- Aborts are severe and not recoverable, for example a machine-check error reporting failing hardware.
Some exceptions cannot be fixed by the kernel, such as division by zero or accessing an invalid address. The kernel then delivers a signal to the process (SIGFPE or SIGSEGV on Linux), which by default kills it. That is what "Segmentation fault (core dumped)" means.
| Event | Source | Synchronous? | Intentional? | Example |
|---|---|---|---|---|
| Hardware interrupt | External device | No (asynchronous) | No | Timer tick, key press, packet arrival |
| Trap / system call | Current instruction | Yes | Yes | syscall, breakpoint |
| Fault | Current instruction | Yes | No | Page fault (retried), divide by zero |
| Abort | Hardware error | Usually | No | Machine check |
Interview tip
A crisp one-liner: "Interrupts come from outside the CPU and are asynchronous; traps and exceptions come from the instruction currently running and are synchronous. A trap is intentional, like a system call; an exception like a page fault is an error condition the kernel may be able to fix." Then add that all three enter the kernel through the same table of handlers.
Software interrupts and signals: not the same thing
Two terms often get mixed up. On older x86 Linux, system calls were made with the instruction int 0x80, a "software interrupt", which is why some books call system calls software interrupts. That is a CPU-level mechanism. A signal (SIGINT, SIGKILL, SIGSEGV) is different: it is an OS-level notification delivered to a process by the kernel. Signals are covered in the processes lesson.
Polling versus interrupts versus DMA
How does the CPU know a device has finished?
- Polling (busy waiting): the CPU repeatedly reads a device status register. Simple and very low latency, but it wastes CPU time if the device is slow.
- Interrupt-driven I/O: the CPU starts the operation, does other work and gets interrupted when the device is done. Efficient for slow devices, but each interrupt has overhead.
- DMA (direct memory access): a DMA controller copies a whole block between the device and RAM without the CPU touching each byte, then raises one interrupt at the end.
Very fast devices flip the trade-off. A 100 Gbit/s network card can generate so many interrupts that handling them becomes the bottleneck, so Linux's NAPI switches to polling under heavy load. Frameworks like DPDK poll exclusively. This hybrid is a good detail to mention if the interviewer pushes further; storage and I/O covers it in depth.
Operating system structures
How should the kernel's code be organized? The answer is a trade-off between performance (fewer boundaries, fewer copies) and reliability and modularity (smaller trusted core, failures contained).
Monolithic kernels
In a monolithic kernel, all core services, including scheduling, memory management, file systems, the network stack and device drivers, run together in kernel mode in one address space. Parts call each other with ordinary function calls.
- Advantage: speed. A file read that needs the file system, the page cache and the disk driver involves only function calls inside the kernel.
- Disadvantage: a bug in any driver runs with full privileges and can crash or compromise the whole system. The code base is huge.
Linux is the standard example. Its loadable kernel modules let you add drivers and file systems while the system runs (modprobe, lsmod), but a loaded module still runs in kernel mode in the same address space. So Linux is modular, but still monolithic. Traditional Unix systems and the BSDs are also monolithic.
Layered systems
A layered design arranges the OS in levels, where each layer uses only the services of the layer below it. Layer 0 is the hardware; the top is the user interface. The classic example is the THE multiprogramming system designed by Edsger Dijkstra's group in the 1960s.
- Advantage: each layer can be built and tested on top of verified lower layers.
- Disadvantage: strict layering is hard to define (does the memory manager sit above or below the disk driver, when each needs the other?) and every request crossing several layers adds overhead.
Pure layered kernels are rare today, but the idea of layering survives inside every OS, for example in the network stack.
Microkernels
A microkernel keeps only the bare minimum in kernel mode: address spaces, threads and scheduling, and inter-process communication (IPC), which is the mechanism for passing messages between processes. File systems, device drivers and network stacks run as separate user-mode server processes.
Monolithic Microkernel
+---------------------------+ +------+ +------+ +-------+ +------+
| app | app | app | | app | | FS | |driver | | net |
+===========================+ +--+---+ +--+---+ +---+---+ +--+---+
| sched | mem | FS | net | | messages| | |
| drivers | IPC (kernel) | +==v=========v========v========v====+
+---------------------------+ | IPC | sched | address spaces |
hardware +------------------------------------+
hardware
- Advantage: reliability and security. If the file-system server crashes, it can be restarted without rebooting. The privileged code is small enough to review carefully, and in the case of seL4, to formally verify mathematically.
- Disadvantage: performance. A file read now involves messages between the app, the file-system server and the disk driver, each crossing the kernel. Early microkernels like Mach were notably slow; later designs such as L4 showed IPC can be made much cheaper.
Examples: MINIX 3, QNX (widely used in embedded and automotive systems), the L4 family and seL4.
Hybrid kernels
A hybrid kernel starts from a microkernel-style design but moves performance-critical services back into kernel mode.
- Windows NT (the base of every modern Windows) has a small kernel layer plus an "executive" of managers (I/O, memory, objects, processes) and drivers, all running in kernel mode. Windows also keeps a lot of its environment in user-mode subsystems and services.
- macOS and iOS use XNU, which combines the Mach microkernel (tasks, threads, IPC, virtual memory) with a BSD layer (processes, POSIX system calls, networking, file systems) and the I/O Kit driver framework, all in one kernel address space.
Some people argue "hybrid" is mostly a marketing label, since these kernels run most services in kernel mode like a monolithic kernel. In an interview, it is fine to say exactly that: hybrid kernels borrow microkernel structure but keep monolithic-style performance by running the important servers in kernel space.
Other designs worth naming
- Modular: a design style, not a separate category. Linux, Windows and macOS all load drivers dynamically.
- Exokernel: a research design in which the kernel only securely multiplexes hardware and lets applications manage abstractions themselves.
- Unikernel: the application and a minimal library OS are compiled into a single image that runs directly on a hypervisor. Used in some specialized cloud workloads.
| Structure | Kernel-mode code | Speed | Fault isolation | Examples |
|---|---|---|---|---|
| Monolithic | Everything | Fastest | Weak: one bad driver crashes all | Linux, FreeBSD |
| Layered | Everything, in strict layers | Slower | Moderate | THE system |
| Microkernel | IPC, scheduling, address spaces | Slower (IPC cost) | Strong: servers restart | MINIX 3, QNX, seL4 |
| Hybrid | Most services, microkernel-style structure | Close to monolithic | Moderate | Windows NT, macOS XNU |
Common misconception
"Linux supports modules, so it is a microkernel." No. Modules are loaded into the kernel's own address space and run in kernel mode. The question that decides the category is where the driver runs, not when it was loaded.
The boot process
Booting is the process of getting from a powered-off machine with empty RAM to a running OS. The challenge is a chicken-and-egg problem: the OS lives on disk, but you need software to read the disk. The answer is a chain of progressively more capable programs, each loading the next.
power on
|
v
+----------------------+ firmware in flash on the motherboard
| BIOS or UEFI | POST: test CPU, RAM, devices
+----------+-----------+ find a bootable device
|
v
+----------------------+ BIOS: 512-byte MBR, then stage 2
| Bootloader (GRUB, | UEFI: .efi file from the EFI System
| Windows Boot Manager)| Partition
+----------+-----------+ loads kernel + initramfs into RAM
|
v
+----------------------+ set up memory, CPUs, interrupts,
| Kernel | drivers; mount initramfs, then
| | the real root file system
+----------+-----------+
|
v
+----------------------+ PID 1: systemd on most Linux
| init / systemd | distributions; starts services,
| | then a login prompt or desktop
+----------------------+
Step 1: firmware (BIOS or UEFI)
When power stabilizes, the CPU starts executing at a fixed address that maps to the motherboard's firmware stored in flash memory.
The older standard is the BIOS (Basic Input/Output System). It runs the POST (power-on self-test) to check the CPU, memory and essential devices, then looks through the configured boot order for a bootable disk. It reads the very first 512-byte sector of that disk, the master boot record (MBR), into memory and jumps to it. The MBR holds a tiny first-stage bootloader, the partition table and the boot signature 0x55AA in its final two bytes. Because the 446 bytes reserved for code are not enough to do much, that first stage usually just loads a larger second stage.
The modern replacement is UEFI (Unified Extensible Firmware Interface). It also runs self-tests, but it is far more capable:
- It understands file systems. It reads the bootloader as an ordinary file (with a
.efiextension) from a small FAT-formatted EFI System Partition, instead of from a fixed raw sector. - It works with GPT (GUID Partition Table) disks, which lift MBR's 2 TiB size limit (with 512-byte sectors) and four-primary-partition limit.
- It supports Secure Boot: the firmware verifies a cryptographic signature on the bootloader, which then verifies the kernel, so tampered boot code is refused.
- It keeps boot entries in firmware variables, so you can choose between several installed systems.
Step 2: bootloader
The bootloader is a program whose only job is to load the kernel. On Linux this is usually GRUB (or systemd-boot); on Windows it is the Windows Boot Manager. GRUB shows a menu if configured, reads the kernel image (commonly /boot/vmlinuz-...) and an initramfs (initial RAM file system) into memory, passes the kernel command line (options like root=/dev/nvme0n1p2 quiet) and jumps to the kernel's entry point.
The initramfs exists because the kernel may need drivers that are not built in (say, for an encrypted disk or a RAID controller) just to find the real root file system. The initramfs is a small temporary root file system in RAM that contains those drivers and setup scripts.
Step 3: kernel initialization
The kernel decompresses itself, sets up its page tables and switches to virtual addressing, detects memory, starts the other CPU cores, installs interrupt handlers, initializes the scheduler and built-in drivers, and mounts the initramfs. Scripts in the initramfs load extra drivers, unlock or assemble the real root disk and then switch to it.
Step 4: init (PID 1)
Finally the kernel starts the first user-space process, which always gets process ID 1. On most current Linux distributions this is systemd; older systems used SysV init scripts, and some minimal systems use OpenRC or BusyBox init. If the kernel cannot start PID 1, it panics.
systemd reads unit files that describe services, mount points and their dependencies, starts independent services in parallel, and brings the system up to a target such as multi-user.target (text login) or graphical.target (desktop). PID 1 also has a lasting job: it becomes the parent of any process whose original parent died, and it collects their exit status. You will meet this again with orphan processes in the processes lesson.
You can see the result on a running Linux machine with ps -p 1 -o pid,comm (shows systemd), and systemd-analyze reports how long each boot stage took.
Interview tip
When asked "what happens when you turn on a computer?", walk the chain in order and name one job per stage: firmware tests hardware and finds a boot device; the bootloader loads the kernel and initramfs; the kernel sets up memory, interrupts and drivers and mounts root; init (systemd) starts services. Mentioning UEFI versus BIOS and Secure Boot shows you know the modern picture.
Types of operating systems
The history of operating systems is a story of keeping an expensive CPU busy, then keeping humans happy, then meeting deadlines. Each type below solved a problem the previous one left open.
Batch systems
Early computers ran one job at a time from punched cards or tape. A human operator grouped similar jobs into batches to reduce setup time, and a simple resident monitor loaded each job in turn. There was no interaction: you submitted a job and came back hours later for the printout.
Problem: when a job waited for slow I/O like a tape read, the CPU sat idle.
Multiprogramming
Multiprogramming keeps several jobs in memory at once. When the running job has to wait for I/O, the OS switches the CPU to another job that is ready. The goal is CPU utilization: never let the CPU idle while there is work.
Single program: CPU [A A A . . . . A A . . . . A] (dots = idle,
waiting on I/O)
Multiprogramming: CPU [A A A B B B B A A C C C C A] (no idle time)
Multiprogramming introduced the needs that define a modern OS: memory protection between jobs, CPU scheduling, and I/O management.
Time-sharing (multitasking)
Time-sharing extends multiprogramming to interactive users. The OS gives each program a short time slice (a few milliseconds) and uses the timer interrupt to switch, so quickly that every user at a terminal feels they have the machine to themselves. The goal shifts from throughput to response time. Unix grew up as a time-sharing system, and every desktop and phone OS today is a time-sharing system.
Multiprocessor, distributed and others
- Multiprocessor (multiprocessing) systems have several CPUs or cores sharing memory. The OS must schedule across cores and protect shared kernel data with locks.
- Distributed systems run across many networked machines that cooperate, and may present a single view of storage or computation.
- Embedded systems run inside devices such as routers, washing machines and car controllers, often with tight memory limits.
- Mobile operating systems (Android, built on the Linux kernel, and iOS, built on XNU) add strong per-app sandboxing and aggressive power management.
Real-time systems
A real-time operating system (RTOS) is judged by whether it meets deadlines, not by average speed. A correct answer delivered late is a wrong answer.
- Hard real-time: missing a deadline is a system failure. Airbag controllers, pacemakers, industrial robots. Examples: VxWorks, QNX, FreeRTOS, Zephyr.
- Soft real-time: missing an occasional deadline degrades quality but is tolerated. Video playback, audio and gaming. General-purpose systems can do soft real-time; Linux has real-time scheduling classes and a
PREEMPT_RTconfiguration that reduces worst-case latency.
The key property of an RTOS is predictability: bounded interrupt latency and bounded scheduling delay, even if average throughput is lower. Real-time scheduling algorithms are covered in the CPU scheduling lesson.
| Type | Main goal | Key mechanism | Example |
|---|---|---|---|
| Batch | Throughput, less setup time | Job queue, no interaction | Early mainframes |
| Multiprogramming | CPU utilization | Switch on I/O wait | 1960s mainframe OSes |
| Time-sharing | Response time | Timer-driven time slices | Unix, Linux, Windows |
| Real-time | Meet deadlines | Priority-based, bounded latency | VxWorks, QNX, FreeRTOS |
| Distributed | Share resources across machines | Networked cooperation | Cluster systems |
Common confusion
Multiprogramming means several programs in memory, with the CPU switching when one blocks. Multitasking or time-sharing adds timer-based preemption so interactive users get quick responses. Multiprocessing means several CPUs. They are independent ideas: a single-core laptop multitasks without multiprocessing.
Putting it together: what happens when you run a program
Here is how the pieces of this lesson combine when you type ./hello in a shell and press Enter. Each step names the lesson that goes deeper.
- The keyboard raises a hardware interrupt for each key. The kernel's handler passes characters to the terminal, and the shell, which was blocked in a
readsystem call, wakes up with the line./hello. - The shell calls
fork(), a system call that creates a copy of the shell process (processes). - The child calls
execve("./hello", ...). The kernel checks permissions, discards the child's old memory image, maps the new program and sets up its stack (memory management). - The scheduler picks the new process to run and drops into user mode at its entry point (CPU scheduling).
- As the program touches its code pages for the first time, page faults bring them in from disk (virtual memory).
printfbuffers text, then libc callswrite, a trap into the kernel, which passes the bytes to the terminal driver.- The program calls
exit. The kernel frees its resources and notifies the shell, which was waiting inwait. The shell prints a new prompt.
Being able to tell this story fluently is one of the most reliable ways to show an interviewer that you understand how an OS works, not just its definitions.
Interview questions
Q1. What is an operating system, and what are its main functions?
An operating system is the software that manages hardware resources and provides common services and abstractions to programs. Its main functions are process management, memory management, file systems, device and I/O management, protection and security, and networking. It acts both as a resource manager that shares the CPU, memory and devices fairly and safely, and as an extended machine that hides hardware details behind files, processes and sockets.
Q2. What is the difference between the kernel and the operating system?
The kernel is the core that runs in privileged mode and is always resident in memory: scheduler, memory manager, file systems, drivers and the system-call interface. "Operating system" often also includes user-space parts such as the shell, system utilities, init and core libraries. Linux is a kernel; Ubuntu is an operating system built on it.
Q3. Why do CPUs have user mode and kernel mode?
To protect the system from buggy or malicious programs. In user mode the CPU refuses privileged instructions such as changing page tables, disabling interrupts or doing direct device I/O, so a program cannot damage other programs or the hardware. Anything sensitive must go through the kernel via a system call, where permissions are checked.
Q4. Walk me through what happens during a system call.
The program calls a libc wrapper, which places the system-call number and arguments in registers and executes a special instruction such as syscall. The CPU switches to kernel mode and jumps to a fixed kernel entry point, which saves registers and uses the number to index the system-call table. The kernel validates arguments, performs the work, places the return value in a register and returns to user mode with sysret. On error, libc sets errno and returns -1.
Q5. Is printf a system call?
No. printf is a C library function that formats text into a buffer in user space. When the buffer is flushed (it fills, a newline is printed to a terminal, or the program exits), libc calls the write system call. Several printf calls can therefore result in a single system call.
Q6. What is the difference between an interrupt, a trap and an exception?
An interrupt is an asynchronous signal from hardware, like a timer tick or a network packet, unrelated to the current instruction. A trap is a synchronous, intentional entry into the kernel, such as a system call or breakpoint. An exception is a synchronous event caused by an instruction that could not complete, such as a page fault (fixable, then the instruction is retried) or a divide by zero (usually ends in a signal that kills the process).
Q7. Why is the timer interrupt so important?
It lets the kernel regain control of the CPU periodically, even from a program stuck in an infinite loop that never makes a system call. That is what makes preemptive multitasking possible: on each tick the scheduler can decide to switch to another process. Without it, a single misbehaving program could monopolize a core.
Q8. Compare monolithic and microkernel architectures.
A monolithic kernel runs all core services, including drivers and file systems, in kernel mode in one address space, so it is fast but a single faulty driver can crash the system; Linux is the main example. A microkernel keeps only IPC, scheduling and address-space management in the kernel and runs services as user-mode servers, so faults are isolated and the trusted code is small, but message passing adds overhead. Examples are MINIX 3, QNX and seL4.
Q9. Linux has loadable kernel modules. Does that make it a microkernel?
No. Modules are loaded into the kernel's address space and run in kernel mode with full privileges, so a buggy module can still crash the system. Modules make Linux modular and extensible at runtime, but its architecture remains monolithic. The category depends on where code runs, not when it is loaded.
Q10. What kind of kernel do Windows and macOS use?
Both are usually described as hybrid kernels. Windows NT has a layered kernel and executive whose managers and drivers all run in kernel mode. macOS uses XNU, which combines the Mach microkernel with a BSD layer and the I/O Kit driver framework in a single kernel address space. They borrow microkernel structure but keep most services in kernel mode for performance.
Q11. Describe the boot process of a Linux machine.
The firmware (BIOS or UEFI) runs self-tests and finds a boot device. With BIOS it loads the 512-byte MBR; with UEFI it runs a .efi bootloader from the EFI System Partition, optionally verifying signatures for Secure Boot. The bootloader, usually GRUB, loads the kernel and the initramfs and passes the kernel command line. The kernel initializes memory, CPUs, interrupts and drivers, uses the initramfs to mount the real root file system, then starts PID 1, typically systemd, which starts services and the login screen.
Q12. What are the advantages of UEFI over BIOS?
UEFI understands file systems and loads bootloaders as files from the EFI System Partition rather than from a fixed raw sector. It works with GPT disks, which remove MBR's 2 TiB limit (with 512-byte sectors) and four-primary-partition limit. It supports Secure Boot to verify signed boot code, and it keeps a configurable list of boot entries in firmware variables.
Q13. What is the difference between multiprogramming, multitasking and multiprocessing?
Multiprogramming keeps several programs in memory and switches the CPU when the running one waits for I/O, to maximize utilization. Multitasking, or time-sharing, adds timer-based preemption with short time slices so interactive users get fast responses. Multiprocessing means the hardware has multiple CPUs or cores that genuinely run code at the same moment.
Q14. What is a real-time OS, and how do hard and soft real-time differ?
A real-time OS guarantees that tasks meet timing deadlines, so its priority is predictable, bounded latency rather than high average throughput. In hard real-time systems a missed deadline is a failure, as in airbag or medical controllers. In soft real-time systems occasional misses only reduce quality, as in video or audio playback.
Q15. Why are system calls more expensive than function calls, and how do systems reduce the cost?
A system call needs a privilege switch, saving and restoring state, argument validation and copying between user and kernel memory, and it can disturb caches and the TLB; security mitigations may add more work. Programs reduce the number of calls by buffering (as libc does for printf) and batching (reading large blocks, or using io_uring on Linux). Linux also serves some frequent read-only calls, like clock_gettime, through the vDSO so they never enter the kernel.
Key takeaways
- The OS is both a resource manager (sharing CPU, memory, devices) and an abstraction provider (processes, files, sockets, address spaces). The kernel is its privileged core.
- The CPU's mode bit separates kernel mode, where any instruction is allowed, from user mode, where privileged instructions cause an exception.
- User code enters the kernel only through system calls, interrupts and exceptions, and always lands at handlers the kernel registered in advance.
- A system call is a library wrapper, a register-loaded number and a
syscall-style instruction;stracemakes them visible. Library calls likeprintfare not system calls. - Interrupts are asynchronous and come from devices; traps and exceptions are synchronous and come from the current instruction. The timer interrupt enables preemptive multitasking.
- Monolithic kernels (Linux) trade isolation for speed; microkernels (MINIX 3, QNX, seL4) trade speed for isolation; hybrid kernels (Windows NT, XNU) sit in between.
- Booting is a chain: firmware (BIOS/UEFI), bootloader (GRUB), kernel with initramfs, then PID 1 (systemd).
- OS types evolved from batch to multiprogramming to time-sharing, with real-time systems optimizing for deadlines rather than averages.
Next lesson
Continue with Memory Management: Allocation, Paging and Segmentation.

