Fecha: 2026-09-17
HEAD Commit: 5e756e1d38d2e859e954a756fd3c0c58a25aa317
Branch: fix/p0-scheduler-runqueue-correctness
Estado: Diagnóstico Forense Completado y Verificado
Se ha realizado una auditoría forense completa paso a paso del flujo de arranque de NeoDOS desde la entrada del kernel (rust_start) hasta el lanzamiento e inicio de NeoShell (NEOSHELL.NXE).
El objetivo principal fue localizar la causa raíz exacta del comportamiento anómalo observado en el baseline, donde NeoInit entra en un bucle infinito [neoinit] shell exited, respawning... sin que NeoShell llegue a presentar su interfaz ni ejecutar su bucle principal de comandos.
El fallo NO se debe a un error en neoshell.nxe, ni a una corrupción en el binario ELF, ni a una falta de memoria inicial.
La causa raíz reside en el mecanismo de conmutación de contexto durante el retorno de llamadas al sistema en syscall_resched_if_needed (neodos-kernel/src/syscall/mod.rs:361-378):
-
Lanzamiento de NeoShell:
NeoInit(PID 3, Ring 3) ejecutasys_ob_create("\Global\FileSystem\C:\Programs\neoshell.nxe", ObType::Process). El kernel crea el procesoNeoShellen estadoThreadState::Suspended. -
Espera en NeoInit:
NeoInitllama asys_ob_wait(fd)para bloquearse hasta que la shell termine. -
Transición en
handler_ob_wait(syscall/ob.rs:3245-3265):- El hilo de
NeoShellse activa (Suspended→Ready). - El hilo de
NeoInitpasa aThreadState::Blocked { waiting_for: magic }. - Se marca
need_resched = true.
- El hilo de
-
Retorno de Syscall:
syscall_entrydetectaneed_resched == truee invocasyscall_resched_if_needed(). -
Selección del Scheduler: El planificador
schedule()selecciona como candidato anetd(TID 2, Ring 0,cs=0x08). -
Filtro de Ring 0 en Syscall Return:
syscall_resched_if_neededcomprueba si el hilo destino es Ring 0 (next_cs & 3 != 3). Al sernetdun hilo de kernel, determina que no puede conmutar a un hilo de Ring 0 desde un marco de retorno de syscall de usuario. -
Violación de Invariante del Scheduler: Para abortar el cambio de contexto a Ring 0,
syscall_resched_if_neededejecuta:if next_cs & 3 != 3 { ... scheduler.current_tid = tid; if let Some(current) = scheduler.find_kthread_mut(tid) { current.state = ThreadState::Running; // ← SOBREESCRIBE EL ESTADO BLOCKED DE NEOINIT } ... return current_rsp; }
-
Efecto Anómalo: El estado
BlockeddeNeoInites forzosamente sobrescrito y restaurado aRunning, retornando inmediatamente de la syscall con valor0. -
NeoInitrecibe el retorno0creyendo que la shell se ha cerrado, imprime[neoinit] shell exited, respawning...y reintentasys_ob_createcontinuamente. -
Resultado:
NeoShell(que estaba enReady) nunca es seleccionado para ejecutarse, y los reintentos continuos deNeoInitagotan los slots de heap (HEAP_SLOT_USED).
| Fase | Descripción del Hito | Resultado | Evidencia / Detalle Técnico |
|---|---|---|---|
| 0 | Baseline | COMPLETADO | Entorno limpio en fix/p0-scheduler-runqueue-correctness. Bucle de respawn reproducido. |
| 1 | Boot Kernel | PASS | rust_start, GDT, IDT, TSS, Paging identity map (0..4 GiB) inicializados. |
| 2 | Heap & Allocations | PASS | Heap 16 MB @ 0x2400000. AlignedKStack (16 KB) con canary 0xDEADBEEFCAFEBABE. |
| 3 | GDT/IRETQ/KStack | PASS | Frames Ring 0 (CS=0x08, SS=0x10) validados. |
| 4 | Scheduler Invariants | FAIL | Violación: Estado Blocked sobrescrito a Running en syscall_resched_if_needed. |
| 5 | Spawn Kthreads | PASS | Hilos TID 0 (BOOT), TID 1 (IDLE), TID 2 (netd) instanciados. |
| 6 | Netd Subsystem | PASS | NETD_CREATED → NETD_QUEUED → NETD_FIRST_RUN → NETD_ENTRY → NETD_READY. |
| 7 | Tests Kernel | PASS | 711 pruebas de kernel pasadas. |
| 8 | Transición Post-Tests | PASS | Transición hacia servicios, NXL, NEM y usermode completa. |
| 9 | NeoInit | PASS | PID 3 cargado en Ring 3 (CS=0x1b, SS=0x23). Registro DefaultShell leído. |
| 10 | Creación NeoShell | PASS | NEOSHELL_CREATE completa. ELF parseado, slot asignado, thread en Suspended. |
| 11 | Primera Ejecución Shell | FAIL | Transición Ready → Running de NeoShell cancelada por syscall_resched_if_needed. |
| 12 | Inicialización Shell | BLOQUEADO | Imposible alcanzar mientras Phase 11 falle. |
| 13 | NeoShell Ready | BLOQUEADO | Imposible alcanzar mientras Phase 11 falle. |
| 14 | Monitor Stack/Heap | FAIL | Bucle infinito de NeoInit agota slots de heap ([USER] WARN: no free heap slots). |
Para resolver esta falla de arquitectura en el scheduler durante la conmutación desde retornos de syscall:
- Corrección del Filtro de Ring 0 en Syscall Return:
- En
syscall_resched_if_needed, cuandoschedule()selecciona un hilo de Ring 0 (comonetd), el planificador no debe abortar la conmutación devolviendo la ejecución al hilo actual si este estáBlocked. - En su lugar, el planificador debe buscar el siguiente hilo candidato de Ring 3 que esté en estado
Ready(comoNeoShell), o permitir que la conmutación ocurra de manera segura.
- En
- Preservación del Estado
Blocked:- Si no hay ningún hilo de Ring 3 listo para ejecutarse, el hilo actual que se ha bloqueado NO debe ser cambiado a
Running. Debe mantenerse enBlockede invocar el bucle de espera de kernel / idle hasta que ocurra una interrupción de temporizador que reprograme el sistema.
- Si no hay ningún hilo de Ring 3 listo para ejecutarse, el hilo actual que se ha bloqueado NO debe ser cambiado a