From 1775af99b06b1ed268331979559444ad3df03a90 Mon Sep 17 00:00:00 2001 From: Martin Petr Date: Sun, 23 Nov 2025 18:52:51 +0100 Subject: [PATCH] Add process manager --- src/kernel/kernel.c | 44 ++++- src/os/src/kernel/filesystem/devfs.ts | 3 + src/os/src/kernel/filesystem/fat32.ts | 3 + src/os/src/kernel/filesystem/procfs.ts | 168 ++++++++++++++++++++ src/os/src/kernel/filesystem/sysfs.ts | 3 + src/os/src/kernel/index.ts | 6 +- src/os/src/kernel/modules/app/app.kmod.ts | 111 ++++++++++++- src/os/src/kernel/modules/terminal/input.ts | 9 +- src/os/src/lib/libts/exec.ts | 4 +- src/os/src/oskrnl/app/launcher.ts | 5 +- src/os/src/oskrnl/app/proc.ts | 25 +++ src/os/src/oskrnl/index.ts | 11 +- src/os/src/oskrnl/input/input.ts | 23 ++- src/os/src/types/c/bindings.d.ts | 4 +- src/os/src/types/filesystem/fs.ts | 1 + test-hdd/apps/echo/index.js | 2 +- test-hdd/apps/tysh/index.js | 18 ++- test-hdd/apps/wfkey/index.js | 6 + test-hdd/apps/wfkey/meta.lam | 5 + 19 files changed, 428 insertions(+), 23 deletions(-) create mode 100644 src/os/src/kernel/filesystem/procfs.ts create mode 100644 src/os/src/oskrnl/app/proc.ts create mode 100644 test-hdd/apps/wfkey/index.js create mode 100644 test-hdd/apps/wfkey/meta.lam diff --git a/src/kernel/kernel.c b/src/kernel/kernel.c index 1210087..422da6d 100644 --- a/src/kernel/kernel.c +++ b/src/kernel/kernel.c @@ -146,6 +146,22 @@ duk_ret_t native_word_out(duk_context *ctx) // Global context for IRQ handlers duk_context *global_ctx = NULL; +// Pending heaps to destroy +duk_context *pending_heaps[16]; +int pending_heaps_count = 0; + +void process_pending_heaps() +{ + for (int i = 0; i < pending_heaps_count; i++) + { + if (pending_heaps[i] != NULL) + { + duk_destroy_heap(pending_heaps[i]); + } + } + pending_heaps_count = 0; +} + // JavaScript IRQ wrapper void js_irq_callback(registers_t *r) { @@ -187,6 +203,9 @@ void js_irq_callback(registers_t *r) } duk_pop(global_ctx); // Pop stash + + // Process any pending heaps + process_pending_heaps(); } // Native function to register IRQ from JavaScript @@ -616,10 +635,29 @@ duk_ret_t native_isolated_exec(duk_context *ctx) // duk_destroy_heap(isolated_ctx); // Return success/failure - duk_push_boolean(ctx, success); + // Return the heap pointer as a number (uint32_t) + duk_push_uint(ctx, (duk_uint_t)isolated_ctx); return 1; } +// Native function to destroy an isolated heap +duk_ret_t native_destroy_isolated_heap(duk_context *ctx) +{ + // Get the heap pointer + duk_uint_t heap_ptr = duk_require_uint(ctx, 0); + duk_context *isolated_ctx = (duk_context *)heap_ptr; + + if (isolated_ctx != NULL) + { + if (pending_heaps_count < 16) + { + pending_heaps[pending_heaps_count++] = isolated_ctx; + } + } + + return 0; +} + void kmain() { // Initialize IDT and ISRs @@ -674,6 +712,10 @@ void kmain() duk_push_c_function(ctx, native_isolated_exec, 1); duk_put_global_string(ctx, "$isolatedExec"); + // Register native isolated heap destruction function + duk_push_c_function(ctx, native_destroy_isolated_heap, 1); + duk_put_global_string(ctx, "$destroyIsolatedHeap"); + // Enable interrupts before JavaScript execution __asm__ volatile("sti"); diff --git a/src/os/src/kernel/filesystem/devfs.ts b/src/os/src/kernel/filesystem/devfs.ts index 0cb3e25..e4ae734 100644 --- a/src/os/src/kernel/filesystem/devfs.ts +++ b/src/os/src/kernel/filesystem/devfs.ts @@ -35,6 +35,9 @@ export function devfs_driver(): KFilesystemDriver { stat(path: string): KFilesystemStat | null { return devfs_statEntity(path); }, + rm(path: string): void { + throw new Error("Cannot remove objects in devfs.") + } }; } diff --git a/src/os/src/kernel/filesystem/fat32.ts b/src/os/src/kernel/filesystem/fat32.ts index c043989..fc51444 100644 --- a/src/os/src/kernel/filesystem/fat32.ts +++ b/src/os/src/kernel/filesystem/fat32.ts @@ -75,6 +75,9 @@ export function fat32_driver(driver: any, data: any): KFilesystemDriver { directRead(path: string, count: number, offset: number) { return fat32_directRead(state, path, count, offset); }, + rm(path: string): void { + throw new Error("Cannot remove objects in fat32."); + }, }; } diff --git a/src/os/src/kernel/filesystem/procfs.ts b/src/os/src/kernel/filesystem/procfs.ts new file mode 100644 index 0000000..54b0272 --- /dev/null +++ b/src/os/src/kernel/filesystem/procfs.ts @@ -0,0 +1,168 @@ +import { Logger } from "../../lib/libstd/logger/logger.kmod"; +import { Path } from "../../lib/libstd/path"; +import type { + KFilesystemDriver, + KFilesystemEntity, + KFilesystemMemdata, + KFilesystemStat, +} from "../../types/filesystem/fs"; + +const procfs_data: KFilesystemMemdata = []; + +export function procfs_driver(): KFilesystemDriver { + return { + id: "procfs", + init: procfs_init, + listDir(path: string): KFilesystemEntity[] | null { + return procfs_listDir(path); + }, + readFile(path: string): unknown { + return procfs_readFile(path); + }, + writeFile(path: string, content: unknown): void { + throw new Error("Cannot write files in procfs."); + }, + createFile(path: string, content: unknown): void { + throw new Error("Cannot create files in procfs."); + }, + mkdir(path: string): void { + throw new Error("Cannot make directories in procfs."); + }, + stat(path: string): KFilesystemStat | null { + return procfs_statEntity(path); + }, + rm(path: string): void { + procfs_rm(path); + }, + }; +} + +export function procfs_init(): void { + procfs_data.push({ + name: "root", + path: "/", + type: "folder", + size: 0, + contents: null, + }); +} + +export function procfs_getEntity(path: string): KFilesystemEntity | null { + for (let i = 0; i < procfs_data.length; i++) { + const entity = procfs_data[i]!; + + if (entity.path === path) return entity; + } + + return null; +} + +export function procfs_statEntity(path: string): KFilesystemStat | null { + const entity = procfs_getEntity(path); + if (!entity) return null; + + return { + name: entity.name, + type: entity.type, + size: entity.size, + }; +} + +export function procfs_mkdir(path: string): void { + if (procfs_getEntity(path)) return; + + const parent = Path.dirname(path); + if (!procfs_getEntity(parent)) return; + + procfs_data.push({ + name: Path.filename(path), + path, + type: "folder", + size: 0, + contents: null, + }); +} + +export function procfs_createFile(path: string, content: unknown): void { + if (procfs_getEntity(path)) return; + + const parent = Path.dirname(path); + if (!procfs_getEntity(parent)) return; + + const size = String(content).length; + + procfs_data.push({ + name: Path.filename(path), + path, + type: "file", + size, + contents: content, + }); +} + +export function procfs_writeFile(path: string, content: unknown): void { + const entity = procfs_getEntity(path); + if (!entity) return procfs_createFile(path, content); + + if (entity.type !== "file") return; + + entity.contents = content; + entity.size = String(content).length; +} + +export function procfs_readFile(path: string): unknown { + const entity = procfs_getEntity(path); + if (!entity) return null; + + if (entity.type !== "file") return null; + + return entity.contents; +} + +export function procfs_listDir(path: string): KFilesystemEntity[] | null { + const entity = procfs_getEntity(path); + if (!entity) return null; + + if (entity.type !== "folder") return null; + + const contents: KFilesystemEntity[] = []; + + for (let i = 0; i < procfs_data.length; i++) { + const e = procfs_data[i]!; + + if (Path.dirname(e.path) === path) { + contents.push(e); + } + } + + return contents; +} + +export function procfs_rm(path: string): void { + const entity = procfs_getEntity(path); + if (!entity) return; + + if (entity.type === "file") { + const index = procfs_data.indexOf(entity); + + if (index !== -1) procfs_data.splice(index, 1); + } else if (entity.type === "folder") { + const entitiesToRemove: KFilesystemEntity[] = []; + const pathPrefix = path === "/" ? "/" : path + "/"; + + for (let i = 0; i < procfs_data.length; i++) { + const e = procfs_data[i]!; + + if (e.path === path || e.path.startsWith(pathPrefix)) { + entitiesToRemove.push(e); + } + } + + for (let i = 0; i < entitiesToRemove.length; i++) { + const toRemove = entitiesToRemove[i]!; + const index = procfs_data.indexOf(toRemove); + + if (index !== -1) procfs_data.splice(index, 1); + } + } +} diff --git a/src/os/src/kernel/filesystem/sysfs.ts b/src/os/src/kernel/filesystem/sysfs.ts index 33e98af..7e64437 100644 --- a/src/os/src/kernel/filesystem/sysfs.ts +++ b/src/os/src/kernel/filesystem/sysfs.ts @@ -31,6 +31,9 @@ export function sysfs_driver(): KFilesystemDriver { stat(path: string): KFilesystemStat | null { return sysfs_statEntity(path); }, + rm(path: string): void { + throw new Error("Cannot remove objects in sysfs."); + }, }; } diff --git a/src/os/src/kernel/index.ts b/src/os/src/kernel/index.ts index ab2537c..36bb72b 100644 --- a/src/os/src/kernel/index.ts +++ b/src/os/src/kernel/index.ts @@ -11,6 +11,7 @@ import { } from "./drivers/etc/serial"; import { devfs_driver, devfs_getDevice } from "./filesystem/devfs"; import { fat32_driver } from "./filesystem/fat32"; +import { procfs_driver } from "./filesystem/procfs"; import { sysfs_driver, sysfs_readFile } from "./filesystem/sysfs"; import { kmod_app_init, kmod_app_run } from "./modules/app/app.kmod"; import { kmod_disks_detectDisks } from "./modules/disks/disks.kmod"; @@ -48,6 +49,7 @@ export function kmain() { kmod_filesystem_init(); kmod_filesystem_mount("/sys", sysfs_driver); kmod_filesystem_mount("/dev", devfs_driver); + kmod_filesystem_mount("/proc", procfs_driver); Logger.log("[Kernel] Initializing terminal module..."); kmod_terminal_input_init(); @@ -60,7 +62,7 @@ export function kmain() { const dev = devfs_getDevice("/hda1"); if (!dev) throw new Error("System drive not found"); - + kmod_filesystem_mount("/disk", function () { return fat32_driver(dev.driver, dev.data); }); @@ -72,5 +74,5 @@ export function kmain() { const shell = uiarrtostr(kmod_filesystem_readFile("/disk/ushellc")!); - kmod_app_run(shell); + kmod_app_run(shell, ""); } diff --git a/src/os/src/kernel/modules/app/app.kmod.ts b/src/os/src/kernel/modules/app/app.kmod.ts index 1e1a61a..6cc3fb5 100644 --- a/src/os/src/kernel/modules/app/app.kmod.ts +++ b/src/os/src/kernel/modules/app/app.kmod.ts @@ -9,12 +9,21 @@ import { } from "../filesystem/filesystem.kmod"; import { oskrnl_register } from "../../../oskrnl"; import { getPathEnv } from "../../../lib/sys/env"; +import { + procfs_mkdir, + procfs_readFile, + procfs_rm, + procfs_writeFile, +} from "../../filesystem/procfs"; + +const exitListeners: { [pid: string]: (() => void)[] } = {}; +const heapHandles: { [pid: string]: number } = {}; export function kmod_app_init() { oskrnl_register(); } -export function kmod_app_run(path: string) { +export function kmod_app_run(path: string, args: string) { const appPath = kmod_app_resolve(path); if (!appPath) throw new Error("App not found"); @@ -27,7 +36,15 @@ export function kmod_app_run(path: string) { const entrypoint = Path.join(appPath, meta["app:entrypoint"]); const code = uiarrtostr(kmod_filesystem_readFile(entrypoint)!); - iexec(code); + const pid = kmod_app_proc_generateId().toString(); + + kmod_app_proc_init(pid, meta["app:name"]!, args); + kmod_app_proc_registerHandle(pid, "app"); + const heapHandle = iexec("var __oskrnl_procd_pid = " + pid + ";\n" + code); + if (heapHandle) heapHandles[pid] = heapHandle; + kmod_app_proc_unregisterHandle(pid, "app"); + + return pid; } export function kmod_app_resolve(path: string): string | null { @@ -43,3 +60,93 @@ export function kmod_app_resolve(path: string): string | null { return null; } + +export function kmod_app_proc_generateId(): number { + return Math.floor(Math.random() * 0xffffffff); +} + +export function kmod_app_proc_init(id: string, name: string, args: string) { + const root = "/" + id; + procfs_mkdir(root); + procfs_writeFile(Path.join(root, "name"), name); + procfs_writeFile(Path.join(root, "args"), args); + procfs_writeFile(Path.join(root, "pid"), id); + procfs_writeFile(Path.join(root, "handles"), ""); +} + +export function kmod_app_proc_getArgs(pid: string): string { + const res = procfs_readFile(Path.join("/" + pid, "args")); + return res as string; +} + +export function kmod_app_proc_registerHandle( + pid: string, + handle: string +): string { + const handles = procfs_readFile(Path.join("/" + pid, "handles")) as string; + + const handlesArr = handles == "" ? [] : handles.split(","); + handlesArr.push(handle); + + procfs_writeFile(Path.join("/" + pid, "handles"), handlesArr.join(",")); + + return handle; +} + +export function kmod_app_proc_unregisterHandle( + pid: string, + handle: string +): void { + const handles = procfs_readFile(Path.join("/" + pid, "handles")) as string; + if (handles == "") return; + + const handlesArr = handles.split(","); + handlesArr.splice(handlesArr.indexOf(handle), 1); + + procfs_writeFile(Path.join("/" + pid, "handles"), handlesArr.join(",")); + + if (handlesArr.length < 1) kmod_app_proc_handleExit(pid); +} + +export function kmod_app_proc_generateHandle(): string { + return Math.floor(Math.random() * 0xffffffff).toString(); +} + +export function kmod_app_proc_handleExit(pid: string): void { + procfs_writeFile(Path.join("/", pid + "/handles"), ""); + procfs_rm("/" + pid); + + if (exitListeners[pid]) { + for (let i = 0; i < exitListeners[pid].length; i++) { + exitListeners[pid][i]!(); + } + } + + exitListeners[pid] = []; + delete exitListeners[pid]; + + if (heapHandles[pid]) { + $destroyIsolatedHeap(heapHandles[pid]!); + delete heapHandles[pid]; + } +} + +export function kmod_app_proc_running(pid: string): boolean { + const handles = procfs_readFile(Path.join("/" + pid, "handles")) as string; + + if (handles === null || handles === undefined) return false; + + return handles !== ""; +} + +export function kmod_app_proc_addExitListener( + pid: string, + listener: () => void +): void { + if (!exitListeners[pid]) exitListeners[pid] = []; + exitListeners[pid].push(listener); +} + +export function kmod_app_proc_exit(pid: string): void { + kmod_app_proc_handleExit(pid); +} diff --git a/src/os/src/kernel/modules/terminal/input.ts b/src/os/src/kernel/modules/terminal/input.ts index f10aefd..d0826a8 100644 --- a/src/os/src/kernel/modules/terminal/input.ts +++ b/src/os/src/kernel/modules/terminal/input.ts @@ -8,8 +8,15 @@ export function kmod_terminal_input_init(): void {} export function kmod_terminal_input_onKeyboardInput( handler: (keycode: keyof typeof Keycode) => void -) { +): () => void { kmod_terminal_input_keyboardInputHandlers.push(handler); + + return function () { + const index = kmod_terminal_input_keyboardInputHandlers.indexOf(handler); + if (index !== -1) { + kmod_terminal_input_keyboardInputHandlers.splice(index, 1); + } + }; } export function kmod_terminal_input_handleKeyboardInput( diff --git a/src/os/src/lib/libts/exec.ts b/src/os/src/lib/libts/exec.ts index a26b97c..469759f 100644 --- a/src/os/src/lib/libts/exec.ts +++ b/src/os/src/lib/libts/exec.ts @@ -1,3 +1,3 @@ export function iexec(code: string) { - $isolatedExec(code); -} \ No newline at end of file + return $isolatedExec(code); +} diff --git a/src/os/src/oskrnl/app/launcher.ts b/src/os/src/oskrnl/app/launcher.ts index ef5769d..de02bcc 100644 --- a/src/os/src/oskrnl/app/launcher.ts +++ b/src/os/src/oskrnl/app/launcher.ts @@ -1,8 +1,5 @@ import { kmod_app_run } from "../../kernel/modules/app/app.kmod"; export function oskrnl_app_launcher_run(path: string, args: string) { - if ((globalThis as any).__oskrnl) { - (globalThis as any).__oskrnl.app_args = args; - } - kmod_app_run(path); + return kmod_app_run(path, args); } diff --git a/src/os/src/oskrnl/app/proc.ts b/src/os/src/oskrnl/app/proc.ts new file mode 100644 index 0000000..86ce46b --- /dev/null +++ b/src/os/src/oskrnl/app/proc.ts @@ -0,0 +1,25 @@ +import { + kmod_app_proc_addExitListener, + kmod_app_proc_exit, + kmod_app_proc_getArgs, + kmod_app_proc_running, +} from "../../kernel/modules/app/app.kmod"; + +export function oskrnl_app_proc_getArgs(pid: string): string { + return kmod_app_proc_getArgs(pid); +} + +export function oskrnl_app_proc_addExitListener( + pid: string, + listener: () => void +): void { + kmod_app_proc_addExitListener(pid, listener); +} + +export function oskrnl_app_proc_exit(pid: string): void { + kmod_app_proc_exit(pid); +} + +export function oskrnl_app_proc_running(pid: string): boolean { + return kmod_app_proc_running(pid); +} diff --git a/src/os/src/oskrnl/index.ts b/src/os/src/oskrnl/index.ts index 9280abe..8a44f99 100644 --- a/src/os/src/oskrnl/index.ts +++ b/src/os/src/oskrnl/index.ts @@ -1,13 +1,22 @@ import { oskrnl_app_launcher_run } from "./app/launcher"; +import { + oskrnl_app_proc_addExitListener, + oskrnl_app_proc_exit, + oskrnl_app_proc_getArgs, + oskrnl_app_proc_running, +} from "./app/proc"; import { oskrnl_console_log, oskrnl_console_update } from "./console/console"; import { oskrnl_input_onKeyPress } from "./input/input"; export function oskrnl_register() { (globalThis as any).__oskrnl = { - app_args: null, console_log: oskrnl_console_log, console_update: oskrnl_console_update, input_onKeyPress: oskrnl_input_onKeyPress, app_launcher_run: oskrnl_app_launcher_run, + app_proc_getArgs: oskrnl_app_proc_getArgs, + app_proc_addExitListener: oskrnl_app_proc_addExitListener, + app_proc_exit: oskrnl_app_proc_exit, + app_proc_running: oskrnl_app_proc_running, }; } diff --git a/src/os/src/oskrnl/input/input.ts b/src/os/src/oskrnl/input/input.ts index 94cc0c3..f489316 100644 --- a/src/os/src/oskrnl/input/input.ts +++ b/src/os/src/oskrnl/input/input.ts @@ -1,5 +1,24 @@ +import { + kmod_app_proc_generateHandle, + kmod_app_proc_registerHandle, + kmod_app_proc_running, + kmod_app_proc_addExitListener, +} from "../../kernel/modules/app/app.kmod"; import { kmod_terminal_input_onKeyboardInput } from "../../kernel/modules/terminal/input"; -export function oskrnl_input_onKeyPress(handler: (key: string) => void) { - kmod_terminal_input_onKeyboardInput(handler); +export function oskrnl_input_onKeyPress( + pid: string, + handler: (key: string) => void +) { + kmod_app_proc_registerHandle(pid, kmod_app_proc_generateHandle()); + const removeHandler = kmod_terminal_input_onKeyboardInput(function ( + key: string + ) { + const running = kmod_app_proc_running(pid); + if (!running) return; + + handler(key); + }); + + kmod_app_proc_addExitListener(pid, removeHandler); } diff --git a/src/os/src/types/c/bindings.d.ts b/src/os/src/types/c/bindings.d.ts index 809d847..9b55063 100644 --- a/src/os/src/types/c/bindings.d.ts +++ b/src/os/src/types/c/bindings.d.ts @@ -11,5 +11,5 @@ declare const $irqregister: ( irq: number, handler: (irqNum: number) => void ) => boolean; -declare function $isolatedExec(code: string): void; - +declare function $isolatedExec(code: string): number; +declare function $destroyIsolatedHeap(handle: number): void; diff --git a/src/os/src/types/filesystem/fs.ts b/src/os/src/types/filesystem/fs.ts index b16ff42..a2879c3 100644 --- a/src/os/src/types/filesystem/fs.ts +++ b/src/os/src/types/filesystem/fs.ts @@ -17,6 +17,7 @@ export type KFilesystemDriver = { writeFile(path: string, content: unknown): void; createFile(path: string, content: unknown): void; mkdir(path: string): void; + rm(path: string): void; stat(path: string): KFilesystemStat | null; directRead?( path: string, diff --git a/test-hdd/apps/echo/index.js b/test-hdd/apps/echo/index.js index ba7f8b1..b6d3f17 100644 --- a/test-hdd/apps/echo/index.js +++ b/test-hdd/apps/echo/index.js @@ -1 +1 @@ -__oskrnl.console_log(__oskrnl.app_args); +__oskrnl.console_log(__oskrnl.app_proc_getArgs(__oskrnl_procd_pid)); diff --git a/test-hdd/apps/tysh/index.js b/test-hdd/apps/tysh/index.js index 6a6d8ff..040fe88 100644 --- a/test-hdd/apps/tysh/index.js +++ b/test-hdd/apps/tysh/index.js @@ -1,14 +1,13 @@ __oskrnl.console_log("> "); var input = ""; -var inputActive = true; +var pid = ""; -__oskrnl.input_onKeyPress(function (key) { - if (!inputActive) return; +__oskrnl.input_onKeyPress(__oskrnl_procd_pid, function (key) { + if (pid != "" && __oskrnl.app_proc_running(pid)) return; if (key == "Backspace") input = input.slice(0, -1); else if (key == "Enter") { - inputActive = false; process(); return; } else input += key.toLowerCase(); @@ -19,5 +18,14 @@ __oskrnl.input_onKeyPress(function (key) { function process() { var app = input.split(" ")[0]; var args = input.split(" ").slice(1); - __oskrnl.app_launcher_run(app, args.join(" ")); + pid = __oskrnl.app_launcher_run(app, args.join(" ")); + __oskrnl.app_proc_addExitListener(pid, function () { + input = ""; + __oskrnl.console_log("> "); + }); + + if (!__oskrnl.app_proc_running(pid)) { + input = ""; + __oskrnl.console_log("> "); + } } diff --git a/test-hdd/apps/wfkey/index.js b/test-hdd/apps/wfkey/index.js new file mode 100644 index 0000000..3cc82f2 --- /dev/null +++ b/test-hdd/apps/wfkey/index.js @@ -0,0 +1,6 @@ +__oskrnl.input_onKeyPress(__oskrnl_procd_pid, function (key) { + if (key == __oskrnl.app_proc_getArgs(__oskrnl_procd_pid)[0].toUpperCase()) { + __oskrnl.app_proc_exit(__oskrnl_procd_pid); + return; + } +}); diff --git a/test-hdd/apps/wfkey/meta.lam b/test-hdd/apps/wfkey/meta.lam new file mode 100644 index 0000000..b6b6494 --- /dev/null +++ b/test-hdd/apps/wfkey/meta.lam @@ -0,0 +1,5 @@ +{ + "app:name": "wfkey", + "app:version": "1.0.0", + "app:entrypoint": "index.js" +}