From f93af4a2a627fd08e246eca666fac7654ccff6ca Mon Sep 17 00:00:00 2001 From: Leonmmcoset Date: Fri, 17 Apr 2026 17:07:19 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E5=A5=BD=E7=9A=84=E8=BF=9B=E7=A8=8B?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 1 + cleonos/c/apps/bg_main.c | 96 +++++++++++ cleonos/c/apps/fg_main.c | 163 +++++++++++++++++++ cleonos/c/apps/help_main.c | 6 +- cleonos/c/apps/jobs_main.c | 142 +++++++++++++++++ cleonos/c/apps/kill_main.c | 94 +++++++++++ cleonos/c/apps/ps_main.c | 191 ++++++++++++++++++++++ cleonos/c/apps/shell/shell_cmd.c | 6 +- cleonos/c/apps/top_main.c | 236 ++++++++++++++++++++++++++++ cleonos/c/include/cleonos_syscall.h | 31 ++++ cleonos/c/src/syscall.c | 16 ++ clks/include/clks/exec.h | 28 ++++ clks/include/clks/syscall.h | 4 + clks/kernel/exec.c | 189 ++++++++++++++++++++++ clks/kernel/log.c | 136 ++++++++++++---- clks/kernel/syscall.c | 46 ++++++ docs/syscall.md | 35 ++++- 17 files changed, 1385 insertions(+), 35 deletions(-) create mode 100644 cleonos/c/apps/bg_main.c create mode 100644 cleonos/c/apps/fg_main.c create mode 100644 cleonos/c/apps/jobs_main.c create mode 100644 cleonos/c/apps/kill_main.c create mode 100644 cleonos/c/apps/ps_main.c create mode 100644 cleonos/c/apps/top_main.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 63db3a8..df85f28 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -362,6 +362,7 @@ set(RAMDISK_ROOT_APPS) set(USER_SHELL_COMMAND_APPS help args ls cat grep head tail wc cut uniq sort pwd cd exec pid spawn wait sleep yield + bg fg jobs kill ps top shutdown restart exit clear ansi ansitest wavplay fastfetch memstat fsstat taskstat userstat shstat stats tty dmesg kbdstat mkdir touch write append cp mv rm ) diff --git a/cleonos/c/apps/bg_main.c b/cleonos/c/apps/bg_main.c new file mode 100644 index 0000000..22ab2f9 --- /dev/null +++ b/cleonos/c/apps/bg_main.c @@ -0,0 +1,96 @@ +#include "cmd_runtime.h" + +static int ush_cmd_bg(const ush_state *sh, const char *arg) { + char target[USH_PATH_MAX]; + char argv_line[USH_ARG_MAX]; + char env_line[USH_PATH_MAX + 32ULL]; + const char *rest = ""; + char path[USH_PATH_MAX]; + u64 pid; + + if (sh == (const ush_state *)0 || arg == (const char *)0 || arg[0] == '\0') { + ush_writeln("bg: usage bg [args...]"); + return 0; + } + + if (ush_split_first_and_rest(arg, target, (u64)sizeof(target), &rest) == 0) { + ush_writeln("bg: usage bg [args...]"); + return 0; + } + + argv_line[0] = '\0'; + if (rest != (const char *)0 && rest[0] != '\0') { + ush_copy(argv_line, (u64)sizeof(argv_line), rest); + } + + if (ush_resolve_exec_path(sh, target, path, (u64)sizeof(path)) == 0) { + ush_writeln("bg: invalid target"); + return 0; + } + + if (ush_path_is_under_system(path) != 0) { + ush_writeln("bg: /system/*.elf is kernel-mode (KELF), not user-exec"); + return 0; + } + + env_line[0] = '\0'; + ush_copy(env_line, (u64)sizeof(env_line), "PWD="); + ush_copy(env_line + 4, (u64)(sizeof(env_line) - 4ULL), sh->cwd); + + pid = cleonos_sys_spawn_pathv(path, argv_line, env_line); + + if (pid == (u64)-1) { + ush_writeln("bg: request failed"); + return 0; + } + + ush_write("["); + ush_write_hex_u64(pid); + ush_write("] "); + ush_writeln(path); + return 1; +} + +int cleonos_app_main(void) { + ush_cmd_ctx ctx; + ush_cmd_ret ret; + ush_state sh; + char initial_cwd[USH_PATH_MAX]; + int has_context = 0; + int success = 0; + const char *arg = ""; + + ush_zero(&ctx, (u64)sizeof(ctx)); + ush_zero(&ret, (u64)sizeof(ret)); + ush_init_state(&sh); + ush_copy(initial_cwd, (u64)sizeof(initial_cwd), sh.cwd); + + if (ush_command_ctx_read(&ctx) != 0) { + if (ctx.cmd[0] != '\0' && ush_streq(ctx.cmd, "bg") != 0) { + has_context = 1; + arg = ctx.arg; + if (ctx.cwd[0] == '/') { + ush_copy(sh.cwd, (u64)sizeof(sh.cwd), ctx.cwd); + ush_copy(initial_cwd, (u64)sizeof(initial_cwd), sh.cwd); + } + } + } + + success = ush_cmd_bg(&sh, arg); + + if (has_context != 0) { + if (ush_streq(sh.cwd, initial_cwd) == 0) { + ret.flags |= USH_CMD_RET_FLAG_CWD; + ush_copy(ret.cwd, (u64)sizeof(ret.cwd), sh.cwd); + } + + if (sh.exit_requested != 0) { + ret.flags |= USH_CMD_RET_FLAG_EXIT; + ret.exit_code = sh.exit_code; + } + + (void)ush_command_ret_write(&ret); + } + + return (success != 0) ? 0 : 1; +} diff --git a/cleonos/c/apps/fg_main.c b/cleonos/c/apps/fg_main.c new file mode 100644 index 0000000..466e5d1 --- /dev/null +++ b/cleonos/c/apps/fg_main.c @@ -0,0 +1,163 @@ +#include "cmd_runtime.h" + +static int ush_fg_is_user_path(const char *path) { + if (path == (const char *)0 || path[0] != '/') { + return 0; + } + + if (path[1] == 's' && path[2] == 'y' && path[3] == 's' && path[4] == 't' && path[5] == 'e' && path[6] == 'm' && + (path[7] == '/' || path[7] == '\0')) { + return 0; + } + + if (path[1] == 'd' && path[2] == 'r' && path[3] == 'i' && path[4] == 'v' && path[5] == 'e' && path[6] == 'r' && + (path[7] == '/' || path[7] == '\0')) { + return 0; + } + + return 1; +} + +static int ush_fg_pick_latest_job(u64 *out_pid) { + u64 proc_count; + u64 tty_active; + u64 i; + u64 best = 0ULL; + + if (out_pid == (u64 *)0) { + return 0; + } + + *out_pid = 0ULL; + proc_count = cleonos_sys_proc_count(); + tty_active = cleonos_sys_tty_active(); + + for (i = 0ULL; i < proc_count; i++) { + u64 pid = 0ULL; + cleonos_proc_snapshot snap; + + if (cleonos_sys_proc_pid_at(i, &pid) == 0ULL || pid == 0ULL) { + continue; + } + + if (cleonos_sys_proc_snapshot(pid, &snap, (u64)sizeof(snap)) == 0ULL) { + continue; + } + + if (snap.tty_index != tty_active) { + continue; + } + + if (ush_fg_is_user_path(snap.path) == 0) { + continue; + } + + if (snap.state != CLEONOS_PROC_STATE_PENDING && snap.state != CLEONOS_PROC_STATE_RUNNING) { + continue; + } + + if (best == 0ULL || snap.pid > best) { + best = snap.pid; + } + } + + if (best == 0ULL) { + return 0; + } + + *out_pid = best; + return 1; +} + +static int ush_fg_wait_pid(u64 pid) { + for (;;) { + u64 status = (u64)-1; + u64 wait_ret = cleonos_sys_wait_pid(pid, &status); + + if (wait_ret == (u64)-1) { + ush_writeln("fg: pid not found"); + return 0; + } + + if (wait_ret == 1ULL) { + ush_write("fg: done ["); + ush_write_hex_u64(pid); + ush_writeln("]"); + if ((status & (1ULL << 63)) != 0ULL) { + ush_print_kv_hex(" SIGNAL", status & 0xFFULL); + ush_print_kv_hex(" VECTOR", (status >> 8) & 0xFFULL); + ush_print_kv_hex(" ERROR", (status >> 16) & 0xFFFFULL); + } else { + ush_print_kv_hex(" STATUS", status); + } + return 1; + } + + (void)cleonos_sys_sleep_ticks(1ULL); + } +} + +static int ush_cmd_fg(const char *arg) { + u64 pid = 0ULL; + + if (arg != (const char *)0 && arg[0] != '\0') { + if (ush_parse_u64_dec(arg, &pid) == 0 || pid == 0ULL) { + ush_writeln("fg: usage fg [pid]"); + return 0; + } + } else { + if (ush_fg_pick_latest_job(&pid) == 0) { + ush_writeln("fg: no active background job"); + return 0; + } + } + + ush_write("fg: waiting ["); + ush_write_hex_u64(pid); + ush_writeln("]"); + return ush_fg_wait_pid(pid); +} + +int cleonos_app_main(void) { + ush_cmd_ctx ctx; + ush_cmd_ret ret; + ush_state sh; + char initial_cwd[USH_PATH_MAX]; + int has_context = 0; + int success = 0; + const char *arg = ""; + + ush_zero(&ctx, (u64)sizeof(ctx)); + ush_zero(&ret, (u64)sizeof(ret)); + ush_init_state(&sh); + ush_copy(initial_cwd, (u64)sizeof(initial_cwd), sh.cwd); + + if (ush_command_ctx_read(&ctx) != 0) { + if (ctx.cmd[0] != '\0' && ush_streq(ctx.cmd, "fg") != 0) { + has_context = 1; + arg = ctx.arg; + if (ctx.cwd[0] == '/') { + ush_copy(sh.cwd, (u64)sizeof(sh.cwd), ctx.cwd); + ush_copy(initial_cwd, (u64)sizeof(initial_cwd), sh.cwd); + } + } + } + + success = ush_cmd_fg(arg); + + if (has_context != 0) { + if (ush_streq(sh.cwd, initial_cwd) == 0) { + ret.flags |= USH_CMD_RET_FLAG_CWD; + ush_copy(ret.cwd, (u64)sizeof(ret.cwd), sh.cwd); + } + + if (sh.exit_requested != 0) { + ret.flags |= USH_CMD_RET_FLAG_EXIT; + ret.exit_code = sh.exit_code; + } + + (void)ush_command_ret_write(&ret); + } + + return (success != 0) ? 0 : 1; +} diff --git a/cleonos/c/apps/help_main.c b/cleonos/c/apps/help_main.c index 2537d50..2e2aeb1 100644 --- a/cleonos/c/apps/help_main.c +++ b/cleonos/c/apps/help_main.c @@ -27,8 +27,10 @@ static int ush_cmd_help(void) { ush_writeln(" mv (/temp only)"); ush_writeln(" rm (/temp only)"); ush_writeln(" pid"); - ush_writeln(" spawn [args...]"); - ush_writeln(" wait "); + ush_writeln(" spawn [args...] / bg [args...]"); + ush_writeln(" wait / fg [pid]"); + ush_writeln(" kill [signal]"); + ush_writeln(" jobs [-a] / ps [-a] [-u] / top [--once] [-n loops] [-d ticks]"); ush_writeln(" sleep "); ush_writeln(" yield"); ush_writeln(" shutdown / restart"); diff --git a/cleonos/c/apps/jobs_main.c b/cleonos/c/apps/jobs_main.c new file mode 100644 index 0000000..287f96e --- /dev/null +++ b/cleonos/c/apps/jobs_main.c @@ -0,0 +1,142 @@ +#include "cmd_runtime.h" + +static int ush_jobs_is_user_path(const char *path) { + if (path == (const char *)0 || path[0] != '/') { + return 0; + } + + if (path[1] == 's' && path[2] == 'y' && path[3] == 's' && path[4] == 't' && path[5] == 'e' && path[6] == 'm' && + (path[7] == '/' || path[7] == '\0')) { + return 0; + } + + if (path[1] == 'd' && path[2] == 'r' && path[3] == 'i' && path[4] == 'v' && path[5] == 'e' && path[6] == 'r' && + (path[7] == '/' || path[7] == '\0')) { + return 0; + } + + return 1; +} + +static const char *ush_jobs_state_name(u64 state) { + if (state == CLEONOS_PROC_STATE_PENDING) { + return "PENDING"; + } + if (state == CLEONOS_PROC_STATE_RUNNING) { + return "RUNNING"; + } + if (state == CLEONOS_PROC_STATE_EXITED) { + return "EXITED "; + } + return "UNUSED "; +} + +static int ush_cmd_jobs(const char *arg) { + u64 proc_count; + u64 tty_active = cleonos_sys_tty_active(); + u64 i; + u64 shown = 0ULL; + int include_exited = 0; + + if (arg != (const char *)0 && arg[0] != '\0') { + if (ush_streq(arg, "-a") != 0 || ush_streq(arg, "--all") != 0) { + include_exited = 1; + } else { + ush_writeln("jobs: usage jobs [-a|--all]"); + return 0; + } + } + + proc_count = cleonos_sys_proc_count(); + ush_writeln("jobs:"); + + for (i = 0ULL; i < proc_count; i++) { + u64 pid = 0ULL; + cleonos_proc_snapshot snap; + const char *state_name; + + if (cleonos_sys_proc_pid_at(i, &pid) == 0ULL || pid == 0ULL) { + continue; + } + + if (cleonos_sys_proc_snapshot(pid, &snap, (u64)sizeof(snap)) == 0ULL) { + continue; + } + + if (ush_jobs_is_user_path(snap.path) == 0) { + continue; + } + + if (snap.tty_index != tty_active) { + continue; + } + + if (include_exited == 0 && snap.state == CLEONOS_PROC_STATE_EXITED) { + continue; + } + + state_name = ush_jobs_state_name(snap.state); + ush_write("["); + ush_write_hex_u64(snap.pid); + ush_write("] "); + ush_write(state_name); + ush_write(" "); + ush_write(snap.path); + if (snap.state == CLEONOS_PROC_STATE_EXITED) { + ush_write(" status="); + ush_write_hex_u64(snap.exit_status); + } + ush_write_char('\n'); + shown++; + } + + if (shown == 0ULL) { + ush_writeln("(no jobs)"); + } + + return 1; +} + +int cleonos_app_main(void) { + ush_cmd_ctx ctx; + ush_cmd_ret ret; + ush_state sh; + char initial_cwd[USH_PATH_MAX]; + int has_context = 0; + int success = 0; + const char *arg = ""; + + ush_zero(&ctx, (u64)sizeof(ctx)); + ush_zero(&ret, (u64)sizeof(ret)); + ush_init_state(&sh); + ush_copy(initial_cwd, (u64)sizeof(initial_cwd), sh.cwd); + + if (ush_command_ctx_read(&ctx) != 0) { + if (ctx.cmd[0] != '\0' && ush_streq(ctx.cmd, "jobs") != 0) { + has_context = 1; + arg = ctx.arg; + if (ctx.cwd[0] == '/') { + ush_copy(sh.cwd, (u64)sizeof(sh.cwd), ctx.cwd); + ush_copy(initial_cwd, (u64)sizeof(initial_cwd), sh.cwd); + } + } + } + + success = ush_cmd_jobs(arg); + + if (has_context != 0) { + if (ush_streq(sh.cwd, initial_cwd) == 0) { + ret.flags |= USH_CMD_RET_FLAG_CWD; + ush_copy(ret.cwd, (u64)sizeof(ret.cwd), sh.cwd); + } + + if (sh.exit_requested != 0) { + ret.flags |= USH_CMD_RET_FLAG_EXIT; + ret.exit_code = sh.exit_code; + } + + (void)ush_command_ret_write(&ret); + } + + return (success != 0) ? 0 : 1; +} diff --git a/cleonos/c/apps/kill_main.c b/cleonos/c/apps/kill_main.c new file mode 100644 index 0000000..c888c8b --- /dev/null +++ b/cleonos/c/apps/kill_main.c @@ -0,0 +1,94 @@ +#include "cmd_runtime.h" + +static int ush_cmd_kill(const char *arg) { + char pid_text[USH_PATH_MAX]; + const char *rest = ""; + u64 pid; + u64 signal = 15ULL; + u64 ret; + + if (arg == (const char *)0 || arg[0] == '\0') { + ush_writeln("kill: usage kill [signal]"); + return 0; + } + + if (ush_split_first_and_rest(arg, pid_text, (u64)sizeof(pid_text), &rest) == 0) { + ush_writeln("kill: usage kill [signal]"); + return 0; + } + + if (ush_parse_u64_dec(pid_text, &pid) == 0 || pid == 0ULL) { + ush_writeln("kill: invalid pid"); + return 0; + } + + if (rest != (const char *)0 && rest[0] != '\0') { + if (ush_parse_u64_dec(rest, &signal) == 0 || signal > 255ULL) { + ush_writeln("kill: invalid signal"); + return 0; + } + } + + ret = cleonos_sys_proc_kill(pid, signal); + + if (ret == (u64)-1) { + ush_writeln("kill: pid not found"); + return 0; + } + + if (ret == 0ULL) { + ush_writeln("kill: target cannot be terminated right now"); + return 0; + } + + ush_write("kill: sent signal "); + ush_write_hex_u64(signal); + ush_write(" to "); + ush_write_hex_u64(pid); + ush_write_char('\n'); + return 1; +} + +int cleonos_app_main(void) { + ush_cmd_ctx ctx; + ush_cmd_ret ret; + ush_state sh; + char initial_cwd[USH_PATH_MAX]; + int has_context = 0; + int success = 0; + const char *arg = ""; + + ush_zero(&ctx, (u64)sizeof(ctx)); + ush_zero(&ret, (u64)sizeof(ret)); + ush_init_state(&sh); + ush_copy(initial_cwd, (u64)sizeof(initial_cwd), sh.cwd); + + if (ush_command_ctx_read(&ctx) != 0) { + if (ctx.cmd[0] != '\0' && ush_streq(ctx.cmd, "kill") != 0) { + has_context = 1; + arg = ctx.arg; + if (ctx.cwd[0] == '/') { + ush_copy(sh.cwd, (u64)sizeof(sh.cwd), ctx.cwd); + ush_copy(initial_cwd, (u64)sizeof(initial_cwd), sh.cwd); + } + } + } + + success = ush_cmd_kill(arg); + + if (has_context != 0) { + if (ush_streq(sh.cwd, initial_cwd) == 0) { + ret.flags |= USH_CMD_RET_FLAG_CWD; + ush_copy(ret.cwd, (u64)sizeof(ret.cwd), sh.cwd); + } + + if (sh.exit_requested != 0) { + ret.flags |= USH_CMD_RET_FLAG_EXIT; + ret.exit_code = sh.exit_code; + } + + (void)ush_command_ret_write(&ret); + } + + return (success != 0) ? 0 : 1; +} diff --git a/cleonos/c/apps/ps_main.c b/cleonos/c/apps/ps_main.c new file mode 100644 index 0000000..2831a3d --- /dev/null +++ b/cleonos/c/apps/ps_main.c @@ -0,0 +1,191 @@ +#include "cmd_runtime.h" + +static int ush_ps_is_user_path(const char *path) { + if (path == (const char *)0 || path[0] != '/') { + return 0; + } + + if (path[1] == 's' && path[2] == 'y' && path[3] == 's' && path[4] == 't' && path[5] == 'e' && path[6] == 'm' && + (path[7] == '/' || path[7] == '\0')) { + return 0; + } + + if (path[1] == 'd' && path[2] == 'r' && path[3] == 'i' && path[4] == 'v' && path[5] == 'e' && path[6] == 'r' && + (path[7] == '/' || path[7] == '\0')) { + return 0; + } + + return 1; +} + +static const char *ush_ps_state_name(u64 state) { + if (state == CLEONOS_PROC_STATE_PENDING) { + return "PEND"; + } + if (state == CLEONOS_PROC_STATE_RUNNING) { + return "RUN "; + } + if (state == CLEONOS_PROC_STATE_EXITED) { + return "EXIT"; + } + return "UNKN"; +} + +static int ush_ps_next_token(const char **io_cursor, char *out, u64 out_size) { + const char *p; + u64 n = 0ULL; + + if (io_cursor == (const char **)0 || out == (char *)0 || out_size == 0ULL) { + return 0; + } + + out[0] = '\0'; + p = *io_cursor; + + if (p == (const char *)0) { + return 0; + } + + while (*p != '\0' && ush_is_space(*p) != 0) { + p++; + } + + if (*p == '\0') { + *io_cursor = p; + return 0; + } + + while (*p != '\0' && ush_is_space(*p) == 0) { + if (n + 1ULL < out_size) { + out[n++] = *p; + } + p++; + } + + out[n] = '\0'; + *io_cursor = p; + return 1; +} + +static void ush_ps_print_one(const cleonos_proc_snapshot *snap) { + if (snap == (const cleonos_proc_snapshot *)0) { + return; + } + + ush_write("PID="); + ush_write_hex_u64(snap->pid); + ush_write(" PPID="); + ush_write_hex_u64(snap->ppid); + ush_write(" ST="); + ush_write(ush_ps_state_name(snap->state)); + ush_write(" TTY="); + ush_write_hex_u64(snap->tty_index); + ush_write(" RT="); + ush_write_hex_u64(snap->runtime_ticks); + ush_write(" MEM="); + ush_write_hex_u64(snap->mem_bytes); + if (snap->state == CLEONOS_PROC_STATE_EXITED) { + ush_write(" EXIT="); + ush_write_hex_u64(snap->exit_status); + } + ush_write(" PATH="); + ush_writeln(snap->path); +} + +static int ush_cmd_ps(const char *arg) { + u64 proc_count; + u64 i; + u64 shown = 0ULL; + int include_exited = 0; + int only_user = 0; + const char *cursor = arg; + char token[USH_PATH_MAX]; + + while (ush_ps_next_token(&cursor, token, (u64)sizeof(token)) != 0) { + if (ush_streq(token, "-a") != 0 || ush_streq(token, "--all") != 0) { + include_exited = 1; + } else if (ush_streq(token, "-u") != 0 || ush_streq(token, "--user") != 0) { + only_user = 1; + } else { + ush_writeln("ps: usage ps [-a|--all] [-u|--user]"); + return 0; + } + } + + proc_count = cleonos_sys_proc_count(); + ush_writeln("ps:"); + + for (i = 0ULL; i < proc_count; i++) { + u64 pid = 0ULL; + cleonos_proc_snapshot snap; + + if (cleonos_sys_proc_pid_at(i, &pid) == 0ULL || pid == 0ULL) { + continue; + } + + if (cleonos_sys_proc_snapshot(pid, &snap, (u64)sizeof(snap)) == 0ULL) { + continue; + } + + if (include_exited == 0 && snap.state == CLEONOS_PROC_STATE_EXITED) { + continue; + } + + if (only_user != 0 && ush_ps_is_user_path(snap.path) == 0) { + continue; + } + + ush_ps_print_one(&snap); + shown++; + } + + if (shown == 0ULL) { + ush_writeln("(no process)"); + } + + return 1; +} + +int cleonos_app_main(void) { + ush_cmd_ctx ctx; + ush_cmd_ret ret; + ush_state sh; + char initial_cwd[USH_PATH_MAX]; + int has_context = 0; + int success = 0; + const char *arg = ""; + + ush_zero(&ctx, (u64)sizeof(ctx)); + ush_zero(&ret, (u64)sizeof(ret)); + ush_init_state(&sh); + ush_copy(initial_cwd, (u64)sizeof(initial_cwd), sh.cwd); + + if (ush_command_ctx_read(&ctx) != 0) { + if (ctx.cmd[0] != '\0' && ush_streq(ctx.cmd, "ps") != 0) { + has_context = 1; + arg = ctx.arg; + if (ctx.cwd[0] == '/') { + ush_copy(sh.cwd, (u64)sizeof(sh.cwd), ctx.cwd); + ush_copy(initial_cwd, (u64)sizeof(initial_cwd), sh.cwd); + } + } + } + + success = ush_cmd_ps(arg); + + if (has_context != 0) { + if (ush_streq(sh.cwd, initial_cwd) == 0) { + ret.flags |= USH_CMD_RET_FLAG_CWD; + ush_copy(ret.cwd, (u64)sizeof(ret.cwd), sh.cwd); + } + + if (sh.exit_requested != 0) { + ret.flags |= USH_CMD_RET_FLAG_EXIT; + ret.exit_code = sh.exit_code; + } + + (void)ush_command_ret_write(&ret); + } + + return (success != 0) ? 0 : 1; +} diff --git a/cleonos/c/apps/shell/shell_cmd.c b/cleonos/c/apps/shell/shell_cmd.c index fcba14e..00a266e 100644 --- a/cleonos/c/apps/shell/shell_cmd.c +++ b/cleonos/c/apps/shell/shell_cmd.c @@ -152,8 +152,10 @@ static int ush_cmd_help(void) { ush_writeln(" mv (/temp only)"); ush_writeln(" rm (/temp only)"); ush_writeln(" pid"); - ush_writeln(" spawn [args...]"); - ush_writeln(" wait "); + ush_writeln(" spawn [args...] / bg [args...]"); + ush_writeln(" wait / fg [pid]"); + ush_writeln(" kill [signal]"); + ush_writeln(" jobs [-a] / ps [-a] [-u] / top [--once] [-n loops] [-d ticks]"); ush_writeln(" sleep "); ush_writeln(" yield"); ush_writeln(" shutdown / restart"); diff --git a/cleonos/c/apps/top_main.c b/cleonos/c/apps/top_main.c new file mode 100644 index 0000000..6cf4526 --- /dev/null +++ b/cleonos/c/apps/top_main.c @@ -0,0 +1,236 @@ +#include "cmd_runtime.h" + +static const char *ush_top_state_name(u64 state) { + if (state == CLEONOS_PROC_STATE_PENDING) { + return "PEND"; + } + if (state == CLEONOS_PROC_STATE_RUNNING) { + return "RUN "; + } + if (state == CLEONOS_PROC_STATE_EXITED) { + return "EXIT"; + } + return "UNKN"; +} + +static int ush_top_next_token(const char **io_cursor, char *out, u64 out_size) { + const char *p; + u64 n = 0ULL; + + if (io_cursor == (const char **)0 || out == (char *)0 || out_size == 0ULL) { + return 0; + } + + out[0] = '\0'; + p = *io_cursor; + + if (p == (const char *)0) { + return 0; + } + + while (*p != '\0' && ush_is_space(*p) != 0) { + p++; + } + + if (*p == '\0') { + *io_cursor = p; + return 0; + } + + while (*p != '\0' && ush_is_space(*p) == 0) { + if (n + 1ULL < out_size) { + out[n++] = *p; + } + p++; + } + + out[n] = '\0'; + *io_cursor = p; + return 1; +} + +static int ush_top_parse(const char *arg, u64 *out_loops, u64 *out_delay) { + const char *cursor = arg; + char token[USH_PATH_MAX]; + u64 loops = 0ULL; + u64 delay = 5ULL; + + if (out_loops == (u64 *)0 || out_delay == (u64 *)0) { + return 0; + } + + while (ush_top_next_token(&cursor, token, (u64)sizeof(token)) != 0) { + if (ush_streq(token, "--once") != 0) { + loops = 1ULL; + continue; + } + + if (ush_streq(token, "-n") != 0) { + if (ush_top_next_token(&cursor, token, (u64)sizeof(token)) == 0 || ush_parse_u64_dec(token, &loops) == 0) { + return 0; + } + continue; + } + + if (ush_streq(token, "-d") != 0) { + if (ush_top_next_token(&cursor, token, (u64)sizeof(token)) == 0 || ush_parse_u64_dec(token, &delay) == 0) { + return 0; + } + continue; + } + + return 0; + } + + *out_loops = loops; + *out_delay = delay; + return 1; +} + +static void ush_top_render_frame(u64 frame_index, u64 delay_ticks) { + u64 proc_count = cleonos_sys_proc_count(); + u64 i; + u64 shown = 0ULL; + + ush_write("\x1B[2J\x1B[H"); + ush_write("top frame="); + ush_write_hex_u64(frame_index); + ush_write(" ticks="); + ush_write_hex_u64(cleonos_sys_timer_ticks()); + ush_write(" delay="); + ush_write_hex_u64(delay_ticks); + ush_write_char('\n'); + ush_writeln("PID ST TTY RTICKS MEM PATH"); + + for (i = 0ULL; i < proc_count; i++) { + u64 pid = 0ULL; + cleonos_proc_snapshot snap; + + if (cleonos_sys_proc_pid_at(i, &pid) == 0ULL || pid == 0ULL) { + continue; + } + + if (cleonos_sys_proc_snapshot(pid, &snap, (u64)sizeof(snap)) == 0ULL) { + continue; + } + + if (snap.state != CLEONOS_PROC_STATE_PENDING && snap.state != CLEONOS_PROC_STATE_RUNNING) { + continue; + } + + ush_write_hex_u64(snap.pid); + ush_write(" "); + ush_write(ush_top_state_name(snap.state)); + ush_write(" "); + ush_write_hex_u64(snap.tty_index); + ush_write(" "); + ush_write_hex_u64(snap.runtime_ticks); + ush_write(" "); + ush_write_hex_u64(snap.mem_bytes); + ush_write(" "); + ush_writeln(snap.path); + shown++; + } + + if (shown == 0ULL) { + ush_writeln("(no active process)"); + } + + ush_writeln(""); + ush_writeln("press q to quit"); +} + +static int ush_top_sleep_or_quit(u64 delay_ticks) { + u64 i; + + if (delay_ticks == 0ULL) { + delay_ticks = 1ULL; + } + + for (i = 0ULL; i < delay_ticks; i++) { + u64 ch = cleonos_sys_kbd_get_char(); + + if (ch != (u64)-1) { + char c = (char)(ch & 0xFFULL); + + if (c == 'q' || c == 'Q') { + return 1; + } + } + + (void)cleonos_sys_sleep_ticks(1ULL); + } + + return 0; +} + +static int ush_cmd_top(const char *arg) { + u64 loops; + u64 delay_ticks; + u64 frame = 0ULL; + + if (ush_top_parse(arg, &loops, &delay_ticks) == 0) { + ush_writeln("top: usage top [--once] [-n loops] [-d ticks]"); + return 0; + } + + for (;;) { + frame++; + ush_top_render_frame(frame, delay_ticks); + + if (loops != 0ULL && frame >= loops) { + break; + } + + if (ush_top_sleep_or_quit(delay_ticks) != 0) { + break; + } + } + + ush_write("\x1B[0m"); + return 1; +} + +int cleonos_app_main(void) { + ush_cmd_ctx ctx; + ush_cmd_ret ret; + ush_state sh; + char initial_cwd[USH_PATH_MAX]; + int has_context = 0; + int success = 0; + const char *arg = ""; + + ush_zero(&ctx, (u64)sizeof(ctx)); + ush_zero(&ret, (u64)sizeof(ret)); + ush_init_state(&sh); + ush_copy(initial_cwd, (u64)sizeof(initial_cwd), sh.cwd); + + if (ush_command_ctx_read(&ctx) != 0) { + if (ctx.cmd[0] != '\0' && ush_streq(ctx.cmd, "top") != 0) { + has_context = 1; + arg = ctx.arg; + if (ctx.cwd[0] == '/') { + ush_copy(sh.cwd, (u64)sizeof(sh.cwd), ctx.cwd); + ush_copy(initial_cwd, (u64)sizeof(initial_cwd), sh.cwd); + } + } + } + + success = ush_cmd_top(arg); + + if (has_context != 0) { + if (ush_streq(sh.cwd, initial_cwd) == 0) { + ret.flags |= USH_CMD_RET_FLAG_CWD; + ush_copy(ret.cwd, (u64)sizeof(ret.cwd), sh.cwd); + } + + if (sh.exit_requested != 0) { + ret.flags |= USH_CMD_RET_FLAG_EXIT; + ret.exit_code = sh.exit_code; + } + + (void)ush_command_ret_write(&ret); + } + + return (success != 0) ? 0 : 1; +} diff --git a/cleonos/c/include/cleonos_syscall.h b/cleonos/c/include/cleonos_syscall.h index af379ea..bcad742 100644 --- a/cleonos/c/include/cleonos_syscall.h +++ b/cleonos/c/include/cleonos_syscall.h @@ -5,6 +5,29 @@ typedef unsigned long long u64; typedef unsigned long long usize; #define CLEONOS_FS_NAME_MAX 96ULL +#define CLEONOS_PROC_PATH_MAX 192ULL + +#define CLEONOS_PROC_STATE_UNUSED 0ULL +#define CLEONOS_PROC_STATE_PENDING 1ULL +#define CLEONOS_PROC_STATE_RUNNING 2ULL +#define CLEONOS_PROC_STATE_EXITED 3ULL + +typedef struct cleonos_proc_snapshot { + u64 pid; + u64 ppid; + u64 state; + u64 started_tick; + u64 exited_tick; + u64 exit_status; + u64 runtime_ticks; + u64 mem_bytes; + u64 tty_index; + u64 last_signal; + u64 last_fault_vector; + u64 last_fault_error; + u64 last_fault_rip; + char path[CLEONOS_PROC_PATH_MAX]; +} cleonos_proc_snapshot; #define CLEONOS_SYSCALL_LOG_WRITE 0ULL #define CLEONOS_SYSCALL_TIMER_TICKS 1ULL @@ -67,6 +90,10 @@ typedef unsigned long long usize; #define CLEONOS_SYSCALL_PROC_FAULT_VECTOR 58ULL #define CLEONOS_SYSCALL_PROC_FAULT_ERROR 59ULL #define CLEONOS_SYSCALL_PROC_FAULT_RIP 60ULL +#define CLEONOS_SYSCALL_PROC_COUNT 61ULL +#define CLEONOS_SYSCALL_PROC_PID_AT 62ULL +#define CLEONOS_SYSCALL_PROC_SNAPSHOT 63ULL +#define CLEONOS_SYSCALL_PROC_KILL 64ULL u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); u64 cleonos_sys_log_write(const char *message, u64 length); @@ -129,5 +156,9 @@ u64 cleonos_sys_proc_last_signal(void); u64 cleonos_sys_proc_fault_vector(void); u64 cleonos_sys_proc_fault_error(void); u64 cleonos_sys_proc_fault_rip(void); +u64 cleonos_sys_proc_count(void); +u64 cleonos_sys_proc_pid_at(u64 index, u64 *out_pid); +u64 cleonos_sys_proc_snapshot(u64 pid, cleonos_proc_snapshot *out_snapshot, u64 out_size); +u64 cleonos_sys_proc_kill(u64 pid, u64 signal); #endif diff --git a/cleonos/c/src/syscall.c b/cleonos/c/src/syscall.c index ce7e254..880a733 100644 --- a/cleonos/c/src/syscall.c +++ b/cleonos/c/src/syscall.c @@ -252,3 +252,19 @@ u64 cleonos_sys_proc_fault_error(void) { u64 cleonos_sys_proc_fault_rip(void) { return cleonos_syscall(CLEONOS_SYSCALL_PROC_FAULT_RIP, 0ULL, 0ULL, 0ULL); } + +u64 cleonos_sys_proc_count(void) { + return cleonos_syscall(CLEONOS_SYSCALL_PROC_COUNT, 0ULL, 0ULL, 0ULL); +} + +u64 cleonos_sys_proc_pid_at(u64 index, u64 *out_pid) { + return cleonos_syscall(CLEONOS_SYSCALL_PROC_PID_AT, index, (u64)out_pid, 0ULL); +} + +u64 cleonos_sys_proc_snapshot(u64 pid, cleonos_proc_snapshot *out_snapshot, u64 out_size) { + return cleonos_syscall(CLEONOS_SYSCALL_PROC_SNAPSHOT, pid, (u64)out_snapshot, out_size); +} + +u64 cleonos_sys_proc_kill(u64 pid, u64 signal) { + return cleonos_syscall(CLEONOS_SYSCALL_PROC_KILL, pid, signal, 0ULL); +} diff --git a/clks/include/clks/exec.h b/clks/include/clks/exec.h index 0374a06..3e72dd3 100644 --- a/clks/include/clks/exec.h +++ b/clks/include/clks/exec.h @@ -3,6 +3,30 @@ #include +#define CLKS_EXEC_PROC_PATH_MAX 192U + +#define CLKS_EXEC_PROC_STATE_UNUSED 0ULL +#define CLKS_EXEC_PROC_STATE_PENDING 1ULL +#define CLKS_EXEC_PROC_STATE_RUNNING 2ULL +#define CLKS_EXEC_PROC_STATE_EXITED 3ULL + +struct clks_exec_proc_snapshot { + u64 pid; + u64 ppid; + u64 state; + u64 started_tick; + u64 exited_tick; + u64 exit_status; + u64 runtime_ticks; + u64 mem_bytes; + u64 tty_index; + u64 last_signal; + u64 last_fault_vector; + u64 last_fault_error; + u64 last_fault_rip; + char path[CLKS_EXEC_PROC_PATH_MAX]; +}; + void clks_exec_init(void); clks_bool clks_exec_run_path(const char *path, u64 *out_status); clks_bool clks_exec_run_pathv(const char *path, const char *argv_line, const char *env_line, u64 *out_status); @@ -20,6 +44,10 @@ u64 clks_exec_current_signal(void); u64 clks_exec_current_fault_vector(void); u64 clks_exec_current_fault_error(void); u64 clks_exec_current_fault_rip(void); +u64 clks_exec_proc_count(void); +clks_bool clks_exec_proc_pid_at(u64 index, u64 *out_pid); +clks_bool clks_exec_proc_snapshot(u64 pid, struct clks_exec_proc_snapshot *out_snapshot); +u64 clks_exec_proc_kill(u64 pid, u64 signal); clks_bool clks_exec_handle_exception(u64 vector, u64 error_code, u64 rip, diff --git a/clks/include/clks/syscall.h b/clks/include/clks/syscall.h index a3db00b..362c97a 100644 --- a/clks/include/clks/syscall.h +++ b/clks/include/clks/syscall.h @@ -64,6 +64,10 @@ #define CLKS_SYSCALL_PROC_FAULT_VECTOR 58ULL #define CLKS_SYSCALL_PROC_FAULT_ERROR 59ULL #define CLKS_SYSCALL_PROC_FAULT_RIP 60ULL +#define CLKS_SYSCALL_PROC_COUNT 61ULL +#define CLKS_SYSCALL_PROC_PID_AT 62ULL +#define CLKS_SYSCALL_PROC_SNAPSHOT 63ULL +#define CLKS_SYSCALL_PROC_KILL 64ULL void clks_syscall_init(void); u64 clks_syscall_dispatch(void *frame_ptr); diff --git a/clks/kernel/exec.c b/clks/kernel/exec.c index b62da73..b11fb99 100644 --- a/clks/kernel/exec.c +++ b/clks/kernel/exec.c @@ -22,6 +22,7 @@ typedef u64 (*clks_exec_entry_fn)(void); #define CLKS_EXEC_MAX_ENVS 24U #define CLKS_EXEC_ITEM_MAX 128U #define CLKS_EXEC_STATUS_SIGNAL_FLAG (1ULL << 63) +#define CLKS_EXEC_DEFAULT_KILL_SIGNAL 15ULL enum clks_exec_proc_state { CLKS_EXEC_PROC_UNUSED = 0, @@ -39,6 +40,7 @@ struct clks_exec_proc_record { u64 exited_tick; u64 exit_status; u32 tty_index; + u64 image_mem_bytes; char path[CLKS_EXEC_PATH_MAX]; char argv_line[CLKS_EXEC_ARG_LINE_MAX]; char env_line[CLKS_EXEC_ENV_LINE_MAX]; @@ -413,6 +415,7 @@ static struct clks_exec_proc_record *clks_exec_prepare_proc_record(i32 slot, proc->exited_tick = 0ULL; proc->exit_status = (u64)-1; proc->tty_index = clks_tty_active(); + proc->image_mem_bytes = 0ULL; clks_exec_copy_path(proc->path, sizeof(proc->path), path); clks_exec_copy_line(proc->argv_line, sizeof(proc->argv_line), argv_line); clks_exec_copy_line(proc->env_line, sizeof(proc->env_line), env_line); @@ -544,6 +547,8 @@ static clks_bool clks_exec_run_proc_slot(i32 slot, u64 *out_status) { goto fail; } + proc->image_mem_bytes = info.total_load_memsz; + if (clks_elf64_load(image, image_size, &loaded) == CLKS_FALSE) { clks_log(CLKS_LOG_WARN, "EXEC", "EXEC ELF LOAD FAILED"); clks_log(CLKS_LOG_WARN, "EXEC", proc->path); @@ -958,6 +963,190 @@ u64 clks_exec_current_fault_rip(void) { return (proc != CLKS_NULL) ? proc->last_fault_rip : 0ULL; } +static u64 clks_exec_proc_runtime_ticks(const struct clks_exec_proc_record *proc, u64 now_tick) { + if (proc == CLKS_NULL || proc->started_tick == 0ULL) { + return 0ULL; + } + + if (proc->state == CLKS_EXEC_PROC_RUNNING) { + if (now_tick <= proc->started_tick) { + return 0ULL; + } + + return now_tick - proc->started_tick; + } + + if (proc->state == CLKS_EXEC_PROC_EXITED) { + if (proc->exited_tick <= proc->started_tick) { + return 0ULL; + } + + return proc->exited_tick - proc->started_tick; + } + + return 0ULL; +} + +static u64 clks_exec_proc_memory_bytes(const struct clks_exec_proc_record *proc) { + u64 mem = 0ULL; + + if (proc == CLKS_NULL) { + return 0ULL; + } + + mem += proc->image_mem_bytes; + + if (proc->state == CLKS_EXEC_PROC_RUNNING) { + mem += CLKS_EXEC_RUN_STACK_BYTES; + } + + return mem; +} + +u64 clks_exec_proc_count(void) { + u64 count = 0ULL; + u32 i; + + for (i = 0U; i < CLKS_EXEC_MAX_PROCS; i++) { + if (clks_exec_proc_table[i].used == CLKS_TRUE) { + count++; + } + } + + return count; +} + +clks_bool clks_exec_proc_pid_at(u64 index, u64 *out_pid) { + u64 pos = 0ULL; + u32 i; + + if (out_pid != CLKS_NULL) { + *out_pid = 0ULL; + } + + if (out_pid == CLKS_NULL) { + return CLKS_FALSE; + } + + for (i = 0U; i < CLKS_EXEC_MAX_PROCS; i++) { + if (clks_exec_proc_table[i].used != CLKS_TRUE) { + continue; + } + + if (pos == index) { + *out_pid = clks_exec_proc_table[i].pid; + return CLKS_TRUE; + } + + pos++; + } + + return CLKS_FALSE; +} + +clks_bool clks_exec_proc_snapshot(u64 pid, struct clks_exec_proc_snapshot *out_snapshot) { + i32 slot; + const struct clks_exec_proc_record *proc; + u64 now_tick; + + if (out_snapshot == CLKS_NULL) { + return CLKS_FALSE; + } + + slot = clks_exec_proc_find_slot_by_pid(pid); + + if (slot < 0) { + return CLKS_FALSE; + } + + proc = &clks_exec_proc_table[(u32)slot]; + + if (proc->used != CLKS_TRUE) { + return CLKS_FALSE; + } + + now_tick = clks_interrupts_timer_ticks(); + clks_memset(out_snapshot, 0, sizeof(*out_snapshot)); + + out_snapshot->pid = proc->pid; + out_snapshot->ppid = proc->ppid; + out_snapshot->state = (u64)proc->state; + out_snapshot->started_tick = proc->started_tick; + out_snapshot->exited_tick = proc->exited_tick; + out_snapshot->exit_status = proc->exit_status; + out_snapshot->runtime_ticks = clks_exec_proc_runtime_ticks(proc, now_tick); + out_snapshot->mem_bytes = clks_exec_proc_memory_bytes(proc); + out_snapshot->tty_index = (u64)proc->tty_index; + out_snapshot->last_signal = proc->last_signal; + out_snapshot->last_fault_vector = proc->last_fault_vector; + out_snapshot->last_fault_error = proc->last_fault_error; + out_snapshot->last_fault_rip = proc->last_fault_rip; + clks_exec_copy_path(out_snapshot->path, sizeof(out_snapshot->path), proc->path); + + return CLKS_TRUE; +} + +u64 clks_exec_proc_kill(u64 pid, u64 signal) { + i32 slot; + struct clks_exec_proc_record *proc; + u64 effective_signal; + u64 status; + u64 now_tick; + + if (pid == 0ULL) { + return (u64)-1; + } + + slot = clks_exec_proc_find_slot_by_pid(pid); + + if (slot < 0) { + return (u64)-1; + } + + proc = &clks_exec_proc_table[(u32)slot]; + + if (proc->used != CLKS_TRUE) { + return (u64)-1; + } + + effective_signal = (signal == 0ULL) ? CLKS_EXEC_DEFAULT_KILL_SIGNAL : (signal & 0xFFULL); + status = clks_exec_encode_signal_status(effective_signal, 0ULL, 0ULL); + now_tick = clks_interrupts_timer_ticks(); + + if (proc->state == CLKS_EXEC_PROC_EXITED) { + return 1ULL; + } + + if (proc->state == CLKS_EXEC_PROC_PENDING) { + proc->state = CLKS_EXEC_PROC_EXITED; + proc->exit_status = status; + proc->exited_tick = now_tick; + proc->last_signal = effective_signal; + proc->last_fault_vector = 0ULL; + proc->last_fault_error = 0ULL; + proc->last_fault_rip = 0ULL; + return 1ULL; + } + + if (proc->state == CLKS_EXEC_PROC_RUNNING) { + i32 depth_index = clks_exec_current_depth_index(); + + if (depth_index < 0 || clks_exec_current_pid() != pid) { + return 0ULL; + } + + proc->last_signal = effective_signal; + proc->last_fault_vector = 0ULL; + proc->last_fault_error = 0ULL; + proc->last_fault_rip = 0ULL; + clks_exec_exit_requested_stack[(u32)depth_index] = CLKS_TRUE; + clks_exec_exit_status_stack[(u32)depth_index] = status; + return 1ULL; + } + + return 0ULL; +} + clks_bool clks_exec_handle_exception(u64 vector, u64 error_code, u64 rip, diff --git a/clks/kernel/log.c b/clks/kernel/log.c index 65b66c7..36ae16d 100644 --- a/clks/kernel/log.c +++ b/clks/kernel/log.c @@ -6,6 +6,8 @@ #define CLKS_LOG_LINE_MAX 256 #define CLKS_LOG_JOURNAL_CAP 256 +#define CLKS_LOG_ANSI_RESET "\x1B[0m" + static char clks_log_journal[CLKS_LOG_JOURNAL_CAP][CLKS_LOG_LINE_MAX]; static u32 clks_log_journal_head = 0U; static u32 clks_log_journal_count_live = 0U; @@ -37,6 +39,10 @@ static void clks_log_append_char(char *buffer, usize *cursor, char ch) { static void clks_log_append_text(char *buffer, usize *cursor, const char *text) { usize i = 0; + if (text == CLKS_NULL) { + return; + } + while (text[i] != '\0') { clks_log_append_char(buffer, cursor, text[i]); i++; @@ -83,51 +89,121 @@ static void clks_log_journal_push(const char *line) { } } -static void clks_log_emit_line(const char *line) { +static const char *clks_log_level_ansi(enum clks_log_level level) { + switch (level) { + case CLKS_LOG_DEBUG: + return "\x1B[38;5;110m"; + case CLKS_LOG_INFO: + return "\x1B[38;5;120m"; + case CLKS_LOG_WARN: + return "\x1B[1;38;5;220m"; + case CLKS_LOG_ERROR: + return "\x1B[1;38;5;203m"; + default: + return "\x1B[38;5;250m"; + } +} + +static const char *clks_log_tag_ansi(const char *tag) { + static const char *palette[] = { + "\x1B[38;5;81m", + "\x1B[38;5;117m", + "\x1B[38;5;159m", + "\x1B[38;5;45m", + "\x1B[38;5;75m", + "\x1B[38;5;141m", + "\x1B[38;5;214m", + "\x1B[38;5;168m", + }; + u32 hash = 5381U; + usize i = 0U; + usize palette_count = sizeof(palette) / sizeof(palette[0]); + + if (tag == CLKS_NULL || tag[0] == '\0') { + return palette[0]; + } + + while (tag[i] != '\0') { + hash = ((hash << 5U) + hash) ^ (u32)(u8)tag[i]; + i++; + } + + return palette[hash % (u32)palette_count]; +} + +static void clks_log_emit_tty_colored(enum clks_log_level level, const char *tag, const char *message) { + const char *safe_tag = (tag == CLKS_NULL) ? "LOG" : tag; + const char *safe_message = (message == CLKS_NULL) ? "" : message; + + clks_tty_write(clks_log_level_ansi(level)); + clks_tty_write("["); + clks_tty_write(clks_log_level_name(level)); + clks_tty_write("]"); + + clks_tty_write(clks_log_tag_ansi(safe_tag)); + clks_tty_write("["); + clks_tty_write(safe_tag); + clks_tty_write("]"); + + clks_tty_write(CLKS_LOG_ANSI_RESET); + clks_tty_write(" "); + clks_tty_write(safe_message); + clks_tty_write("\n"); +} + +static void clks_log_build_line(enum clks_log_level level, const char *tag, const char *message, char *line) { + const char *safe_tag = (tag == CLKS_NULL) ? "LOG" : tag; + const char *safe_message = (message == CLKS_NULL) ? "" : message; + usize cursor = 0U; + + if (line == CLKS_NULL) { + return; + } + + clks_log_append_char(line, &cursor, '['); + clks_log_append_text(line, &cursor, clks_log_level_name(level)); + clks_log_append_char(line, &cursor, ']'); + clks_log_append_char(line, &cursor, '['); + clks_log_append_text(line, &cursor, safe_tag); + clks_log_append_char(line, &cursor, ']'); + clks_log_append_char(line, &cursor, ' '); + clks_log_append_text(line, &cursor, safe_message); + line[cursor] = '\0'; +} + +static void clks_log_emit_line(enum clks_log_level level, const char *tag, const char *message, const char *line) { + if (line == CLKS_NULL) { + return; + } + clks_log_journal_push(line); clks_serial_write(line); clks_serial_write("\n"); - clks_tty_write(line); - clks_tty_write("\n"); + clks_log_emit_tty_colored(level, tag, message); } void clks_log(enum clks_log_level level, const char *tag, const char *message) { char line[CLKS_LOG_LINE_MAX]; - usize cursor = 0; - clks_log_append_char(line, &cursor, '['); - clks_log_append_text(line, &cursor, clks_log_level_name(level)); - clks_log_append_char(line, &cursor, ']'); - clks_log_append_char(line, &cursor, '['); - clks_log_append_text(line, &cursor, tag); - clks_log_append_char(line, &cursor, ']'); - clks_log_append_char(line, &cursor, ' '); - clks_log_append_text(line, &cursor, message); - line[cursor] = '\0'; - - clks_log_emit_line(line); + clks_log_build_line(level, tag, message, line); + clks_log_emit_line(level, tag, message, line); } void clks_log_hex(enum clks_log_level level, const char *tag, const char *label, u64 value) { + char message[CLKS_LOG_LINE_MAX]; char line[CLKS_LOG_LINE_MAX]; - usize cursor = 0; + usize cursor = 0U; - clks_log_append_char(line, &cursor, '['); - clks_log_append_text(line, &cursor, clks_log_level_name(level)); - clks_log_append_char(line, &cursor, ']'); - clks_log_append_char(line, &cursor, '['); - clks_log_append_text(line, &cursor, tag); - clks_log_append_char(line, &cursor, ']'); - clks_log_append_char(line, &cursor, ' '); - clks_log_append_text(line, &cursor, label); - clks_log_append_char(line, &cursor, ':'); - clks_log_append_char(line, &cursor, ' '); - clks_log_append_hex_u64(line, &cursor, value); - line[cursor] = '\0'; + clks_log_append_text(message, &cursor, (label == CLKS_NULL) ? "VALUE" : label); + clks_log_append_char(message, &cursor, ':'); + clks_log_append_char(message, &cursor, ' '); + clks_log_append_hex_u64(message, &cursor, value); + message[cursor] = '\0'; - clks_log_emit_line(line); + clks_log_build_line(level, tag, message, line); + clks_log_emit_line(level, tag, message, line); } u64 clks_log_journal_count(void) { @@ -153,4 +229,4 @@ clks_bool clks_log_journal_read(u64 index_from_oldest, char *out_line, usize out clks_log_journal_copy_line(out_line, out_line_size, clks_log_journal[slot]); return CLKS_TRUE; -} \ No newline at end of file +} diff --git a/clks/kernel/syscall.c b/clks/kernel/syscall.c index 624c580..eda072e 100644 --- a/clks/kernel/syscall.c +++ b/clks/kernel/syscall.c @@ -362,6 +362,44 @@ static u64 clks_syscall_proc_fault_rip(void) { return clks_exec_current_fault_rip(); } +static u64 clks_syscall_proc_count(void) { + return clks_exec_proc_count(); +} + +static u64 clks_syscall_proc_pid_at(u64 arg0, u64 arg1) { + u64 pid = 0ULL; + + if (arg1 == 0ULL) { + return 0ULL; + } + + if (clks_exec_proc_pid_at(arg0, &pid) == CLKS_FALSE) { + return 0ULL; + } + + clks_memcpy((void *)arg1, &pid, sizeof(pid)); + return 1ULL; +} + +static u64 clks_syscall_proc_snapshot(u64 arg0, u64 arg1, u64 arg2) { + struct clks_exec_proc_snapshot snap; + + if (arg1 == 0ULL || arg2 < (u64)sizeof(snap)) { + return 0ULL; + } + + if (clks_exec_proc_snapshot(arg0, &snap) == CLKS_FALSE) { + return 0ULL; + } + + clks_memcpy((void *)arg1, &snap, sizeof(snap)); + return 1ULL; +} + +static u64 clks_syscall_proc_kill(u64 arg0, u64 arg1) { + return clks_exec_proc_kill(arg0, arg1); +} + static u64 clks_syscall_exit(u64 arg0) { return (clks_exec_request_exit(arg0) == CLKS_TRUE) ? 1ULL : 0ULL; } @@ -723,6 +761,14 @@ u64 clks_syscall_dispatch(void *frame_ptr) { return clks_syscall_proc_fault_error(); case CLKS_SYSCALL_PROC_FAULT_RIP: return clks_syscall_proc_fault_rip(); + case CLKS_SYSCALL_PROC_COUNT: + return clks_syscall_proc_count(); + case CLKS_SYSCALL_PROC_PID_AT: + return clks_syscall_proc_pid_at(frame->rbx, frame->rcx); + case CLKS_SYSCALL_PROC_SNAPSHOT: + return clks_syscall_proc_snapshot(frame->rbx, frame->rcx, frame->rdx); + case CLKS_SYSCALL_PROC_KILL: + return clks_syscall_proc_kill(frame->rbx, frame->rcx); case CLKS_SYSCALL_EXIT: return clks_syscall_exit(frame->rbx); case CLKS_SYSCALL_SLEEP_TICKS: diff --git a/docs/syscall.md b/docs/syscall.md index 031b13f..50c847b 100644 --- a/docs/syscall.md +++ b/docs/syscall.md @@ -60,7 +60,7 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); - `FS_MKDIR` / `FS_WRITE` / `FS_APPEND` / `FS_REMOVE` 仅允许 `/temp` 树下路径。 -## 4. Syscall 列表(0~60) +## 4. Syscall 列表(0~64) ### 0 `CLEONOS_SYSCALL_LOG_WRITE` @@ -446,6 +446,38 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); - 参数:无 - 返回:当前进程最近一次 CPU 异常 RIP(无则 `0`) +### 61 `CLEONOS_SYSCALL_PROC_COUNT` + +- 参数:无 +- 返回:当前进程表中已使用槽位数量 + +### 62 `CLEONOS_SYSCALL_PROC_PID_AT` + +- 参数: +- `arg0`: `u64 index` +- `arg1`: `u64 *out_pid` +- 返回:成功 `1`,失败 `0` +- 说明:用于枚举进程;`index` 超出范围返回 `0`。 + +### 63 `CLEONOS_SYSCALL_PROC_SNAPSHOT` + +- 参数: +- `arg0`: `u64 pid` +- `arg1`: `struct cleonos_proc_snapshot *out_snapshot` +- `arg2`: `u64 out_size`(需 `>= sizeof(cleonos_proc_snapshot)`) +- 返回:成功 `1`,失败 `0` +- 说明:返回 PID/PPID/状态/运行 tick/内存估算/TTY/路径等快照信息。 + +### 64 `CLEONOS_SYSCALL_PROC_KILL` + +- 参数: +- `arg0`: `u64 pid` +- `arg1`: `u64 signal`(`0` 时按 `SIGTERM(15)` 处理) +- 返回: +- `1`:请求成功 +- `0`:当前不可终止(例如非当前上下文中的 running 进程) +- `-1`:PID 不存在 + ## 5. 用户态封装函数 用户态封装位于: @@ -467,6 +499,7 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); - `cleonos_sys_audio_available()` / `cleonos_sys_audio_play_tone()` / `cleonos_sys_audio_stop()` - `cleonos_sys_proc_argc()` / `cleonos_sys_proc_argv()` / `cleonos_sys_proc_envc()` / `cleonos_sys_proc_env()` - `cleonos_sys_proc_last_signal()` / `cleonos_sys_proc_fault_vector()` / `cleonos_sys_proc_fault_error()` / `cleonos_sys_proc_fault_rip()` +- `cleonos_sys_proc_count()` / `cleonos_sys_proc_pid_at()` / `cleonos_sys_proc_snapshot()` / `cleonos_sys_proc_kill()` ## 6. 开发注意事项