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.
Förkunskaper
- DTerminal och shellkrävs
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
Logga in för att göra övningarna och bygga upp din behärskning.