Diese Seite dokumentiert das Symmetric-Multiprocessing-Modul (kernel/smp.lyx, WP10): LAPIC-Zugriff, AP-Startup und die leichtgewichtige Task-API für Kernel-Code.
→ Architektur · Sync-Primitive · Kernel-Interna
smp.lyx stellt drei Funktionsgruppen bereit:
Alle Operationen laufen als vmm_op-Aufrufe (mmap mit negativem Protokoll-Marker).
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.
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).
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:
0x80000x8000 und wechselt selbst in den 64-Bit-Long-ModeDie 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:
fn_idx 0 und 1 — für Demos)
// 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