Types of Operating Systems
Operating systems are classified by the workload they are built to serve. What separates them is the scheduling policy — whether the machine optimises for throughput, responsiveness, deadlines, or footprint — because no single policy serves every purpose.
Batch and Multiprogramming Systems
A batch processing operating system groups jobs with similar requirements and runs them without user interaction. Jobs were once submitted on punched cards and collected as printouts hours later, with no way to intervene once a job started.
Running one job to completion before starting the next wastes the CPU entirely during I/O. Multiprogramming fixed this by keeping several jobs in memory at once and switching to another whenever the running one blocks — the first step towards a modern scheduler.
Batch systems optimise throughput, meaning jobs completed per hour, and accept terrible response times as the price. The tracer on this page shows the cost directly: running three jobs to completion makes the last one wait for both predecessors before it executes a single instruction.
The model has not disappeared, only moved. Payroll runs, nightly ETL pipelines, scientific cluster jobs submitted through SLURM, and CI build queues are all batch processing under a modern name — nobody watches them, so latency does not matter and utilisation does.
- Jobs run to completion without interaction
- Multiprogramming overlaps computation with I/O
- Optimises throughput, not response time
- Still alive in payroll, ETL, and HPC schedulers
Time-Sharing Systems
A time-sharing operating system slices the CPU among many users by giving each process a short quantum — typically 10 to 100 ms — and preempting it when a timer interrupt fires. Switching fast enough makes every user feel they have the machine to themselves.
The mechanism is preemption. A hardware timer interrupts the running process whether or not it is finished, the scheduler saves its state, and another process resumes. Without that interrupt, one misbehaving program could hold the CPU forever, which is exactly what happened on cooperatively scheduled systems such as classic Mac OS and Windows 3.x.
The trade against batch is measurable. Time slicing adds a context switch per quantum — saving registers, swapping page tables, and losing cache warmth — so total throughput falls slightly while response time improves dramatically. The tracer contrasts the two on identical jobs.
Unix was designed around this model and every general-purpose desktop and server OS inherits it. Linux's CFS scheduler refines the idea by tracking each task's accumulated runtime and always running whichever has had least, rather than using a fixed rotation.
- A timer interrupt forces preemption on each quantum
- Optimises response time at a small throughput cost
- Cooperative scheduling lets one process hang the machine
- Linux CFS runs whichever task has had the least CPU
Real-Time Operating Systems
A real-time operating system is judged on timing guarantees, not speed or fairness. What matters is that a task provably completes before its deadline, every time, under worst-case conditions.
The distinction between hard and soft is the consequence of a miss. In a hard real-time system a missed deadline is a system failure — anti-lock brakes, pacemakers, flight control, and industrial robots cannot negotiate. A soft real-time system degrades instead: video playback drops a frame, audio glitches, and the system continues.
Achieving this means designing away unpredictability. RTOS kernels use priority-based preemptive scheduling with algorithms such as rate-monotonic or earliest-deadline-first, bound their interrupt latency, and avoid anything with variable timing — dynamic allocation, paging to disk, and unbounded caching are all restricted. Determinism is bought by giving up average-case performance.
Priority inversion is the classic hazard: a high-priority task blocks on a lock held by a low-priority one, which is itself preempted by a medium-priority task. This stalled the Mars Pathfinder rover in 1997, and the standard defence is priority inheritance. Common examples include VxWorks, QNX, FreeRTOS, and RTLinux.
- Judged on deadlines, not throughput or fairness
- Hard: a miss is failure. Soft: a miss degrades quality
- Rate-monotonic and earliest-deadline-first scheduling
- Priority inversion is the classic failure mode
Terms, operations, and practical uses
Workload categories
- BatchRuns prepared jobs with little interactive input and emphasizes throughput.
- Time sharingPreempts work frequently to keep many interactive programs responsive.
- Real timeEvaluates correctness partly by whether deadlines are met.
Specialized systems
- EmbeddedRuns inside a device with a narrow purpose and constrained resources.
- MobileCoordinates radios, sensors, touch input, application sandboxes, and battery limits.
- DistributedCoordinates services across multiple computers while hiding some machine boundaries.
Evaluation criteria
- Response timeDelay before a request begins receiving service.
- ThroughputAmount of work completed per unit of time.
- PredictabilityAbility to bound when important work will finish.
Share CPU time with round-robin scheduling
from collections import deque
def round_robin(bursts, quantum):
ready = deque(bursts)
order = []
while ready:
name, remaining = ready.popleft()
order.append(name)
remaining -= min(quantum, remaining)
if remaining:
ready.append((name, remaining))
return order
print(*round_robin([('A', 4), ('B', 3), ('C', 2)], 2))#include <iostream>
#include <queue>
#include <utility>
#include <vector>
using namespace std;
vector<char> roundRobin(queue<pair<char, int>> ready, int quantum) {
vector<char> order;
while (!ready.empty()) {
pair<char, int> job = ready.front();
ready.pop();
order.push_back(job.first);
job.second -= min(quantum, job.second);
// unfinished jobs go to the back instead of holding the CPU
if (job.second > 0) ready.push(job);
}
return order;
}
int main() {
queue<pair<char, int>> ready;
ready.push({'A', 4});
ready.push({'B', 3});
ready.push({'C', 2});
for (char name : roundRobin(ready, 2)) cout << name << ' ';
cout << '\n';
}import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
class Main {
static class Job {
char name;
int remaining;
Job(char name, int remaining) {
this.name = name;
this.remaining = remaining;
}
}
static List<Character> roundRobin(Queue<Job> ready, int quantum) {
List<Character> order = new ArrayList<>();
while (!ready.isEmpty()) {
Job job = ready.remove();
order.add(job.name);
job.remaining -= Math.min(quantum, job.remaining);
// unfinished jobs go to the back instead of holding the CPU
if (job.remaining > 0) ready.add(job);
}
return order;
}
public static void main(String[] args) {
Queue<Job> ready = new ArrayDeque<>();
ready.add(new Job('A', 4));
ready.add(new Job('B', 3));
ready.add(new Job('C', 2));
for (char name : roundRobin(ready, 2)) System.out.print(name + " ");
System.out.println();
}
}Step through it
Running on A=4 ms, B=3 ms, C=2 ms; quantum=2 ms
Read all 12 Steps
- Three jobs arrive A needs 4 ms, B needs 3 ms, C needs 2 ms, and all three are ready at once. How the OS orders this work is the single clearest difference between operating system types.
- Batch OS: run A to completion A batch system takes one job and runs it until it finishes, with no interruption. A occupies the CPU for all 4 ms while B and C simply wait.
- Batch OS: then B, then C B runs its full 3 ms, then C its 2 ms. Throughput is excellent — zero switching overhead — but C waited 7 ms before its first instruction. Nobody sitting at a terminal would accept that, which is exactly why batch systems ran overnight on punched cards.
- Time-sharing OS: set a 2 ms quantum A time-sharing system slices the CPU instead. Each job gets at most 2 ms before the timer interrupt fires and forces a switch, so no job can monopolise the machine.
- Run A for 2 ms, requeue it A uses its full quantum and still needs 2 ms. The scheduler preempts it and moves it to the back of the queue rather than letting it continue.
- Run B for 2 ms, requeue it B uses a quantum and has 1 ms left, so it also returns to the back. Notice C has now waited only 4 ms instead of 7 — every job makes visible progress early.
- Run C for 2 ms — it finishes C needs exactly one quantum, so it completes and leaves the queue for good. Its total wait was 4 ms against 7 ms under batch.
- Run A's final 2 ms A reaches the front of the queue again and uses its final 2 ms, completing at the 8 ms mark. It took two separate turns to finish work that batch scheduling would have done in one uninterrupted run.
- Run B's final 1 ms B needs less than a full quantum, so it runs 1 ms and yields voluntarily rather than being preempted. A job never has to consume its whole slice, and the scheduler simply moves on to whatever is next.
- Compare the completion times Batch finishes A at 4 ms, B at 7, C at 9 — average 6.7. Time-sharing finishes C at 6, A at 8, B at 9 — average 7.7. Batch wins on average completion, yet every user waited idle until their job's turn came. That is the trade the two policies make.
- Real-time systems change the question again A hard real-time OS cares about neither throughput nor fairness, only deadlines. If C must finish within 3 ms, it is scheduled first regardless of arrival order, and a missed deadline counts as total system failure — the standard for anti-lock brakes or a pacemaker.
- Real-time: the deadline is the constraint A real-time task with a 3 ms deadline preempts everything else the moment it becomes runnable, and completes in 2 ms. Throughput drops — A and B are pushed back — but the deadline holds. An embedded controller makes this trade permanently; a batch mainframe never does.
Mobile, Embedded, and General-Purpose Systems
An embedded operating system runs fixed software on dedicated hardware — a washing machine controller, a car's ECU, a router. It must fit in kilobytes of ROM, boot in milliseconds, and often run without an MMU or a filesystem. FreeRTOS and Zephyr are typical, and many embedded systems are also hard real-time.
Mobile operating systems add constraints a desktop never faces. Battery life makes energy a first-class scheduling concern, so Android and iOS aggressively suspend background apps and coalesce timer wakeups. Memory is not overcommitted to disk the way a desktop pages, so both terminate background processes under pressure rather than swapping. Both are Unix-derived — Android on the Linux kernel, iOS on Darwin — with heavily modified policies above.
General-purpose systems such as Windows, macOS, and desktop Linux compromise deliberately. They run interactive applications, background services, and batch work simultaneously, so their schedulers balance responsiveness against throughput rather than optimising either.
The classification is a spectrum, not a set of boxes. Linux ships in embedded routers, Android phones, desktops, servers, and supercomputers, with the same kernel tuned by different configuration. What genuinely distinguishes these systems is the policy layer — how the scheduler, memory manager, and power manager are configured for the workload.
- Embedded fits kilobytes of ROM on fixed hardware
- Mobile treats battery and memory pressure as scheduling inputs
- General-purpose deliberately balances competing goals
- One kernel can serve every class through configuration