====== Lyx OS – SMP & Task-API ====== Diese Seite dokumentiert das Symmetric-Multiprocessing-Modul (''kernel/smp.lyx'', WP10): LAPIC-Zugriff, AP-Startup und die leichtgewichtige Task-API für Kernel-Code. → [[lyxos:architektur|Architektur]] · [[lyxos:sync|Sync-Primitive]] · [[lyxos:kernel|Kernel-Interna]] ---- ===== 1. Überblick ===== ''smp.lyx'' stellt drei Funktionsgruppen bereit: - **CPU-Erkennung** — Anzahl logischer Prozessoren via CPUID - **LAPIC-Zugriff** — Direkte Lese-/Schreiboperationen auf den Local APIC - **AP-Startup** — Trampoline-Code in Niedrigspeicher schreiben, INIT+SIPI senden - **Task-API** — Leichtgewichtige Aufgaben an APs delegieren; auf Single-Core synchron ausführen Alle Operationen laufen als ''vmm_op''-Aufrufe (''mmap'' mit negativem Protokoll-Marker). ---- ===== 2. CPU-Erkennung ===== var ncpu: int64 := GetCpuCount(); // CPUID — Anzahl logischer Prozessoren var ap_count: int64 := GetApStartedCount(); // Wie viele APs sind hochgefahren? ''GetCpuCount()'' gibt die Anzahl der logischen Kerne zurück, die CPUID meldet. Das schließt Hyper-Threading mit ein. ''GetApStartedCount()'' gibt an, wie viele Application Processors (APs) das Trampoline durchlaufen und sich als bereit gemeldet haben. Auf einem echten Single-Core-System oder wenn der Trampoline-Start fehlschlägt, bleibt der Wert 0. ---- ===== 3. LAPIC-Zugriff ===== var val: int64 := LapicRead(offset); // LAPIC-Register lesen (offset in Bytes) LapicWrite(offset, value); // LAPIC-Register schreiben Der LAPIC ist memory-mapped. Offset-Konstanten folgen dem Intel-Handbuch (z.B. 0x20 = LAPIC-ID, 0x280 = ESR, 0x300 = ICR-Low für IPIs). ---- ===== 4. AP-Startup ===== Der Bootstrap Processor (BSP) schreibt einen kleinen 16-Bit-Trampoline-Stub in den Niedrigspeicher (physisch 0x8000) und schickt dann die INIT+2×SIPI-Sequenz an den Ziel-LAPIC. // Trampoline-Stub schreiben (inkl. CR3/Stack/Entry-Point/GDTR patchen): var stack: int64 := mmap(0, 8192, 3, 34, -1, 0); SmpPrepareTrampoline(stack + 8192); // stack_top = Oberkante des 8-KB-Stacks // INIT + 2× SIPI an AP mit LAPIC-ID 1 senden: SmpStartAp(1); // Warten bis AP sich meldet (Busy-Wait mit Timeout): var wait: int64 := 0; while (GetApStartedCount() == 0 && wait < 5000000) { wait := wait + 1; } var ready: int64 := GetApStartedCount(); **Was ''SmpPrepareTrampoline'' macht:** * Kopiert Trampoline-Code nach physisch ''0x8000'' * Patcht CR3 (aus aktuellem Kernel-CR3), Stack-Top, Kernel-Entry-Point und GDTR in den Stub * Nach dem ersten SIPI läuft der AP im 16-Bit-Real-Mode in ''0x8000'' und wechselt selbst in den 64-Bit-Long-Mode ---- ===== 5. Task-API ===== Die Task-API ermöglicht es, einfache Berechnungsaufgaben an APs zu delegieren. Es gibt 16 Task-Slots; jeder Slot hat einen Status (FREE/PENDING/RUNNING/DONE) und ein Ergebnis-Feld. // Task erzeugen (fn_idx: 0=square(arg), 1=sum(1..arg)): var t0: int64 := TaskSpawn(0, 7); // square(7) → erwartet 49 var t1: int64 := TaskSpawn(1, 10); // sum(10) → erwartet 55 // Single-Core-Fallback (kein AP verfügbar): if (GetApStartedCount() == 0) { TaskRunPending(); // BSP führt alle ausstehenden Tasks synchron aus } // Auf Abschluss warten und Ergebnis holen: var r0: int64 := TaskAwait(t0); // spinnt auf DONE, gibt TaskGetResult zurück var r1: int64 := TaskAwait(t1); **Funktionen im Detail:** ^ Funktion ^ Beschreibung ^ | ''TaskSpawn(fn_idx, arg)'' | Task anlegen; gibt ''task_id'' (0–15) oder −1 (keine freien Slots) zurück | | ''TaskPoll(task_id)'' | Status lesen: 0=FREE, 1=PENDING, 2=RUNNING, 3=DONE | | ''TaskGetResult(task_id)'' | Ergebnis holen (nur gültig wenn Status = DONE) | | ''TaskAwait(task_id)'' | Kombination aus ''TaskPoll'' + ''TaskGetResult''; spinnt bis DONE | | ''TaskRunPending()'' | BSP-Fallback: alle PENDING-Tasks sequenziell ausführen | **Einschränkungen:** * Nur zwei vordefinierte Task-Funktionen (''fn_idx'' 0 und 1 — für Demos) * Maximal 16 gleichzeitige Task-Slots * Kein Scheduling zwischen Tasks — ein Task läuft auf einem AP bis zum Ende durch * Erweiterung auf beliebige Funktionen ist für spätere Work-Packages geplant ---- ===== 6. Typischer Ablauf (Multi-Core-Boot) ===== // In kernel.lyx main(): // 1. Trampoline schreiben und AP hochfahren: var ap_stack: int64 := mmap(0, 8192, 3, 34, -1, 0); SmpPrepareTrampoline(ap_stack + 8192); SmpStartAp(1); // 2. Auf AP warten (max. ~5 Mio. Iterationen): var wait: int64 := 0; while (GetApStartedCount() == 0 && wait < 5000000) { wait := wait + 1; } // 3. Tasks spawnen: var t0: int64 := TaskSpawn(0, 7); var t1: int64 := TaskSpawn(1, 10); // 4. Single-Core-Fallback: if (GetApStartedCount() == 0) { TaskRunPending(); } // 5. Ergebnisse abwarten: PrintLn(IntToStr(TaskAwait(t0))); // "49" PrintLn(IntToStr(TaskAwait(t1))); // "55" Letzte Aktualisierung: 2026-06-13