Lyx OS – Sync-Primitive

Diese Seite dokumentiert die Synchronisations-Primitive in kernel/sync.lyx (WP9): Spinlock, Mutex und Semaphor. Alle Primitive sind für Kernel-Code (Ring-0) konzipiert.

Architektur · SMP & Tasks · Kernel-Interna


1. Überblick

sync.lyx stellt drei Arten von Synchronisations-Primitiven bereit:

Primitiv Einsatz Implementierung
Spinlock Kurze kritische Abschnitte, Unterbrechungsschutz Aktives Warten via LOCK XCHG (vmm_op8)
Mutex Längere Abschnitte, schlafen erlaubt Kernel-verwaltet via vmm_op9–11, optionaler Timeout
Semaphor Producer-Consumer, Ressourcenzähler Kernel-verwaltet via vmm_op12–14, Zähler konfigurierbar

2. Spinlock

Ein Spinlock ist ein einfacher busy-wait-Lock. Er eignet sich für sehr kurze kritische Abschnitte, z.B. Schutz von Print-Ausgaben über mehrere Prozessoren.

// Adresse des system-weiten Print-Spinlocks holen (einmal, beim Start):
var print_lock: int64 := GetPrintSpinlockAddr();   // → vmm_op15

// Kritischen Abschnitt schützen:
SpinlockAcquire(print_lock);
PrintLn("Atomar ausgegeben");
SpinlockRelease(print_lock);

Eigenschaften:

  • SpinlockAcquire(lock_ptr) — schreibt 1 in die Lock-Variable via LOCK XCHG; dreht sich so lange, bis der alte Wert 0 war
  • SpinlockRelease(lock_ptr) — schreibt 0 (poke64(ptr, 0))
  • Kein Schlaf, kein Scheduling-Eingriff — nur für sehr kurze Abschnitte geeignet
  • GetPrintSpinlockAddr() gibt eine kernel-weite Lock-Adresse zurück, die alle Kernel-Threads und APs gemeinsam nutzen

3. Mutex

Ein Mutex ist ein schläfender Lock. Der Kernel schläft wartende Threads ein und weckt sie nach dem Unlock wieder auf.

// Mutex anlegen:
var mx: int64 := MutexCreate(MUTEX_PLAIN);   // MUTEX_PLAIN=0, MUTEX_RECURSIVE=1

// Lock mit unbegrenztem Timeout:
var err: int64 := MutexLock(mx);

// Lock mit Timeout (Nanosekunden):
var err: int64 := MutexLockTimeout(mx, 1000000);  // 1 ms

// Unlock:
MutexUnlock(mx);

Konstante Wert Bedeutung
MUTEX_PLAIN 0 Normaler Mutex — kein rekursives Lock erlaubt
MUTEX_RECURSIVE 1 Rekursiver Mutex — selber Thread darf mehrfach locken

Hinweis: MutexCreate gibt eine Integer-ID zurück (kein Zeiger). Die eigentliche Mutex-Struktur liegt im Kernel-Verwaltungsbereich und ist nicht direkt zugänglich.


4. Semaphor

Ein Semaphor verwaltet einen ganzzahligen Zähler. SemWait dekrementiert (und schläft, wenn Zähler = 0). SemPost inkrementiert und weckt einen wartenden Thread auf.

// Semaphor anlegen (initial count = 2):
var sem: int64 := SemCreate(2, 0);   // flags: 0 = anonym, SEM_NAMED=1 = benannt

SemWait(sem);   // Zähler −1; schläft wenn Zähler = 0
SemWait(sem);   // nochmal −1

SemPost(sem);   // Zähler +1; weckt ggf. wartenden Thread auf

Typischer Einsatz — Producer/Consumer:

var sem_full:  int64 := SemCreate(0, 0);   // Elemente im Puffer
var sem_empty: int64 := SemCreate(8, 0);   // Freie Plätze (Puffergröße = 8)

// Producer:
SemWait(sem_empty);
// ... in Puffer schreiben ...
SemPost(sem_full);

// Consumer:
SemWait(sem_full);
// ... aus Puffer lesen ...
SemPost(sem_empty);


5. Wann welches Primitiv?

Situation Empfehlung
Print-Ausgaben serialisieren (Multi-Core) SpinlockAcquire / Release auf GetPrintSpinlockAddr()
Gemeinsame Datenstruktur schützen (kurz) Spinlock
Gemeinsame Datenstruktur schützen (länger) Mutex mit MUTEX_PLAIN
Initialisierung einmalig sicherstellen Mutex: vor Init locken, danach unlocked lassen
Ressourcenzähler (n Slots verfügbar) Semaphor
Producer-Consumer-Puffer Zwei Semaphore (sem_full + sem_empty)

Letzte Aktualisierung: 2026-06-13