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 viaLOCK XCHG; dreht sich so lange, bis der alte Wert 0 warSpinlockRelease(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
