Processer, trådar och resursgränser
Kunna förklara processer, trådar, cgroups och varför en sandlåda kan begränsa CPU och minne.
Intuition
En process har eget minne och egna resurser. En tråd delar minne med andra trådar i samma process.
I Python finns GIL (global interpreter lock): bara en tråd kör Python-bytekod åt gången. Konsekvensen:
| Uppgift | Använd |
|---|---|
| Väntar på nätverk eller disk (I/O) | trådar eller async — GIL släpps under väntan |
| Räknar i Python | processer (multiprocessing) — trådar hjälper inte |
| Räknar i NumPy/PyTorch | trådar fungerar — biblioteken släpper GIL i C-koden |
Det är därför asyncio löser LLM-anrop men inte matrisberäkningar.
Formellt
cgroups (control groups) är Linux-kärnans mekanism för att begränsa vad en grupp processer får använda. Det är vad docker run --cpus 1 --memory 512m faktiskt sätter upp.
| Begränsning | Effekt när den nås |
|---|---|
cpu.max | processen struypas — körs långsammare, dödas inte |
memory.max | OOM-killer dödar processen (exit 137) |
pids.max | fork() misslyckas — stoppar fork-bomber |
io.max | disk-I/O struypas |
Skillnaden är viktig i praktiken: en labbkörning som når CPU-taket blir bara långsam och träffar så småningom sin timeout. En som når minnestaket dödas tvärt med exit 137 — och det felet ser ut som en krasch i användarens kod om man inte känner igen det.
Namespaces kompletterar cgroups genom att isolera vad processen ser: PID (egna processnummer), nätverk (--network none), mount (eget filsystem), user (uid-mappning). cgroups begränsar hur mycket, namespaces begränsar vad.
Tillsammans är det hela grunden för containerisolering — och precis det AI-grafens labbrunner använder (ADR-003).
Kod
import os, multiprocessing, threading, time
def cpu_tung(n=10_000_000):
return sum(i * i for i in range(n))
def mat(fn, n=4):
t0 = time.perf_counter(); fn(n); return round(time.perf_counter() - t0, 2)
def med_tradar(n):
tr = [threading.Thread(target=cpu_tung) for _ in range(n)]
[t.start() for t in tr]; [t.join() for t in tr]
def med_processer(n):
with multiprocessing.Pool(n) as p:
p.map(cpu_tung, [10_000_000] * n)
print("trådar: ", mat(med_tradar)) # 4.1 s — GIL, ingen vinst
print("processer:", mat(med_processer)) # 1.1 s — fyra kärnor
# Se gränserna inifrån en container
cat /sys/fs/cgroup/memory.max # 536870912 (512 MB)
cat /sys/fs/cgroup/cpu.max # 100000 100000 (1 CPU)
# Exit 137 = 128 + 9 (SIGKILL) → OOM-killern tog processen
docker run --rm --memory 64m python:3.12-slim python -c "x = bytearray(200_000_000)"; echo $?
Behärskning innebär
- Förklarar processer, trådar och GIL
- Beskriver hur cgroups begränsar CPU och minne
- Kopplar det till sandlådans säkerhet
Förkunskaper
- DTerminal och shellkrävs
Logga in för att göra övningarna och bygga upp din behärskning.