In the two previous lessons we have been counting the cost of a page fault in milliseconds and we have watched a server fall into thrashing because of the disk. Now it is time to look straight at that component we have been treating as a slow black box. Because it is not a black box: a mechanical disk and an NVMe SSD have completely different physics, and the decisions the operating system makes for one are counterproductive for the other.

A word about scope: this lesson deals with storage as a managed resource, not as a file system. Here you will see the physics of the device, how much an access costs, how requests are ordered and how several disks are combined into a reliable volume. Everything to do with files, directories, inodes, partitions and mounting belongs to module 4. The boundary is clear: here we manage numbered blocks; there they are given meaning.

Contents

  1. The complete storage hierarchy
  2. Inside a mechanical hard disk
  3. Calculating the time of a disk access
  4. SSDs and NAND memory: different physics, different rules
  5. Write amplification, garbage collection and TRIM
  6. NVMe and why it changes the rules
  7. LBA and the block device abstraction
  8. Disk request scheduling
  9. Linux's real schedulers
  10. RAID: combining disks for capacity, speed or reliability
  11. Meteora's decision for /var/lib/meteora
  12. Measuring with lsblk and iostat -x

The complete storage hierarchy

In 01-01 we saw the memory hierarchy as far as RAM. Now we complete it downwards, which is where the differences turn brutal:

Level Typical latency Bandwidth Capacity Cost per GB Volatile
Registers 0.3 ns ~1 KB Yes
L1 cache 1 ns 1 TB/s 64 KB Yes
L2 cache 4 ns 500 GB/s 1 MB Yes
L3 cache 15 ns 200 GB/s 32 MB Yes
RAM (DDR4) 80-100 ns 20 GB/s 8 GB €4 Yes
NVMe SSD 20-100 µs 3,500 MB/s 1 TB €0.08 No
SATA SSD 100-200 µs 550 MB/s 1 TB €0.06 No
Hard disk 5-15 ms 150 MB/s 8 TB €0.02 No
LTO-9 tape tens of seconds 400 MB/s 18 TB €0.008 No

The jumps between adjacent levels are factors of 2 to 5, except for one:

RAM → NVMe SSD:  100 ns → 50 µs   = factor of 500
RAM → hard disk: 100 ns → 8 ms    = factor of 80,000

The abyss lies between RAM and persistent storage, and that abyss is the reason the page cache exists, the reason for the buffering we calculated in 01-06 and the reason for the demand paging of the previous lesson. The entire design of an operating system's I/O side consists of avoiding that crossing.

An analogy on a human scale, scaling 1 ns up to 1 second:

Operation Real time On a human scale
L1 cache access 1 ns 1 second
RAM access 100 ns A minute and a half
NVMe SSD access 50 µs 14 hours
Hard disk access 8 ms 3 months
Server reboot 60 s 1,900 years

When the aggregator causes a major fault, from the CPU's point of view it is like waiting three months for one piece of data.

Inside a mechanical hard disk

An HDD is the only component with moving parts in a modern server, and its physics governs everything about its behavior.

Side view:                       Top view of a platter:

   ┌─────────────────┐           ╭─────────────╮
═══╪═══ platter 0 ═══╡ ← head    │ ╭─────────╮ │  ← track 0 (outer)
   │                 │           │ │ ╭─────╮ │ │  ← track 1
═══╪═══ platter 1 ═══╡ ← head    │ │ │  ·  │ │ │  ← spindle
   │                 │           │ │ ╰─────╯ │ │
═══╪═══ platter 2 ═══╡ ← head    │ ╰─────────╯ │
   └────────┬────────┘           ╰─────────────╯
        actuator arm              sector = arc of a track

The vocabulary, which you need to be clear about for the calculations:

Term What it is
Platter A spinning magnetic disk. An HDD has between 1 and 9
Surface Each side of a platter; each one has its own head
Track A concentric circle on a surface
Cylinder The set of tracks at the same radius on every platter
Sector The minimum read/write unit: 512 bytes or 4 KB
Head Reads and writes; they all move together on the arm

The cylinder is a key concept: since all the heads move as one, reaching data in the same cylinder requires no arm movement, even if it lives on different platters. It is practically free.

An access has three time components:

Component What happens Order of magnitude Depends on
Seek time Moving the arm to the right cylinder 3-12 ms The distance travelled
Rotational latency Waiting for the sector to pass under the head 2-8 ms The disk's RPM
Transfer time Reading the data as it spins past 0.01-0.1 ms The block size

The average rotational latency is exactly half a revolution, because on average the sector will be halfway round:

RPM Full revolution Average rotational latency
5,400 11.1 ms 5.56 ms
7,200 8.33 ms 4.17 ms
10,000 6.0 ms 3.00 ms
15,000 4.0 ms 2.00 ms

Calculating the time of a disk access

Let us work it out with the figures for meteo-01's disk:

Disk: 7,200 RPM
Average seek time: 8.5 ms
Sustained transfer rate: 150 MB/s
Sector: 4 KB

Case 1: reading a 4 KB block at a random position.

Seek:                  8.50 ms
Rotational latency:    4.17 ms   (60,000 ms/min ÷ 7,200 RPM ÷ 2)
Transfer:              4 KB / 150 MB/s = 0.027 ms
                     ──────────
Total:                12.70 ms

Look at the proportion, which is what really matters:

Seek + latency: 12.67 ms = 99.8 % of the time
Transfer:        0.027 ms = 0.2 % of the time

99.8 % of the time goes on positioning, not on reading. That single figure explains the whole design of storage systems built on mechanical disks.

Case 2: reading 1 MB contiguously.

Seek:                  8.50 ms
Rotational latency:    4.17 ms
Transfer:              1 MB / 150 MB/s = 6.67 ms
                     ──────────
Total:                19.34 ms

Reading 256 times as much data costs only 52 % more time.

Case 3: reading that same 1 MB as 256 scattered 4 KB blocks.

256 × 12.70 ms = 3,251 ms = 3.25 seconds

A direct comparison:

Pattern Time Effective MB/s
1 MB sequential 19.3 ms 51.8 MB/s
1 MB in 256 random blocks 3,251 ms 0.31 MB/s
Difference factor 168×

A mechanical disk is 168 times slower with random access than with sequential access. Three consequences follow that dominate system design:

  1. Reading more than you asked for is almost free (read-ahead): since you have already paid for the seek, make the most of it.
  2. Grouping scattered writes into a sequential one pays off enormously, even if it means writing more bytes. It is the foundation of log-structured file systems and of journaling (04-05).
  3. Reordering requests to minimize arm movement has a gigantic impact. That is the subject of section 8.

Applied to Meteora, with the 17 MB a day in /var/lib/meteora/readings/:

Sequential read of the day:      17 MB / 150 MB/s + 12.7 ms ≈ 126 ms
Read as 4,250 blocks of 4 KB:    4,250 × 12.7 ms ≈ 54 seconds

The same data, 428 times slower. That is why the aggregator has to walk the file in order, and why the madvise(MADV_SEQUENTIAL) of the previous lesson makes sense.

SSDs and NAND memory: different physics, different rules

An SSD has no moving parts. It stores bits as electrical charge trapped in NAND flash memory cells. This eliminates seek time and rotational latency, but it introduces new and very peculiar restrictions.

The internal structure:

SSD
 └── Channels (4-8, in parallel)
      └── NAND chips
           └── Erase blocks (256 KB - 4 MB)
                └── Pages (4-16 KB)  ← the READ and WRITE unit

And here is the asymmetry that explains everything:

Operation Unit Typical time Restriction
Read Page (4-16 KB) 25-100 µs None
Write Page (4-16 KB) 200-900 µs Only into already-erased pages
Erase Block (256 KB-4 MB) 2-10 ms Affects the whole block

A flash page cannot be overwritten. It has to be erased first, and erasing only works on entire blocks that are hundreds of times bigger than a page.

That restriction, which looks like a minor technical detail, has cascading consequences for the device's entire behavior.

The problem of the misaligned write

Suppose you want to modify 4 KB inside a 2 MB block that is full of data. The naive sequence would be:

1. Read the block's 2 MB into an internal buffer  → 2 MB read
2. Modify the 4 KB in the buffer
3. Erase the 2 MB block                           → 5 ms erase
4. Rewrite the 2 MB                               → 2 MB written

You have written 2 MB of flash in order to modify 4 KB. The amplification factor is 512.

No real SSD does this, precisely because it would be unacceptable. Instead, the controller writes the new 4 KB into an already-erased page somewhere else, and updates an internal table that translates logical addresses into physical ones. The old page is marked invalid, awaiting recycling.

That table is called the FTL (Flash Translation Layer) and it is, conceptually, a page table inside the SSD: the same indirection mechanism we studied in the previous lesson, applied to storage. Neither the operating system nor you ever see the flash's physical addresses.

Write amplification, garbage collection and TRIM

Since invalid pages pile up, the SSD needs to recycle them. That process is garbage collection:

Block A (2 MB) before:
[valid][invalid][valid][invalid][invalid][valid][free][free]

Process:
1. Copy the 3 valid pages into a new block
2. Erase the whole of block A
3. Block A becomes available

Block A after: [free][free][free][free][free][free][free][free]

The cost: to free up space, data that was already written had to be copied. That is write amplification:

WA = bytes written to the flash / bytes written by the operating system
Scenario Typical WA Why
Sequential writing, empty SSD 1.0-1.1 Blocks fill up and are recycled whole
Random writing, SSD 50 % full 2-3 Valid pages have to be relocated
Random writing, SSD 95 % full 5-10 There are hardly any free blocks left to recycle
Random writing, no TRIM 10-20 The SSD does not know which data is obsolete

An almost-full SSD behaves far worse than one with free space. And it is not just a matter of performance: flash has a limited number of erase cycles per cell.

NAND type Bits per cell Erase cycles Use
SLC 1 50,000-100,000 Industrial, caching
MLC 2 3,000-10,000 Enterprise
TLC 3 1,000-3,000 Consumer and server
QLC 4 300-1,000 Archival, read-intensive

With this you can work out the service life of meteo-01's SSD:

1 TB TLC SSD, 1,500 erase cycles
Total supported writes = 1 TB × 1,500 = 1,500 TBW (terabytes written)

Meteora's daily writes:
  Readings: 17 MB/day
  Logs: ~200 MB/day
  Backups and temporary files: ~500 MB/day
  Total: ~720 MB/day

With a write amplification of 3:
  720 MB × 3 = 2.16 GB/day onto the flash

Service life = 1,500 TB / 2.16 GB/day = 694,444 days = 1,902 years

Meteora's workload will never wear the SSD out. But change the scenario: a database server writing 500 GB/day with a WA of 5 would burn 2.5 TB/day, and those same 1,500 TBW would last 600 days. That is why TBW is a specification you have to check when buying SSDs for write-intensive servers.

Wear levelling

If the controller always wrote to the same blocks, those would wear out while the rest stayed pristine. Wear levelling spreads writes evenly, and it even moves static data that has not changed in a long time in order to free up its lightly worn blocks.

It is another of the reasons the SSD needs indirection: the relationship between a logical address and a physical location changes constantly.

TRIM

When you delete a file, the file system marks its blocks as free in its own structures, but the SSD never finds out: as far as it is concerned, that data is still valid and it will go on copying it during garbage collection.

The TRIM command (discard in NVMe) tells the SSD which blocks no longer hold useful data:

$ sudo fstrim -av
/var/lib/meteora: 128.4 GiB (137886920704 bytes) trimmed on /dev/nvme0n1p2
/: 12.1 GiB (12992958464 bytes) trimmed on /dev/nvme0n1p1

$ systemctl status fstrim.timer
● fstrim.timer - Discard unused blocks once a week
     Loaded: loaded (/lib/systemd/system/fstrim.timer; enabled)
     Active: active (waiting) since Mon 2026-08-24 00:00:12 CEST
    Trigger: Mon 2026-09-07 00:00:00 CEST; 6 days left

Without TRIM, the SSD ends up believing it is full even though the file system sees free space, and write amplification goes through the roof. Modern distributions enable fstrim.timer weekly, which is the recommended option over discard in the mount options (the latter issues a TRIM on every deletion and can hurt performance).

HDD versus SSD compared

Characteristic HDD 7,200 RPM SATA SSD NVMe SSD
Read latency 12.7 ms 150 µs 50 µs
Random 4K IOPS ~120 ~90,000 ~600,000
Sequential bandwidth 150 MB/s 550 MB/s 3,500 MB/s
Random versus sequential 168× worse 1.2× worse ~1×, the same
Power draw 6-10 W 2-3 W 5-8 W
Cost per GB €0.02 €0.06 €0.08
Failure mode Gradual, with warnings Sudden when cycles run out Sudden

The most important row is the fourth: on an SSD, random access costs practically the same as sequential access. That single difference invalidates decades of optimizations designed for mechanical disks, including almost every scheduling algorithm we are about to look at.

NVMe and why it changes the rules

The first SSDs connected over SATA using the AHCI protocol, designed in 2004 for mechanical disks. Its assumptions no longer held:

AHCI (SATA) NVMe
Command queues 1 65,535
Commands per queue 32 65,536
Physical interface SATA 6 Gb/s PCIe (4 GB/s per lane ×4)
Instructions per I/O ~4 MMIO registers 2
Interrupts One shared line MSI-X, one per queue and core
Protocol latency ~6 µs ~2.8 µs

The structural difference is parallelism. With AHCI, a single queue means every core competes for it behind a shared lock. NVMe gives one queue per core, removing the contention entirely: each CPU submits its requests to its own queue without synchronizing with anybody.

And here it connects with what we know: an SSD has 4-8 internal channels working in parallel. Saturating it takes many simultaneous requests in flight. With a single queue of 32 commands that is impossible; with 65,535 queues, trivial.

$ lsblk -d -o NAME,ROTA,SIZE,MODEL
NAME    ROTA   SIZE MODEL
nvme0n1    0 931.5G Samsung SSD 980 PRO 1TB
sda        1   7.3T ST8000NM0055-1RM112

The ROTA column (rotational) is the one to look at: 1 is a mechanical disk, 0 an SSD. The kernel uses it to decide read-ahead, the default scheduler and other policies. Checking it is always the first step when diagnosing disk performance.

LBA and the block device abstraction

Everything above — cylinders, heads, NAND channels, FTL — is hidden. The operating system sees a uniform abstraction: the block device.

Block device = linear array of N fixed-size blocks
               numbered from 0 to N−1

That numbering is LBA (Logical Block Addressing). Before 1990, CHS addressing (cylinder-head-sector) was used, which exposed the physical geometry and capped capacity at 8 GB. LBA replaced it with a sequential number and the disk's firmware does the translation.

$ sudo blockdev --getsize64 /dev/nvme0n1
1000204886016
$ sudo blockdev --getbsz /dev/nvme0n1
4096
$ cat /sys/block/nvme0n1/queue/logical_block_size
512
$ cat /sys/block/nvme0n1/queue/physical_block_size
512

The abstraction gives the operating system exactly four operations:

Operation What it does
read(lba, n) Reads n blocks starting at the given LBA
write(lba, n, data) Writes n blocks
flush() Forces the device's internal cache to be flushed
discard(lba, n) TRIM: these blocks no longer hold useful data

This uniformity is what lets the same kernel code work over a floppy disk, an NVMe SSD, a RAID volume or a network disk. It is the same extended-machine idea we saw in 01-01, applied to storage. The flush() operation is especially important and will come back in Space Allocation, Journaling and Integrity, because an fsync() that does not reach the physical platter guarantees nothing.

Disk request scheduling

With a mechanical disk, the order in which requests are served determines how much the arm moves, and we already know that is where 99.8 % of the cost lives. It is a scheduling problem analogous to the CPU one, but with a different metric: total head movement.

We will work through all the algorithms over the same queue:

Request queue (cylinders): 98, 183, 37, 122, 14, 124, 65, 67
Initial head position: 53
Disk range: 0 to 199

FCFS

Requests are served in arrival order.

53 → 98 → 183 → 37 → 122 → 14 → 124 → 65 → 67
Movement Distance
53 → 98 45
98 → 183 85
183 → 37 146
37 → 122 85
122 → 14 108
14 → 124 110
124 → 65 59
65 → 67 2
Total 640

The arm crosses the disk end to end several times. It is fair and simple, but dreadful.

SSTF (shortest seek time first)

The request closest to the current position is always chosen.

53 → 65 → 67 → 37 → 14 → 98 → 122 → 124 → 183
Movement Distance
53 → 65 12
65 → 67 2
67 → 37 30
37 → 14 23
14 → 98 84
98 → 122 24
122 → 124 2
124 → 183 59
Total 236

A 63 % reduction against FCFS. But SSTF is the disk equivalent of SJF, and it shares its flaw: starvation. If requests keep arriving near cylinder 60, the one at cylinder 183 may never be served.

SCAN (the elevator algorithm)

The head moves in one direction serving everything it finds, reaches the end and comes back. Like an elevator.

Let us assume it moves towards lower cylinders:

53 → 37 → 14 → 0 → 65 → 67 → 98 → 122 → 124 → 183
Movement Distance
53 → 0 53
0 → 183 183
Total 236

The same total as SSTF here, but with no starvation: every request is served within at most two full sweeps. That guarantee is what makes it usable.

Its flaw: requests just behind the head wait a whole sweep, while those just ahead of it are served immediately. Waiting time is not uniform.

C-SCAN (circular SCAN)

It only serves requests in one direction; on reaching the end it returns to the start serving nothing on the way back.

53 → 65 → 67 → 98 → 122 → 124 → 183 → 199 → 0 → 14 → 37
Movement Distance
53 → 199 146
199 → 0 (fast return) 199
0 → 37 37
Total 382

It travels further, but in exchange it offers a much more uniform waiting time: the disk behaves like a circular list and no area is systematically favored. It is the compromise usually preferred by systems where predictability matters more than average performance.

LOOK and C-LOOK

The obvious optimization of SCAN and C-SCAN: do not go all the way to the physical end if there are no requests there.

LOOK:

53 → 37 → 14 → 65 → 67 → 98 → 122 → 124 → 183
Movement Distance
53 → 14 39
14 → 183 169
Total 208

C-LOOK:

53 → 65 → 67 → 98 → 122 → 124 → 183 → 14 → 37
Movement Distance
53 → 183 130
183 → 14 (return) 169
14 → 37 23
Total 322

The complete comparison

Algorithm Head movement Against FCFS Starvation Uniform wait
FCFS 640 No Yes
SSTF 236 −63 % Yes No
SCAN 236 −63 % No No
C-SCAN 382 −40 % No Yes
LOOK 208 −67.5 % No No
C-LOOK 322 −50 % No Yes

LOOK wins on total movement and is the one UNIX systems have historically used. C-SCAN and C-LOOK are preferable when the variance of the latency matters more than the mean.

And now the observation you must always keep in mind: this whole section assumes a head that moves. On an SSD, the "distance" between LBA 14 and LBA 183 is zero: both accesses cost the same. Reordering requests for an SSD not only fails to help, it adds latency and burns CPU for no benefit whatsoever. That is exactly what motivated Linux's none scheduler.

Linux's real schedulers

Linux lets you choose the I/O scheduler per device:

$ cat /sys/block/nvme0n1/queue/scheduler
[none] mq-deadline kyber bfq

$ cat /sys/block/sda/queue/scheduler
none [mq-deadline] kyber bfq

The brackets mark the active one. Notice the decision the kernel has taken all by itself: none for the NVMe, mq-deadline for the mechanical disk.

Scheduler Strategy Suitable for Overhead
none (noop) FIFO, no reordering NVMe, fast SSDs Minimal
mq-deadline Orders by LBA with maximum deadlines HDD, SATA SSD Low
kyber Throttles the queues against a latency target Multi-queue NVMe with mixed load Low
bfq Fair sharing per process, with weights Desktop, interactive workloads High

none does nothing: it hands requests to the device in arrival order. It sounds like surrender, but it is the right choice for NVMe: the device has its own parallel queues and its own internal scheduler, and it knows more than the kernel does about its own geometry. Any reordering by the operating system would be a mistaken guess.

mq-deadline keeps two queues sorted by LBA (one for reads, one for writes) plus two FIFO queues by deadline. It serves in LBA order — which gives LOOK-like behavior — except when a request is about to run out its deadline, at which point it jumps in to serve it. The default deadlines are revealing:

$ cat /sys/block/sda/queue/iosched/read_expire
500
$ cat /sys/block/sda/queue/iosched/write_expire
5000

500 ms for reads and 5,000 ms for writes. Reads get ten times the priority, and the reason is solid: a read is almost always synchronous (there is a process blocked waiting for it, in state D), while a write is usually asynchronous (the process has already moved on, and the kernel will flush it when it can). Delaying a read blocks somebody; delaying a write does not.

bfq (Budget Fair Queueing) gives each process a budget of sectors and shares the bandwidth fairly, with weights configurable through ionice. It is what made the backup solution in lesson 02-02 possible:

$ sudo ionice -c 3 -p $(pgrep -f daily-backup)
$ ionice -p 1877
best-effort: prio 4
ionice class Name Behavior
1 Real time Guaranteed priority access
2 Best effort Priority 0-7, 4 by default
3 Idle Only when nobody else is asking for the disk

Changing the scheduler takes effect immediately and needs no reboot:

$ echo bfq | sudo tee /sys/block/sda/queue/scheduler
$ cat /sys/block/sda/queue/scheduler
none mq-deadline kyber [bfq]

To make it persistent you use a udev rule, a mechanism we will see in the next lesson.

A practical rule for choosing:

Device and workload Choice
NVMe, server none
SATA SSD, server mq-deadline
HDD, server mq-deadline
Anything, desktop bfq
NVMe with mixed latency workloads kyber

RAID: combining disks for capacity, speed or reliability

Disks fail. The annual failure probability of a server disk is around 1-2 %, which sounds small until you have 100 disks: then you expect one or two failures a year with practical certainty.

RAID (Redundant Array of Independent Disks) combines several disks into one logical volume. The levels differ in how they distribute data and redundancy.

RAID 0 (striping)

Data is spread in stripes across all the disks. No redundancy.

Block:   0    1    2    3    4    5
Disk A:  [0]      [2]      [4]
Disk B:       [1]      [3]      [5]
  • Capacity: 100 % of the sum.
  • Performance: multiplied by N for both reads and writes.
  • Fault tolerance: none. If one disk fails, the whole volume is lost.

And the statistical trap: RAID 0 is less reliable than a single disk. With 4 disks at a 2 % annual failure rate:

P(all 4 survive) = 0.98^4 = 0.922
P(volume failure) = 7.8 % per year, against 2 % for a single disk

You multiply the risk by four. It only makes sense for reproducible data: caches, temporary files, intermediate results.

RAID 1 (mirroring)

Every piece of data is written identically to all the disks.

Disk A: [0][1][2][3]
Disk B: [0][1][2][3]
  • Capacity: 50 % with two disks.
  • Reads: can be spread across both, up to 2× faster.
  • Writes: the speed of one disk (both have to be written).
  • Tolerates the failure of one disk without losing anything and with barely any degradation.

RAID 5 (distributed parity)

Data is split across N disks and a parity block computed with XOR is added, rotating which disk holds it.

Disk A:  [D0]  [D3]  [P2]  [D8]
Disk B:  [D1]  [P1]  [D6]  [D9]
Disk C:  [P0]  [D4]  [D7]  [P3]
Disk D:  [D2]  [D5]  [P?]  [D10]

P0 = D0 XOR D1 XOR D2

The magic of XOR: if D1 is lost, it is recovered as D1 = D0 XOR D2 XOR P0. The same operation serves both to compute and to rebuild.

  • Capacity: (N−1)/N. With 4 disks, 75 %.
  • Tolerates one failure.
  • Write penalty: modifying one block requires reading the old data, reading the old parity, computing the new parity and writing two blocks. Four I/O operations per write.

The serious risk of RAID 5 with large disks is the rebuild. Replacing an 8 TB disk means reading the other three in full:

Data to read: 3 × 8 TB = 24 TB
At 150 MB/s: 24,000,000 MB / 150 MB/s = 160,000 s ≈ 44 hours

Almost two days with the array degraded and without redundancy, subjecting the surviving disks — the same age and the same batch — to an intense read load. It is exactly when a second failure is most likely, and a second failure in RAID 5 means total loss. That is why RAID 5 is not advisable with disks larger than 2 TB.

RAID 6 (double parity)

Like RAID 5 but with two independent parity blocks.

  • Capacity: (N−2)/N. With 6 disks, 66.7 %.
  • Tolerates two simultaneous failures.
  • Write penalty: six operations per write.

It is the answer to RAID 5's rebuild problem: during those 44 hours of rebuilding there is still one level of redundancy left.

RAID 10 (mirroring + striping)

Two-disk mirrors, joined together by striping.

Stripe 1: Mirror(A, B)
Stripe 2: Mirror(C, D)
  • Capacity: 50 %.
  • No parity penalty: two operations per write.
  • Tolerates one failure per mirror; with luck, up to half the disks.
  • Extremely fast rebuild: you only have to copy the mirror disk, not read the whole array.

With 8 TB disks, rebuilding means copying 8 TB: about 15 hours, and without putting the rest of the array at risk.

Comparison table

With 4 disks of 8 TB each:

Level Usable capacity Failures tolerated I/O per write Read Rebuild
RAID 0 32 TB (100 %) 0 1 Impossible
RAID 1 (2+2) 16 TB (50 %) 1 per mirror 2 A copy: fast
RAID 5 24 TB (75 %) 1 4 Slow and risky
RAID 6 16 TB (50 %) 2 6 Slow but safe
RAID 10 16 TB (50 %) 1-2 2 A copy: fast

And the indispensable warning:

RAID is not a backup. It protects against hardware failure, and against nothing else. It will not save you from an accidental rm -rf, from ransomware, from a file system failure, from a RAID controller failure or from a fire. A backup is a copy separated in time and in space.

Meteora's decision for /var/lib/meteora

Let us apply all of it to a concrete case. The requirements:

Requirement Value
Data volume 17 MB/day = 6.2 GB/year
Retention 10 years = 62 GB, plus headroom: 500 GB
Write pattern Sequential, one file per day, continuous append
Read pattern Sequential (aggregator), light random (meteo-api)
Criticality High: lost readings cannot be recovered
Budget Moderate

Analysis of the options:

Option Capacity Assessment
A single SSD 1 TB Ruled out: one failure loses 10 years of data
RAID 0 (2 SSDs) 2 TB Ruled out: doubles the risk without needing the speed
RAID 5 (4 × 8 TB HDD) 24 TB Ruled out: 24 TB for 500 GB, and a 44 h rebuild
RAID 6 (6 disks) Ruled out: excessive cost and write penalty
RAID 1 (2 × 1 TB SSD) 1 TB Chosen

Why RAID 1 with two SSDs:

  1. The volume is small. 500 GB projected over 10 years fits into 1 TB with room to spare. Optimizing capacity contributes nothing here, and capacity is precisely the argument for RAID 5 and 6.
  2. Writes are continuous and append-only. RAID 5 penalizes you with 4 I/O operations per write; RAID 1 with only 2. With the ingestor writing constantly, that difference matters more than capacity.
  3. The rebuild is a plain copy. Replacing an SSD means copying at most 500 GB, about 15 minutes at 550 MB/s, without exposing the data the way RAID 5 would.
  4. Reads improve: the aggregator and meteo-api can read from both disks in parallel.
  5. Simplicity. RAID 1 is the easiest level to understand, set up and recover. In an emergency at three in the morning, that is worth more than 25 % extra capacity.

The configuration:

# Create the array
$ sudo mdadm --create /dev/md0 --level=1 --raid-devices=2 \
    /dev/nvme0n1p2 /dev/nvme1n1p2

# Check the status
$ cat /proc/mdstat
Personalities : [raid1]
md0 : active raid1 nvme1n1p2[1] nvme0n1p2[0]
      976630464 blocks super 1.2 [2/2] [UU]

# Scheduler: none, because these are NVMe
$ echo none | sudo tee /sys/block/nvme0n1/queue/scheduler
$ echo none | sudo tee /sys/block/nvme1n1/queue/scheduler

The [2/2] [UU] line is the one to keep an eye on: two disks out of two active, both U (up). If you saw [2/1] [U_], the array would be degraded and a disk would need replacing urgently.

And the part RAID does not cover: a daily copy to another server, in another physical location. RAID 1 protects against one SSD failing; only a backup protects against an accidental rm or a fire in the rack.

Measuring with lsblk and iostat -x

$ lsblk -o NAME,ROTA,SIZE,TYPE,MOUNTPOINT,SCHED
NAME        ROTA   SIZE TYPE  MOUNTPOINT          SCHED
nvme0n1        0 931.5G disk                      none
├─nvme0n1p1    0   512M part  /boot/efi           none
└─nvme0n1p2    0   931G part                      none
  └─md0        0   931G raid1 /var/lib/meteora
nvme1n1        0 931.5G disk                      none
└─nvme1n1p2    0   931G part                      none
  └─md0        0   931G raid1 /var/lib/meteora
sda            1   7.3T disk                      mq-deadline
└─sda1         1   7.3T part  /backup             mq-deadline

At a glance you have the complete topology: two NVMe drives forming md0 mounted on /var/lib/meteora, a 7.3 TB mechanical disk for /backup, and the right scheduler on each one.

iostat -x is the main diagnostic tool:

$ iostat -x 2 2
Device  r/s     w/s     rkB/s    wkB/s   rrqm/s wrqm/s  r_await w_await aqu-sz  %util
nvme0n1 142.50  388.00  4104.00  9820.00   0.00  12.50    0.08    0.12   0.04   3.20
nvme1n1 138.00  388.00  4020.00  9820.00   0.00  12.50    0.09    0.12   0.04   3.15
md0     280.50  388.00  8124.00  9820.00   0.00   0.00    0.00    0.00   0.00   0.00
sda       2.00  118.50    64.00 18204.00   0.00  84.00   11.20   28.40   3.44  92.10

Column by column, with what each one means:

Column What it measures When to worry
r/s, w/s Operations per second (IOPS) Compare against the device's maximum
rkB/s, wkB/s Bandwidth Compare against the maximum
rrqm/s, wrqm/s Requests merged by the kernel High = sequential access (good)
r_await, w_await Average latency in ms, queue included The key metric
aqu-sz Average queue length > 1 sustained = saturation
%util % of time with at least one request in flight Misleading on NVMe

Reading this particular data:

  • nvme0n1 and nvme1n1 show almost identical figures, as they should in RAID 1: the same writes to both, reads shared out.
  • md0 adds up the reads (280.5 = 142.5 + 138) but does not double the writes (388), which is exactly the expected mirror behavior.
  • An r_await of 0.08 ms on the NVMe drives. Excellent, within expectations.
  • sda is the problem: a w_await of 28.4 ms, an aqu-sz of 3.44 and a %util of 92.1 %. The backup disk is saturated writing 18 MB/s. Since it is a mechanical disk under a write-intensive load, that is its normal behavior, but the backup had better not compete with anything critical (hence the ionice -c 3).
  • A wrqm/s of 84 on sda: the kernel is merging many contiguous requests before sending them, a sign of sequential writing. That is what you want on a mechanical disk.

An important warning about %util: on mechanical disks it is reliable (they can only serve one request at a time, so 100 % means saturation). On NVMe it is misleading: the device serves dozens of requests in parallel, so it can read 100 % while running at 5 % of its real capacity. On SSDs, the reference metric is await, not %util.

$ iostat -x 1 | awk '/^(nvme|sd|md)/ {printf "%-8s await_r=%6.2f await_w=%6.2f queue=%5.2f\n", $1, $10, $11, $21}'

And to measure the device's real capacity before putting anything into production:

$ sudo fio --name=random4k --filename=/var/lib/meteora/testfile \
    --rw=randread --bs=4k --iodepth=32 --numjobs=4 \
    --size=1G --runtime=30 --group_reporting

read: IOPS=487k, BW=1903MiB/s (1995MB/s)
     lat (usec): min=8, avg=64.21, max=1842

487,000 random 4 KB IOPS with an average latency of 64 µs. Compare that with the ~120 IOPS of a mechanical disk: a factor of 4,000.

Common Mistakes and Tips

Applying mechanical-disk intuition to an SSD. Defragmenting an SSD achieves nothing and burns write cycles. Neither does reordering requests: in flash there is no distance. The first thing to do when analyzing performance is to look at lsblk -o NAME,ROTA and know what you are dealing with.

Trusting %util on an SSD. An NVMe drive at 100 % %util may be completely idle in real terms. Use await and aqu-sz.

Filling an SSD to the brim. Above 90 % occupancy, garbage collection loses its effectiveness and write amplification jumps from 2 to 10. Always leave 10-20 % free; many server SSDs reserve it at the factory (over-provisioning).

Using RAID 5 with large disks. With 8 TB disks, a 44-hour rebuild with no redundancy is a real risk of losing everything. For disks larger than 2 TB, use RAID 6 or RAID 10.

Confusing RAID with a backup. RAID protects against a disk failing. It does not protect against accidental deletions, logical corruption, ransomware or physical disasters. Those are different problems with different solutions.

Forgetting TRIM. With no fstrim.timer active, an SSD ends up with degraded write performance for no apparent reason. Check it with systemctl status fstrim.timer.

Changing the I/O scheduler without measuring. The default the kernel picks is usually right. If you are going to change it, measure before and after with fio or with the real workload; a lot of "optimization" changes make things worse.

Diagnostic tip: when you suspect a disk problem, this is the order that works: lsblk -o NAME,ROTA,SCHED (what you have), iostat -x 2 (look at await and aqu-sz), iotop -o (which process is doing the I/O) and cat /proc/mdstat if there is RAID (is it degraded?). And remember to look at vmstat's b column too: if there are processes in state D, you already know from lesson 02-01 that they are stuck waiting on exactly this.

Exercises

Exercise 1: calculating disk access times

meteo-01 has a backup disk with these characteristics:

Speed: 7,200 RPM
Average seek time: 9.0 ms
Transfer rate: 160 MB/s
Sector: 4 KB
  1. Calculate the time to read a random 4 KB block.
  2. Calculate the time to read the 17 MB of 2026-08-31.dat sequentially.
  3. Calculate the time to read those same 17 MB as 4 KB blocks scattered across the disk.
  4. How many random 4 KB IOPS does this disk deliver? Compare it with the NVMe drive's 487,000.
  5. If the aggregator takes 90 seconds to process the day reading the file sequentially, what proportion of the time is CPU and what proportion is disk?

Exercise 2: disk request scheduling

A disk running from cylinder 0 to 4,999 has its head at 2,150 and this request queue:

2069, 1212, 2296, 2800, 544, 1618, 356, 1523, 4965, 3681
  1. Calculate the total head movement with FCFS, SSTF, SCAN (upwards), C-SCAN (upwards), LOOK (upwards) and C-LOOK (upwards).
  2. Rank them from best to worst.
  3. Which one would you choose if the requirement were that no request should wait more than two full sweeps? And if it were to minimize the total time?
  4. If this device were an NVMe SSD, what would your answer be and why?

Exercise 3: deciding a RAID configuration

Meteora wants to expand its storage. You have 6 disks of 4 TB and these requirements:

  • Readings data (/var/lib/meteora): 500 GB, critical, continuous sequential writing, read-intensive.
  • Logs and temporary files (/var/log, /tmp): 200 GB, reproducible, write-intensive.
  • Historical archive (/archive): 8 TB, written once and read rarely, important but not critical.
  1. Propose a RAID configuration for each of the three uses, stating how many disks you assign.
  2. Calculate the usable capacity of each volume.
  3. Justify each choice using the criteria from the lesson.
  4. Calculate the rebuild time of each volume if a disk fails (assume 180 MB/s).
  5. What is left uncovered by RAID and what would you do about it?

Solutions

Solution 1

1. A random 4 KB block.

Seek time:               9.000 ms
Rotational latency:      (60,000 ms/min ÷ 7,200 RPM) ÷ 2 = 8.33 ÷ 2 = 4.167 ms
Transfer:                4 KB ÷ 160 MB/s = 0.0244 ms
                       ─────────
Total:                  13.191 ms

How the time breaks down:

Positioning: 13.167 ms = 99.8 %
Transfer:     0.024 ms =  0.2 %

2. The 17 MB sequentially.

Initial seek:            9.000 ms
Rotational latency:      4.167 ms
Transfer:                17 MB ÷ 160 MB/s = 106.25 ms
                       ─────────
Total:                  119.42 ms

In practice a 17 MB file is not perfectly contiguous, so there would be some extra seeking, but the order of magnitude is right: about 120 ms.

3. The same 17 MB in scattered blocks.

Number of blocks: 17,000,000 ÷ 4,096 = 4,150 blocks
Time: 4,150 × 13.191 ms = 54,743 ms = 54.7 seconds
Pattern Time Effective MB/s Factor
Sequential 0.119 s 143 MB/s
Random 54.7 s 0.31 MB/s 459× worse

The same data, 459 times slower. It is the difference between the aggregator finishing before you have made a coffee and it taking nearly a minute just to read.

4. Random IOPS.

IOPS = 1,000 ms/s ÷ 13.191 ms = 75.8 IOPS

The comparison:

Device Random 4K IOPS Factor
HDD 7,200 RPM 76
SATA SSD ~90,000 1,184×
NVMe SSD 487,000 6,408×

That factor of 6,408 is one of the most important figures in computing over the last decade. It explains why databases that were unworkable became trivial, and why many software optimizations designed to minimize random I/O stopped making sense.

A complementary reading of the figure: 76 IOPS means that disk cannot even serve 76 random requests per second. If meteo-api receives 40 requests per second and each one causes 2 random reads, that disk is already at its limit.

5. The CPU/disk split within the 90 seconds.

Disk time (sequential read):  0.119 s
Total time:                  90.000 s
Disk share: 0.119 / 90 = 0.13 %
CPU share:  99.87 %

The aggregator is CPU-bound, not disk-bound. This quantitatively confirms what we deduced in 02-02 from its context switches (a ratio of 63 to 1 between voluntary and involuntary ones, far below that of the other processes).

The practical consequence is important: swapping the backup disk for an NVMe drive would not speed the aggregator up at all. You would save 0.1 seconds out of 90. To speed it up you have to optimize the computation, parallelize it or give it more CPU. It is the kind of conclusion that stops you spending money on the wrong component, and it is why you measure before you buy.

A note: this calculation would change completely if the aggregator read randomly. In that case it would be 54.7 s of disk against 90 s in total, 61 % disk, and the conclusion would be reversed.

Solution 2

Queue: 2069, 1212, 2296, 2800, 544, 1618, 356, 1523, 4965, 3681. Head at 2150. Disk from 0 to 4999.

Sorted: 356, 544, 1212, 1523, 1618, 2069, | 2150 | , 2296, 2800, 3681, 4965

FCFS:

Movement Distance
2150 → 2069 81
2069 → 1212 857
1212 → 2296 1084
2296 → 2800 504
2800 → 544 2256
544 → 1618 1074
1618 → 356 1262
356 → 1523 1167
1523 → 4965 3442
4965 → 3681 1284
Total 13,011

SSTF:

Step From Nearest Distance
1 2150 2069 (81) against 2296 (146) 81
2 2069 2296 (227) against 1618 (451) 227
3 2296 2800 (504) against 1618 (678) 504
4 2800 3681 (881) against 1618 (1182) 881
5 3681 4965 (1284) against 1618 (2063) 1284
6 4965 1618 3347
7 1618 1523 95
8 1523 1212 311
9 1212 544 668
10 544 356 188
Total 7,586

SCAN (upwards, as far as 4999):

2150 → 2296 → 2800 → 3681 → 4965 → 4999 → 2069 → 1618 → 1523 → 1212 → 544 → 356
Leg Distance
2150 → 4999 2849
4999 → 356 4643
Total 7,492

C-SCAN (upwards, jumping from 4999 to 0):

2150 → ... → 4965 → 4999 → [jump to 0] → 356 → ... → 2069
Leg Distance
2150 → 4999 2849
4999 → 0 (return) 4999
0 → 2069 2069
Total 9,917

LOOK (upwards, without reaching the end):

2150 → 2296 → 2800 → 3681 → 4965 → 2069 → 1618 → 1523 → 1212 → 544 → 356
Leg Distance
2150 → 4965 2815
4965 → 356 4609
Total 7,424

C-LOOK:

2150 → ... → 4965 → [jump to 356] → 356 → ... → 2069
Leg Distance
2150 → 4965 2815
4965 → 356 (return) 4609
356 → 2069 1713
Total 9,137

2. The ranking:

Place Algorithm Head movement Against FCFS
1 LOOK 7,424 −43 %
2 SCAN 7,492 −42 %
3 SSTF 7,586 −42 %
4 C-LOOK 9,137 −30 %
5 C-SCAN 9,917 −24 %
6 FCFS 13,011

3. The choice according to the requirement.

If no request may wait more than two full sweeps: SCAN, C-SCAN, LOOK or C-LOOK, all valid. What is decisive is ruling out SSTF, which offers no such guarantee: if requests kept arriving near 2150, the one at 4965 might never be served. The fact that SSTF happens to do well in this particular case changes nothing; the guarantee is about the worst case, not about one example. Among the four valid ones, C-LOOK is the best option if you also want the wait to be uniform, because it does not systematically favor any area of the disk.

If the goal is to minimize the total time: LOOK, with 7,424 cylinders. It wins because it wastes no movement travelling to empty ends (34 cylinders too many in SCAN) and makes none of the unproductive return trips of the circular variants.

A methodological note: the difference between LOOK (7,424) and SSTF (7,586) is only 2.1 %. With a different queue SSTF could win. LOOK's solid advantage is not the 2 %, it is the guarantee of no starvation at the same price.

4. If it were an NVMe SSD: none, and none of these algorithms.

The full reasoning:

  • The metric loses its meaning. "Total head movement" measures the physical motion of an arm. In flash there is no arm. Reaching LBA 356 and LBA 4965 costs exactly the same: about 50 µs.
  • Reordering is counterproductive. Sorting requests burns CPU and adds latency in the kernel queue, in exchange for a benefit of zero. You are paying to get nothing.
  • The device knows more. The SSD has its own FTL, its 4-8 parallel channels and its own internal scheduler, which knows the real physical location — invisible to the kernel because of wear levelling. Any order the operating system imposes rests on a geometry that does not exist.
  • NVMe wants parallelism, not order. With 65,535 queues, the optimum is to submit all 10 requests at once and let the device serve them in parallel across its different channels. Serializing them into an "optimal" order destroys precisely what makes NVMe good.
$ echo none | sudo tee /sys/block/nvme0n1/queue/scheduler

An estimate of the outcome: the 10 requests, submitted in parallel at a latency of ~50 µs each and with enough internal parallelism, would complete in roughly 50-100 µs in total. With the mechanical disk and LOOK, 7,424 cylinders of movement come to something like 150-200 ms. A factor of around 2,000, obtained not by scheduling better but by giving up scheduling altogether.

Solution 3

1 and 2. The proposed configuration.

Use Disks RAID level Usable capacity Needs
/var/lib/meteora 2 × 4 TB RAID 1 4 TB 500 GB
/var/log, /tmp (shared with the above) 200 GB
/archive 4 × 4 TB RAID 5 12 TB 8 TB

The calculations in detail:

RAID 1 with 2 disks:  4 TB × 1 = 4 TB usable (50 % of 8 TB)
RAID 5 with 4 disks:  4 TB × (4−1) = 12 TB usable (75 % of 16 TB)
Total usable: 16 TB out of 24 TB raw (66.7 %)

The critical data (500 GB) and the reproducible data (200 GB) fit together into the 4 TB RAID 1 with plenty of headroom, in separate subvolumes or logical partitions. Dedicating separate disks to /var/log would waste two 4 TB disks on 200 GB.

3. The justification for each choice.

/var/lib/meteora on RAID 1:

  • Maximum criticality. Lost readings are unrecoverable, so redundancy is non-negotiable.
  • Continuous sequential writing. RAID 1 costs 2 I/O operations per write; RAID 5 costs 4 (read data, read parity, write data, write parity). With the ingestor writing non-stop, that difference is paid every second.
  • Read-intensive. RAID 1 lets reads be served from both disks in parallel, which directly benefits the aggregator and meteo-api.
  • There is capacity to spare. 500 GB needed against 4 TB available: there is no argument for sacrificing performance or simplicity in exchange for more space.

/var/log and /tmp on the same RAID 1:

  • They are reproducible, so technically RAID 0 would do. But dedicating disks of their own would squander 8 TB raw on 200 GB.
  • Living alongside the critical data gives them redundancy for free, which is a welcome bonus: losing the logs exactly when you are diagnosing a disk failure is the worst possible moment.
  • A necessary precaution: /var/log growing unchecked could fill the volume and block the writing of readings. It has to be capped with logrotate and journald (SystemMaxUse=), or isolated into a subvolume with a quota.

/archive on RAID 5:

  • Here capacity is what rules: 8 TB needed out of 12 TB usable. RAID 10 with the same 4 disks would give only 8 TB, right at the limit and with no room to grow.
  • It is written once and read rarely. RAID 5's write penalty is irrelevant in a workload that hardly writes at all: it is precisely the scenario RAID 5 was designed for.
  • It is important but not critical: if it were lost, it would be recoverable from the backups and by reprocessing the original data.
  • The disks are 4 TB, not 8, which puts the rebuild in an acceptable range, as we will see in the next point.

4. Rebuild times at 180 MB/s.

RAID 1 (4 TB):

Data to copy: 4 TB = 4,000,000 MB
Time = 4,000,000 / 180 = 22,222 s = 6.2 hours

And with only 700 GB of real data, many implementations (mdadm with a bitmap, or ZFS/btrfs) copy only the blocks actually in use:

700,000 MB / 180 MB/s = 3,889 s = 1.1 hours

During the rebuild, the surviving disk carries on serving requests normally.

RAID 5 (4 disks of 4 TB):

All 3 surviving disks have to be read in full: 3 × 4 TB = 12 TB
Time = 12,000,000 MB / 180 MB/s = 66,667 s = 18.5 hours

Comparison and risk:

Volume Rebuild Redundancy during it Risk
RAID 1 1.1 - 6.2 h None (1 disk left) Low: a short window
RAID 5 18.5 h None (tolerates 0 further failures) Moderate: a long window with 3 disks under heavy load

RAID 5's 18.5 hours are acceptable — a long way from the 44 hours of the 8 TB case we saw in the lesson — but they deserve two concrete precautions:

# 1. Cap the rebuild speed so as not to saturate the array
$ echo 100000 | sudo tee /proc/sys/dev/raid/speed_limit_min

# 2. Weekly integrity check to find unreadable sectors
#    BEFORE they are needed during a rebuild
$ sudo systemctl enable --now mdcheck_start.timer

# 3. SMART monitoring to anticipate failures
$ sudo smartctl -a /dev/sda | grep -E 'Reallocated|Pending|Uncorrectable'

The periodic check is the most important of the three: the scenario that kills RAID 5 is discovering an unreadable sector on disk B right in the middle of rebuilding disk A. Reading the whole array every week finds those sectors while there is still redundancy to repair them.

5. What RAID does not cover.

RAID protects only against the physical failure of a disk. Left out:

Threat Does RAID protect? What you need
A disk failing Yes RAID 1 / 5
Accidental rm -rf No A backup with history
Ransomware No An immutable or offline backup
File system corruption No A backup + checksums
RAID controller failure No A backup on another system
Fire, theft, flood No A backup off site
A bug in the ingestor writing garbage No A backup with history

The reference rule is 3-2-1: three copies of the data, on two different media, with one off site.

Copy 1: RAID 1 on meteo-01                                (production)
Copy 2: /archive on the RAID 5, with daily snapshots      (same building, different volume)
Copy 3: nightly sync to remote storage                    (another location)
# Daily incremental backup with history and verification
$ rsync -avz --link-dest=/backup/previous \
    /var/lib/meteora/readings/ backup@remote:/backup/meteora/$(date +%F)/

--link-dest creates hard links to the previous copy, so each day takes up only what has changed but looks like a complete, independent copy. At 17 MB a day, a year of daily history takes about 6.2 GB instead of 6.2 TB.

And one last piece that everyone forgets: a backup that has never been restored is not a backup, it is a hope. You have to schedule a periodic test restore and verify that the recovered data is readable and correct.

Conclusion

Storage is the level of the hierarchy where the abyss appears: from RAM to an SSD there is a factor of 500, and to a mechanical disk a factor of 80,000. The entire design of the I/O subsystem consists of avoiding that crossing.

A mechanical disk spends 99.8 % of its time positioning — seek plus rotational latency — and only 0.2 % transferring, which makes it 168 times slower at random access than at sequential access. From that come read-ahead, the grouping of writes and the whole business of request scheduling. An SSD turns the rules upside down: with no moving parts, random access costs the same as sequential, but it brings restrictions of its own — you cannot overwrite without erasing the whole block — that force an FTL, garbage collection, wear levelling and TRIM, and that make an almost-full SSD behave far worse than one with room. NVMe is not just a faster connector: it replaces one queue with 65,535 and removes the contention between cores.

On top of that physics, the operating system builds the block device abstraction addressed by LBA, with only four operations, which is what lets the same code serve a floppy disk and a RAID array. Request scheduling — FCFS, SSTF, SCAN, C-SCAN, LOOK, C-LOOK — cuts head movement by up to 67 %, but it only makes sense if there is a head to move: on NVMe the right choice is none, that is, not to schedule at all. Linux's real schedulers reflect exactly that, and mq-deadline also builds in a very well-reasoned asymmetry: reads get a deadline ten times shorter than writes, because behind a read there is almost always a process blocked in state D.

RAID combines disks for capacity, speed or reliability, and the choice depends on the pattern: RAID 1 for critical data with continuous writing and a small volume — the case of /var/lib/meteora — RAID 5 only with moderate disks and light writing, RAID 6 or 10 when the rebuild would be long. And the warning that is never superfluous: RAID is not a backup. Finally, lsblk tells you what you have and iostat -x how it is behaving, with await as the reference metric and %util as the trap on SSDs.

So far we have treated the disk as the device. But a server has network cards, terminals, clocks, sensors and controllers of every kind, and the operating system has to offer a coherent interface for all of them without knowing the details of any. How it manages that — the classification into block, character and network devices, the principle that everything is a file, /dev and udev, and the three ways the CPU has of talking to a device — is what we will see in Device Management.

Operating Systems Fundamentals

Module 1: Introduction to Operating Systems

Module 2: Resource Management

Module 3: Concurrency

Module 4: File Structures

Module 5: System Protection and Security

Module 6: Virtualization and Containers

Module 7: Administration and Troubleshooting in Practice

© Copyright 2026. All rights reserved