Skip to content
academia.sh

Lesson 21 / 22

Swap Space

Pressure lasts 84 seconds, but the used-swap indicator raises a false alarm for 272 seconds, free memory for 451; as the swap area grows, the closability diagnosis rises from 44 wrong to 354 wrong.

Contents

The previous lessons treated storage only as a layer that holds data: partitions, file systems, mount points, and logical volumes. A portion of the same block device, though, holds no files at all and appears at no mount point. That portion is where pages are written when memory falls short.

This lesson’s question is not one of storage but of seeing: in which indicator does swap usage show up, and how late. The answer will not come out one-directional. Some indicators report pressure late, one keeps reporting it even after the pressure has passed, and one shouts even when there is no pressure at all.

Swap Space and What It Is Not

Swap is the disk space pages that do not fit in physical memory get written to. It can be a partition, it can be a file; both are formatted the same way and activated the same way. The file form is flexible because it can be added and removed afterward, the partition form is plain because it is independent of the file system’s state. The difference between them is not a ranking of superiority but a management preference.

The mechanics of paged memory were measured in the Operating System Concepts course: translating a virtual page to a physical page, the page fault, and the page-replacement procedure are defined there and are not repeated here. This lesson counts only what the operator can see.

What can be seen is a handful of numbers: swap’s total size, its used portion, pages written to swap and read from swap per second, free memory, and available memory. These are read through swapon --show, free, and the file holding memory statistics. The transcript below is a sample transcript showing the shape of the output; it was not run, and no numeric claim in this lesson comes from it.

NAME      TYPE       SIZE   USED PRIO
/dev/sdb4 partition  4G     1.2G   -2

               total        used        free      shared  buff/cache   available
Mem:            7.6G        4.1G        160M         88M        3.3G        3.1G
Swap:           4.0G        1.2G        2.8G

The free and available columns being separate in the transcript is the subject of this lesson’s first measurement: one does not count reclaimable cache as free, the other does.

Four Indicators, Four Separate Errors

The measurement is done in a 600-second window on the fictional server. The report-producing unit starts growing gradually from the two-hundredth second, finishes its work at the three-hundred-twentieth second; there is one more short, sharp spike at the four-hundred-eightieth second.

  • ST32 — Physical memory is 1200 pages, the usual resident demand is 900 pages. Pressure is the second demand exceeds physical memory, and the oracle knows this.
  • ST33 — A page written to swap stays there; used swap does not decrease until it is read back. This is the behavior that is the axis of the measurement.
  • ST34 — The cache fills free memory and grows by four pages per second. Available memory counts reclaimable cache as free.
  • ST35 — The warning threshold is 100 pages. The tool looks once every interval seconds; between two looks, the operator’s belief is the last reading.
"""How late (or how early) swap usage shows up in each indicator."""
SEED = 20260218
PERIOD = 600
PHYSICAL = 1200          # physical memory, pages
BASELINE = 900           # usual resident demand
THRESHOLD = 100          # memory warning threshold, pages
AMPLITUDE = 60            # fluctuation in demand


def generator(seed):
    d = seed

    def next_value(n):
        nonlocal d
        d = (d * 1103515245 + 12345) % 2147483648
        return d % n
    return next_value


def demand(seed=SEED):
    """Oracle: the resident pages really requested every second."""
    r = generator(seed)
    values = []
    for t in range(PERIOD):
        if 200 <= t < 320:
            base = BASELINE + (t - 200) * 6      # report job grows gradually
        elif 480 <= t < 495:
            base = 1700                           # short, sharp spike
        else:
            base = BASELINE
        values.append(base + r(2 * AMPLITUDE + 1) - AMPLITUDE)
    return values


def curves(seed=SEED):
    d_list = demand(seed)
    used, rate, free, available, pressure = [], [], [], [], []
    peak = previous = 0
    for t, d in enumerate(d_list):
        overflow = max(0, d - PHYSICAL)
        delta = abs(overflow - previous)          # pages sent to and read from swap per second
        previous = overflow
        peak = max(peak, overflow)                # written page stays until read back
        headroom = PHYSICAL - min(d, PHYSICAL)
        cache = min(4 * t, headroom)               # cache fills the free space
        used.append(peak)
        rate.append(delta)
        free.append(headroom - cache)
        available.append(headroom)                # reclaimable cache is not counted
        pressure.append(d > PHYSICAL)
    return {"used": used, "rate": rate, "free": free,
            "available": available, "pressure": pressure}


RULE = {"used swap": lambda e, i: e["used"][i] > 0,
        "swap rate": lambda e, i: e["rate"][i] > 0,
        "free memory": lambda e, i: e["free"][i] < THRESHOLD,
        "available memory": lambda e, i: e["available"][i] < THRESHOLD}


def count(e, rule, interval):
    missed = false_alarm = 0
    last = False
    for t in range(PERIOD):
        if t % interval == 0:
            last = rule(e, t)
        missed += e["pressure"][t] and not last
        false_alarm += last and not e["pressure"][t]
    return missed, false_alarm


E = curves()
print("oracle: seconds under pressure:", sum(E["pressure"]), "/", PERIOD)
print("indicator        t=100  t=240  t=300  t=330  t=485  t=560")
for name in ("used", "rate", "free", "available"):
    print(f"  {name:14s}", "  ".join(f"{E[name][t]:5d}" for t in
                                      (100, 240, 300, 330, 485, 560)))
print()
print("indicator              interval  missed  false alarm  wrong diagnosis")
for name, rule in RULE.items():
    for interval in (5, 30, 60, 120):
        missed, false_alarm = count(E, rule, interval)
        print(f"  {name:21s} {interval:6d}  {missed:9d}  {false_alarm:12d}"
              f"  {missed + false_alarm:11d}")
print()
for seed in (SEED, 20260219):
    e = curves(seed)
    s = " | ".join(f"{name.split()[0]} {sum(count(e, k, 5))}"
                    for name, k in RULE.items())
    print(f"seed {seed}: pressure {sum(e['pressure']):3d} seconds | interval 5 : {s}")
oracle: seconds under pressure: 84 / 600
indicator        t=100  t=240  t=300  t=330  t=485  t=560
  used               0      0    331    423    533    535
  rate               0      0     57      0     61      0
  free               0      0      0      0      0      0
  available        268    120      0    258      0    333

indicator              interval  missed  false alarm  wrong diagnosis
  used swap                  5          1           272          273
  used swap                 30         19           265          284
  used swap                 60         49           265          314
  used swap                120         69           225          294
  swap rate                  5          2            13           15
  swap rate                 30         19            25           44
  swap rate                 60         49            85          134
  swap rate                120         69           105          174
  free memory                5          0           451          451
  free memory               30          0           426          426
  free memory               60          0           396          396
  free memory              120          0           396          396
  available memory           5          1            12           13
  available memory          30         19            25           44
  available memory          60         49            85          134
  available memory         120         69           105          174

seed 20260218: pressure  84 seconds | interval 5 : used 273 | swap 15 | free 451 | available 13
seed 20260219: pressure  86 seconds | interval 5 : used 271 | swap 16 | free 469 | available 9

Three numbers side by side. The oracle: the window has 84 seconds of real memory pressure. The tool’s output: the indicator whose name directly names swap, used swap, sits at 423 at the three-hundred-thirtieth second, at 533 after the four-hundred-eighty-fifth, and never comes down. Wrong diagnosis: even read once every five seconds, that indicator raises a false alarm for 272 seconds; its total is 273.

The four indicators have four separate ways of erring. Used swap is cumulative: once a page is written to swap, it stays there until read back, so the indicator stays high even after the pressure ends and shows the past as if it were today; its false alarm reaches 272 seconds. Swap rate is instantaneous, and at a five-second interval it misses only 2 seconds and raises 13 false alarms; when the interval rises to sixty, the missed count jumps to 49, because the spike lasts fifteen seconds and falls between two looks. Free memory alarms almost the whole time — 451 seconds — because the cache fills the free space, and free memory sits near zero even on a healthy system. Available memory misses 1 second and raises 12 false alarms at the five-second interval.

The two lowest totals are 13 and 15, and both depend on the same sampling interval: when the interval rises to sixty, all four climb past 134. In the second seed, pressure lasts 86 seconds, and the five-second-interval totals come out to 271, 16, 469, and 9; the ordering does not change. The course’s rule finds its exact match here: what distinguishes is not how many numbers are looked at, but which number is looked at. The indicator with “swap” in its name turned out to be the worst one for finding swap pressure.

The Cost of Turning It Off

Turning off swap is a one-line command, and it has a one-line risk: every page in the area has to be read back into physical memory. If there is not enough room, the operation either fails or ends with a process being terminated for lack of memory.

  • ST36 — Turning off is possible when used swap fits into available memory. The oracle makes this comparison every second.
  • ST37 — The diagnosis tested is the common one: if usage percentage is under half, it can be turned off.
"""To turn off swap, every page in it must be read back into memory."""
SEED = 20260218
PERIOD = 600
PHYSICAL = 1200
BASELINE = 900
AMPLITUDE = 60


def generator(seed):
    d = seed

    def next_value(n):
        nonlocal d
        d = (d * 1103515245 + 12345) % 2147483648
        return d % n
    return next_value


def demand(seed=SEED):
    r = generator(seed)
    values = []
    for t in range(PERIOD):
        if 200 <= t < 320:
            base = BASELINE + (t - 200) * 6
        elif 480 <= t < 495:
            base = 1700
        else:
            base = BASELINE
        values.append(base + r(2 * AMPLITUDE + 1) - AMPLITUDE)
    return values


def state(seed=SEED):
    used, available, peak = [], [], 0
    for d in demand(seed):
        peak = max(peak, max(0, d - PHYSICAL))
        used.append(peak)
        available.append(PHYSICAL - min(d, PHYSICAL))
    return used, available


def tally(size, seed=SEED):
    used, available = state(seed)
    actual = diagnosed = wrong = 0
    for t in range(PERIOD):
        g = used[t] <= available[t]              # oracle: do the pages fit back in memory
        y = used[t] / size < 0.50                 # diagnosis: usage under half
        actual += g
        diagnosed += y
        wrong += g != y
    return actual, diagnosed, wrong


print("swap size  closable seconds  closable per DIAGNOSIS  wrong diagnosis")
for size in (512, 1024, 2048, 4096):
    g, t, y = tally(size)
    print(f"{size:9d}  {g:16d}  {t:21d}  {y:11d}")
print("observation window:", PERIOD, "seconds")
print()
for seed in (SEED, 20260219):
    print(f"seed {seed}: " + " | ".join(
        f"size {size}: wrong {tally(size, seed)[2]}" for size in (512, 4096)))
swap size  closable seconds  closable per DIAGNOSIS  wrong diagnosis
      512               246                    290           44
     1024               246                    483          237
     2048               246                    600          354
     4096               246                    600          354
observation window: 600 seconds

seed 20260218: size 512: wrong 44 | size 4096: wrong 354
seed 20260219: size 512: wrong 45 | size 4096: wrong 355

Three numbers side by side. The oracle: swap can be safely turned off in 246 of six hundred seconds. The tool’s output: in a 2048-page swap area, usage never rises above fifty percent, meaning the indicator says “fine” in 600 of the window’s 600 seconds. Wrong diagnosis: the same rule is mistaken 44 times at 512 pages, and 354 times at 2048 and above. In the second seed, these two numbers come out to 45 and 355.

The result runs against intuition, and precisely for that reason it is instructive: growing the swap area makes the percentage-based diagnosis worse. The same 533 pages appear as nearly all of a small area, an eighth of a large one; the percentage changes even though the page count does not. A percentage is a ratio, and the ratio’s denominator here is a number the operator chose. The right question is not “how full is swap” but “do the pages inside it fit back into memory,” and the number that answers that question is not in swap’s output but in memory’s.

How Much It Should Be and What Not to Touch

Swap’s size is not a correctness decision but a budget decision, and it has a cost at both ends. On a system with no swap at all, a process gets terminated the moment demand exceeds physical memory; the failure is hard, fast, and easy to diagnose. On a system with generous swap, the same demand produces no termination, but pages go to disk and every read-back adds a wait; the failure is soft, slow, and hard to diagnose. The choice between the two is made according to which failure is manageable.

There is also swap tendency: the system carries a setting that determines how willing it is to write rarely used pages to swap even without memory pressure. Raising this setting pushes the used-swap indicator above zero even when there is no pressure, and grows the false-alarm column in the table above even further.

The destructive-command boundary applies in two places here. First, the command that formats a partition as swap irreversibly destroys the file system on that partition; if the wrong partition name is written, it runs without a warning. Second, turning off swap while under pressure can lead to process termination. This lesson does not give either command in a runnable, complete form. The safe way to test is verifying the target partition’s identity beforehand, doing the trial with swap built on a file, and attempting the turn-off only at a moment when used swap is below available memory.

Summary

  • Swap can be a partition or a file, and it appears at no mount point; the mechanics of paged memory are not repeated in this course, what is measured is what the operator can see.
  • Real memory pressure in the six-hundred-second window is 84 seconds; even read once every five seconds, the used-swap indicator raises a false alarm for 272 seconds, because a page written to swap stays there until it is read back.
  • Free memory raises a false alarm for 451 seconds, because the cache fills the free space; available memory gives the lowest total at the five-second interval, with 1 miss and 12 false alarms.
  • Swap rate misses 2 seconds and raises 13 false alarms at the same interval, but when the interval rises to sixty, the missed count jumps to 49; at coarse sampling, all four indicators climb past 134.
  • Swap can be safely turned off in 246 of the window’s 600 seconds; the rule that looks at usage percentage is mistaken 44 times at 512 pages, 354 times at 2048 and above — growing the area makes the percentage-based diagnosis worse.

Next Step

Throughout this topic, storage was always something that already existed: a device, partitions on it, file systems inside them. The course’s last lesson returns to the start of the chain and follows a new device joining the system from start to finish — recognition, partitioning, formatting, mounting, making it persistent, and extending it. At every step a tool says “done,” and a layer above is not yet aware of it; what will be measured is how many steps of the chain give a wrong “the work is done” diagnosis. The lesson also closes the course.

To keep your progress and take notes, Log in

My notes

Log in to take notes.

Start typing to search.

↑↓ Esc navigate · open · close