diff --git a/cleonos/c/apps/bg_main.c b/cleonos/c/apps/bg_main.c index 22ab2f9..f366cf7 100644 --- a/cleonos/c/apps/bg_main.c +++ b/cleonos/c/apps/bg_main.c @@ -1,5 +1,31 @@ #include "cmd_runtime.h" +static int ush_bg_resume_pid(const char *pid_text) { + u64 pid = 0ULL; + u64 ret; + + if (pid_text == (const char *)0 || ush_parse_u64_dec(pid_text, &pid) == 0 || pid == 0ULL) { + return 0; + } + + ret = cleonos_sys_proc_kill(pid, CLEONOS_SIGCONT); + + if (ret == (u64)-1) { + ush_writeln("bg: pid not found"); + return 1; + } + + if (ret == 0ULL) { + ush_writeln("bg: target cannot be resumed right now"); + return 1; + } + + ush_write("bg: resumed ["); + ush_write_hex_u64(pid); + ush_writeln("]"); + return 1; +} + static int ush_cmd_bg(const ush_state *sh, const char *arg) { char target[USH_PATH_MAX]; char argv_line[USH_ARG_MAX]; @@ -23,6 +49,10 @@ static int ush_cmd_bg(const ush_state *sh, const char *arg) { ush_copy(argv_line, (u64)sizeof(argv_line), rest); } + if (argv_line[0] == '\0' && ush_bg_resume_pid(target) != 0) { + return 1; + } + if (ush_resolve_exec_path(sh, target, path, (u64)sizeof(path)) == 0) { ush_writeln("bg: invalid target"); return 0; diff --git a/cleonos/c/apps/fg_main.c b/cleonos/c/apps/fg_main.c index 466e5d1..4b263a1 100644 --- a/cleonos/c/apps/fg_main.c +++ b/cleonos/c/apps/fg_main.c @@ -52,7 +52,9 @@ static int ush_fg_pick_latest_job(u64 *out_pid) { continue; } - if (snap.state != CLEONOS_PROC_STATE_PENDING && snap.state != CLEONOS_PROC_STATE_RUNNING) { + if (snap.state != CLEONOS_PROC_STATE_PENDING && + snap.state != CLEONOS_PROC_STATE_RUNNING && + snap.state != CLEONOS_PROC_STATE_STOPPED) { continue; } @@ -99,6 +101,7 @@ static int ush_fg_wait_pid(u64 pid) { static int ush_cmd_fg(const char *arg) { u64 pid = 0ULL; + cleonos_proc_snapshot snap; if (arg != (const char *)0 && arg[0] != '\0') { if (ush_parse_u64_dec(arg, &pid) == 0 || pid == 0ULL) { @@ -112,6 +115,25 @@ static int ush_cmd_fg(const char *arg) { } } + if (cleonos_sys_proc_snapshot(pid, &snap, (u64)sizeof(snap)) == 0ULL) { + ush_writeln("fg: pid not found"); + return 0; + } + + if (snap.state == CLEONOS_PROC_STATE_STOPPED) { + u64 ret = cleonos_sys_proc_kill(pid, CLEONOS_SIGCONT); + + if (ret == (u64)-1) { + ush_writeln("fg: pid not found"); + return 0; + } + + if (ret == 0ULL) { + ush_writeln("fg: failed to continue job"); + return 0; + } + } + ush_write("fg: waiting ["); ush_write_hex_u64(pid); ush_writeln("]"); diff --git a/cleonos/c/apps/jobs_main.c b/cleonos/c/apps/jobs_main.c index 287f96e..15df968 100644 --- a/cleonos/c/apps/jobs_main.c +++ b/cleonos/c/apps/jobs_main.c @@ -25,6 +25,9 @@ static const char *ush_jobs_state_name(u64 state) { if (state == CLEONOS_PROC_STATE_RUNNING) { return "RUNNING"; } + if (state == CLEONOS_PROC_STATE_STOPPED) { + return "STOPPED"; + } if (state == CLEONOS_PROC_STATE_EXITED) { return "EXITED "; } diff --git a/cleonos/c/apps/kill_main.c b/cleonos/c/apps/kill_main.c index c888c8b..3ab69a3 100644 --- a/cleonos/c/apps/kill_main.c +++ b/cleonos/c/apps/kill_main.c @@ -1,19 +1,105 @@ #include "cmd_runtime.h" +static void ush_kill_upper_copy(char *dst, u64 dst_size, const char *src) { + u64 i = 0ULL; + + if (dst == (char *)0 || dst_size == 0ULL) { + return; + } + + dst[0] = '\0'; + + if (src == (const char *)0) { + return; + } + + while (src[i] != '\0' && i + 1ULL < dst_size) { + char ch = src[i]; + if (ch >= 'a' && ch <= 'z') { + ch = (char)(ch - ('a' - 'A')); + } + dst[i] = ch; + i++; + } + + dst[i] = '\0'; +} + +static int ush_kill_parse_signal_name(const char *text, u64 *out_signal) { + char name[32]; + u64 start = 0ULL; + + if (text == (const char *)0 || out_signal == (u64 *)0 || text[0] == '\0') { + return 0; + } + + ush_kill_upper_copy(name, (u64)sizeof(name), text); + + if (name[0] == '-') { + start = 1ULL; + } + + if (name[start] == 'S' && name[start + 1ULL] == 'I' && name[start + 2ULL] == 'G') { + start += 3ULL; + } + + if (name[start] == '\0') { + return 0; + } + + if (ush_streq(&name[start], "TERM") != 0) { + *out_signal = CLEONOS_SIGTERM; + return 1; + } + + if (ush_streq(&name[start], "KILL") != 0) { + *out_signal = CLEONOS_SIGKILL; + return 1; + } + + if (ush_streq(&name[start], "STOP") != 0) { + *out_signal = CLEONOS_SIGSTOP; + return 1; + } + + if (ush_streq(&name[start], "CONT") != 0) { + *out_signal = CLEONOS_SIGCONT; + return 1; + } + + return 0; +} + +static int ush_kill_parse_signal(const char *text, u64 *out_signal) { + u64 parsed = 0ULL; + + if (text == (const char *)0 || out_signal == (u64 *)0 || text[0] == '\0') { + return 0; + } + + if (ush_parse_u64_dec(text, &parsed) != 0 && parsed <= 255ULL) { + *out_signal = parsed; + return 1; + } + + return ush_kill_parse_signal_name(text, out_signal); +} + static int ush_cmd_kill(const char *arg) { char pid_text[USH_PATH_MAX]; + char signal_text[USH_PATH_MAX]; const char *rest = ""; u64 pid; - u64 signal = 15ULL; + u64 signal = CLEONOS_SIGTERM; u64 ret; if (arg == (const char *)0 || arg[0] == '\0') { - ush_writeln("kill: usage kill [signal]"); + ush_writeln("kill: usage kill [TERM|KILL|STOP|CONT|signal]"); return 0; } if (ush_split_first_and_rest(arg, pid_text, (u64)sizeof(pid_text), &rest) == 0) { - ush_writeln("kill: usage kill [signal]"); + ush_writeln("kill: usage kill [TERM|KILL|STOP|CONT|signal]"); return 0; } @@ -23,7 +109,9 @@ static int ush_cmd_kill(const char *arg) { } if (rest != (const char *)0 && rest[0] != '\0') { - if (ush_parse_u64_dec(rest, &signal) == 0 || signal > 255ULL) { + ush_copy(signal_text, (u64)sizeof(signal_text), rest); + ush_trim_line(signal_text); + if (ush_kill_parse_signal(signal_text, &signal) == 0) { ush_writeln("kill: invalid signal"); return 0; } diff --git a/cleonos/c/apps/ps_main.c b/cleonos/c/apps/ps_main.c index 2831a3d..b8b6a43 100644 --- a/cleonos/c/apps/ps_main.c +++ b/cleonos/c/apps/ps_main.c @@ -25,6 +25,9 @@ static const char *ush_ps_state_name(u64 state) { if (state == CLEONOS_PROC_STATE_RUNNING) { return "RUN "; } + if (state == CLEONOS_PROC_STATE_STOPPED) { + return "STOP"; + } if (state == CLEONOS_PROC_STATE_EXITED) { return "EXIT"; } diff --git a/cleonos/c/apps/top_main.c b/cleonos/c/apps/top_main.c index 6cf4526..054a5a1 100644 --- a/cleonos/c/apps/top_main.c +++ b/cleonos/c/apps/top_main.c @@ -7,6 +7,9 @@ static const char *ush_top_state_name(u64 state) { if (state == CLEONOS_PROC_STATE_RUNNING) { return "RUN "; } + if (state == CLEONOS_PROC_STATE_STOPPED) { + return "STOP"; + } if (state == CLEONOS_PROC_STATE_EXITED) { return "EXIT"; } @@ -114,7 +117,9 @@ static void ush_top_render_frame(u64 frame_index, u64 delay_ticks) { continue; } - if (snap.state != CLEONOS_PROC_STATE_PENDING && snap.state != CLEONOS_PROC_STATE_RUNNING) { + if (snap.state != CLEONOS_PROC_STATE_PENDING && + snap.state != CLEONOS_PROC_STATE_RUNNING && + snap.state != CLEONOS_PROC_STATE_STOPPED) { continue; } diff --git a/cleonos/c/include/cleonos_syscall.h b/cleonos/c/include/cleonos_syscall.h index bcad742..827ad46 100644 --- a/cleonos/c/include/cleonos_syscall.h +++ b/cleonos/c/include/cleonos_syscall.h @@ -11,6 +11,12 @@ typedef unsigned long long usize; #define CLEONOS_PROC_STATE_PENDING 1ULL #define CLEONOS_PROC_STATE_RUNNING 2ULL #define CLEONOS_PROC_STATE_EXITED 3ULL +#define CLEONOS_PROC_STATE_STOPPED 4ULL + +#define CLEONOS_SIGKILL 9ULL +#define CLEONOS_SIGTERM 15ULL +#define CLEONOS_SIGCONT 18ULL +#define CLEONOS_SIGSTOP 19ULL typedef struct cleonos_proc_snapshot { u64 pid; diff --git a/clks/include/clks/exec.h b/clks/include/clks/exec.h index 3e72dd3..e2d8ead 100644 --- a/clks/include/clks/exec.h +++ b/clks/include/clks/exec.h @@ -9,6 +9,12 @@ #define CLKS_EXEC_PROC_STATE_PENDING 1ULL #define CLKS_EXEC_PROC_STATE_RUNNING 2ULL #define CLKS_EXEC_PROC_STATE_EXITED 3ULL +#define CLKS_EXEC_PROC_STATE_STOPPED 4ULL + +#define CLKS_EXEC_SIGNAL_KILL 9ULL +#define CLKS_EXEC_SIGNAL_TERM 15ULL +#define CLKS_EXEC_SIGNAL_CONT 18ULL +#define CLKS_EXEC_SIGNAL_STOP 19ULL struct clks_exec_proc_snapshot { u64 pid; diff --git a/clks/kernel/exec.c b/clks/kernel/exec.c index b11fb99..94be5d9 100644 --- a/clks/kernel/exec.c +++ b/clks/kernel/exec.c @@ -22,13 +22,14 @@ 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 +#define CLKS_EXEC_DEFAULT_KILL_SIGNAL CLKS_EXEC_SIGNAL_TERM enum clks_exec_proc_state { CLKS_EXEC_PROC_UNUSED = 0, CLKS_EXEC_PROC_PENDING = 1, CLKS_EXEC_PROC_RUNNING = 2, CLKS_EXEC_PROC_EXITED = 3, + CLKS_EXEC_PROC_STOPPED = 4, }; struct clks_exec_proc_record { @@ -38,6 +39,8 @@ struct clks_exec_proc_record { u64 ppid; u64 started_tick; u64 exited_tick; + u64 run_started_tick; + u64 runtime_ticks_accum; u64 exit_status; u32 tty_index; u64 image_mem_bytes; @@ -413,6 +416,8 @@ static struct clks_exec_proc_record *clks_exec_prepare_proc_record(i32 slot, proc->ppid = clks_exec_current_pid(); proc->started_tick = 0ULL; proc->exited_tick = 0ULL; + proc->run_started_tick = 0ULL; + proc->runtime_ticks_accum = 0ULL; proc->exit_status = (u64)-1; proc->tty_index = clks_tty_active(); proc->image_mem_bytes = 0ULL; @@ -446,6 +451,42 @@ static struct clks_exec_proc_record *clks_exec_prepare_proc_record(i32 slot, return proc; } +static void clks_exec_proc_mark_running(struct clks_exec_proc_record *proc, u64 now_tick) { + if (proc == CLKS_NULL) { + return; + } + + if (proc->started_tick == 0ULL) { + proc->started_tick = now_tick; + } + + proc->run_started_tick = now_tick; + proc->state = CLKS_EXEC_PROC_RUNNING; +} + +static void clks_exec_proc_pause_runtime(struct clks_exec_proc_record *proc, u64 now_tick) { + if (proc == CLKS_NULL) { + return; + } + + if (proc->run_started_tick != 0ULL && now_tick > proc->run_started_tick) { + proc->runtime_ticks_accum += (now_tick - proc->run_started_tick); + } + + proc->run_started_tick = 0ULL; +} + +static void clks_exec_proc_mark_exited(struct clks_exec_proc_record *proc, u64 now_tick, u64 status) { + if (proc == CLKS_NULL) { + return; + } + + clks_exec_proc_pause_runtime(proc, now_tick); + proc->state = CLKS_EXEC_PROC_EXITED; + proc->exit_status = status; + proc->exited_tick = now_tick; +} + static clks_bool clks_exec_invoke_entry(void *entry_ptr, u32 depth_index, u64 *out_ret) { if (entry_ptr == CLKS_NULL || out_ret == CLKS_NULL) { return CLKS_FALSE; @@ -509,22 +550,17 @@ static clks_bool clks_exec_run_proc_slot(i32 slot, u64 *out_status) { } if (proc->path[0] != '/') { - proc->state = CLKS_EXEC_PROC_EXITED; - proc->exit_status = (u64)-1; - proc->exited_tick = clks_interrupts_timer_ticks(); + clks_exec_proc_mark_exited(proc, clks_interrupts_timer_ticks(), (u64)-1); return CLKS_FALSE; } if (clks_exec_pid_stack_depth >= CLKS_EXEC_MAX_DEPTH) { clks_log(CLKS_LOG_WARN, "EXEC", "PROCESS STACK DEPTH EXCEEDED"); - proc->state = CLKS_EXEC_PROC_EXITED; - proc->exit_status = (u64)-1; - proc->exited_tick = clks_interrupts_timer_ticks(); + clks_exec_proc_mark_exited(proc, clks_interrupts_timer_ticks(), (u64)-1); return CLKS_FALSE; } - proc->state = CLKS_EXEC_PROC_RUNNING; - proc->started_tick = clks_interrupts_timer_ticks(); + clks_exec_proc_mark_running(proc, clks_interrupts_timer_ticks()); depth_index = (i32)clks_exec_pid_stack_depth; clks_exec_pid_stack[(u32)depth_index] = proc->pid; @@ -593,9 +629,7 @@ static clks_bool clks_exec_run_proc_slot(i32 slot, u64 *out_status) { clks_exec_success++; - proc->state = CLKS_EXEC_PROC_EXITED; - proc->exit_status = run_ret; - proc->exited_tick = clks_interrupts_timer_ticks(); + clks_exec_proc_mark_exited(proc, clks_interrupts_timer_ticks(), run_ret); if (depth_pushed == CLKS_TRUE && clks_exec_pid_stack_depth > 0U) { clks_exec_pid_stack_depth--; @@ -617,9 +651,7 @@ fail: clks_exec_running_depth--; } - proc->state = CLKS_EXEC_PROC_EXITED; - proc->exit_status = (u64)-1; - proc->exited_tick = clks_interrupts_timer_ticks(); + clks_exec_proc_mark_exited(proc, clks_interrupts_timer_ticks(), (u64)-1); if (depth_pushed == CLKS_TRUE && clks_exec_pid_stack_depth > 0U) { clks_exec_pid_stack_depth--; @@ -805,7 +837,9 @@ u64 clks_exec_wait_pid(u64 pid, u64 *out_status) { clks_exec_pending_dispatch_active = CLKS_FALSE; } - if (proc->state == CLKS_EXEC_PROC_PENDING || proc->state == CLKS_EXEC_PROC_RUNNING) { + if (proc->state == CLKS_EXEC_PROC_PENDING || + proc->state == CLKS_EXEC_PROC_RUNNING || + proc->state == CLKS_EXEC_PROC_STOPPED) { return 0ULL; } @@ -964,27 +998,21 @@ u64 clks_exec_current_fault_rip(void) { } static u64 clks_exec_proc_runtime_ticks(const struct clks_exec_proc_record *proc, u64 now_tick) { + u64 runtime; + 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; - } + runtime = proc->runtime_ticks_accum; - return now_tick - proc->started_tick; + if (proc->state == CLKS_EXEC_PROC_RUNNING && + proc->run_started_tick != 0ULL && + now_tick > proc->run_started_tick) { + runtime += (now_tick - proc->run_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; + return runtime; } static u64 clks_exec_proc_memory_bytes(const struct clks_exec_proc_record *proc) { @@ -1117,14 +1145,33 @@ u64 clks_exec_proc_kill(u64 pid, u64 signal) { 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; + proc->last_signal = effective_signal; + proc->last_fault_vector = 0ULL; + proc->last_fault_error = 0ULL; + proc->last_fault_rip = 0ULL; + + if (effective_signal == CLKS_EXEC_SIGNAL_CONT) { + if (proc->state == CLKS_EXEC_PROC_STOPPED) { + proc->state = CLKS_EXEC_PROC_PENDING; + } + return 1ULL; + } + + if (effective_signal == CLKS_EXEC_SIGNAL_STOP) { + if (proc->state == CLKS_EXEC_PROC_PENDING) { + proc->state = CLKS_EXEC_PROC_STOPPED; + return 1ULL; + } + + if (proc->state == CLKS_EXEC_PROC_STOPPED) { + return 1ULL; + } + + return 0ULL; + } + + if (proc->state == CLKS_EXEC_PROC_PENDING || proc->state == CLKS_EXEC_PROC_STOPPED) { + clks_exec_proc_mark_exited(proc, now_tick, status); return 1ULL; } @@ -1135,10 +1182,6 @@ u64 clks_exec_proc_kill(u64 pid, u64 signal) { 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; diff --git a/clks/kernel/syscall.c b/clks/kernel/syscall.c index eda072e..929a037 100644 --- a/clks/kernel/syscall.c +++ b/clks/kernel/syscall.c @@ -25,6 +25,7 @@ #define CLKS_SYSCALL_ARG_LINE_MAX 256U #define CLKS_SYSCALL_ENV_LINE_MAX 512U #define CLKS_SYSCALL_ITEM_MAX 128U +#define CLKS_SYSCALL_PROCFS_TEXT_MAX 2048U #define CLKS_SYSCALL_USER_TRACE_BUDGET 128ULL struct clks_syscall_frame { @@ -166,6 +167,333 @@ static u64 clks_syscall_kbd_get_char(void) { return (u64)(u8)ch; } +static clks_bool clks_syscall_procfs_is_root(const char *path) { + return (path != CLKS_NULL && clks_strcmp(path, "/proc") == 0) ? CLKS_TRUE : CLKS_FALSE; +} + +static clks_bool clks_syscall_procfs_is_self(const char *path) { + return (path != CLKS_NULL && clks_strcmp(path, "/proc/self") == 0) ? CLKS_TRUE : CLKS_FALSE; +} + +static clks_bool clks_syscall_procfs_is_list(const char *path) { + return (path != CLKS_NULL && clks_strcmp(path, "/proc/list") == 0) ? CLKS_TRUE : CLKS_FALSE; +} + +static clks_bool clks_syscall_parse_u64_dec(const char *text, u64 *out_value) { + u64 value = 0ULL; + usize i = 0U; + + if (text == CLKS_NULL || out_value == CLKS_NULL || text[0] == '\0') { + return CLKS_FALSE; + } + + while (text[i] != '\0') { + u64 digit; + + if (text[i] < '0' || text[i] > '9') { + return CLKS_FALSE; + } + + digit = (u64)(text[i] - '0'); + + if (value > ((0xFFFFFFFFFFFFFFFFULL - digit) / 10ULL)) { + return CLKS_FALSE; + } + + value = (value * 10ULL) + digit; + i++; + } + + *out_value = value; + return CLKS_TRUE; +} + +static clks_bool clks_syscall_procfs_parse_pid(const char *path, u64 *out_pid) { + const char *part; + usize i = 0U; + char pid_text[32]; + u64 pid; + + if (path == CLKS_NULL || out_pid == CLKS_NULL) { + return CLKS_FALSE; + } + + if (path[0] != '/' || path[1] != 'p' || path[2] != 'r' || path[3] != 'o' || path[4] != 'c' || path[5] != '/') { + return CLKS_FALSE; + } + + part = &path[6]; + + if (part[0] == '\0' || clks_strcmp(part, "self") == 0 || clks_strcmp(part, "list") == 0) { + return CLKS_FALSE; + } + + while (part[i] != '\0') { + if (i + 1U >= sizeof(pid_text)) { + return CLKS_FALSE; + } + + if (part[i] < '0' || part[i] > '9') { + return CLKS_FALSE; + } + + pid_text[i] = part[i]; + i++; + } + + pid_text[i] = '\0'; + + if (clks_syscall_parse_u64_dec(pid_text, &pid) == CLKS_FALSE || pid == 0ULL) { + return CLKS_FALSE; + } + + *out_pid = pid; + return CLKS_TRUE; +} + +static const char *clks_syscall_proc_state_name(u64 state) { + if (state == CLKS_EXEC_PROC_STATE_PENDING) { + return "PENDING"; + } + + if (state == CLKS_EXEC_PROC_STATE_RUNNING) { + return "RUNNING"; + } + + if (state == CLKS_EXEC_PROC_STATE_STOPPED) { + return "STOPPED"; + } + + if (state == CLKS_EXEC_PROC_STATE_EXITED) { + return "EXITED"; + } + + return "UNUSED"; +} + +static usize clks_syscall_procfs_append_char(char *out, usize out_size, usize pos, char ch) { + if (out == CLKS_NULL || out_size == 0U) { + return pos; + } + + if (pos + 1U < out_size) { + out[pos] = ch; + out[pos + 1U] = '\0'; + return pos + 1U; + } + + out[out_size - 1U] = '\0'; + return pos; +} + +static usize clks_syscall_procfs_append_text(char *out, usize out_size, usize pos, const char *text) { + usize i = 0U; + + if (text == CLKS_NULL) { + return pos; + } + + while (text[i] != '\0') { + pos = clks_syscall_procfs_append_char(out, out_size, pos, text[i]); + i++; + } + + return pos; +} + +static usize clks_syscall_procfs_append_u64_dec(char *out, usize out_size, usize pos, u64 value) { + char temp[32]; + usize len = 0U; + usize i; + + if (value == 0ULL) { + return clks_syscall_procfs_append_char(out, out_size, pos, '0'); + } + + while (value != 0ULL && len + 1U < sizeof(temp)) { + temp[len++] = (char)('0' + (value % 10ULL)); + value /= 10ULL; + } + + for (i = 0U; i < len; i++) { + pos = clks_syscall_procfs_append_char(out, out_size, pos, temp[len - 1U - i]); + } + + return pos; +} + +static usize clks_syscall_procfs_append_u64_hex(char *out, usize out_size, usize pos, u64 value) { + i32 nibble; + + pos = clks_syscall_procfs_append_text(out, out_size, pos, "0X"); + + for (nibble = 15; nibble >= 0; nibble--) { + u64 current = (value >> (u64)(nibble * 4)) & 0x0FULL; + char ch = (current < 10ULL) ? (char)('0' + current) : (char)('A' + (current - 10ULL)); + pos = clks_syscall_procfs_append_char(out, out_size, pos, ch); + } + + return pos; +} + +static clks_bool clks_syscall_procfs_snapshot_for_path(const char *path, struct clks_exec_proc_snapshot *out_snap) { + u64 pid; + + if (path == CLKS_NULL || out_snap == CLKS_NULL) { + return CLKS_FALSE; + } + + if (clks_syscall_procfs_is_self(path) == CLKS_TRUE) { + pid = clks_exec_current_pid(); + + if (pid == 0ULL) { + return CLKS_FALSE; + } + + return clks_exec_proc_snapshot(pid, out_snap); + } + + if (clks_syscall_procfs_parse_pid(path, &pid) == CLKS_TRUE) { + return clks_exec_proc_snapshot(pid, out_snap); + } + + return CLKS_FALSE; +} + +static usize clks_syscall_procfs_render_snapshot(char *out, + usize out_size, + const struct clks_exec_proc_snapshot *snap) { + usize pos = 0U; + + if (out == CLKS_NULL || out_size == 0U || snap == CLKS_NULL) { + return 0U; + } + + out[0] = '\0'; + + pos = clks_syscall_procfs_append_text(out, out_size, pos, "pid="); + pos = clks_syscall_procfs_append_u64_dec(out, out_size, pos, snap->pid); + pos = clks_syscall_procfs_append_char(out, out_size, pos, '\n'); + + pos = clks_syscall_procfs_append_text(out, out_size, pos, "ppid="); + pos = clks_syscall_procfs_append_u64_dec(out, out_size, pos, snap->ppid); + pos = clks_syscall_procfs_append_char(out, out_size, pos, '\n'); + + pos = clks_syscall_procfs_append_text(out, out_size, pos, "state="); + pos = clks_syscall_procfs_append_text(out, out_size, pos, clks_syscall_proc_state_name(snap->state)); + pos = clks_syscall_procfs_append_char(out, out_size, pos, '\n'); + + pos = clks_syscall_procfs_append_text(out, out_size, pos, "state_id="); + pos = clks_syscall_procfs_append_u64_dec(out, out_size, pos, snap->state); + pos = clks_syscall_procfs_append_char(out, out_size, pos, '\n'); + + pos = clks_syscall_procfs_append_text(out, out_size, pos, "tty="); + pos = clks_syscall_procfs_append_u64_dec(out, out_size, pos, snap->tty_index); + pos = clks_syscall_procfs_append_char(out, out_size, pos, '\n'); + + pos = clks_syscall_procfs_append_text(out, out_size, pos, "runtime_ticks="); + pos = clks_syscall_procfs_append_u64_dec(out, out_size, pos, snap->runtime_ticks); + pos = clks_syscall_procfs_append_char(out, out_size, pos, '\n'); + + pos = clks_syscall_procfs_append_text(out, out_size, pos, "mem_bytes="); + pos = clks_syscall_procfs_append_u64_dec(out, out_size, pos, snap->mem_bytes); + pos = clks_syscall_procfs_append_char(out, out_size, pos, '\n'); + + pos = clks_syscall_procfs_append_text(out, out_size, pos, "exit_status="); + pos = clks_syscall_procfs_append_u64_hex(out, out_size, pos, snap->exit_status); + pos = clks_syscall_procfs_append_char(out, out_size, pos, '\n'); + + pos = clks_syscall_procfs_append_text(out, out_size, pos, "last_signal="); + pos = clks_syscall_procfs_append_u64_dec(out, out_size, pos, snap->last_signal); + pos = clks_syscall_procfs_append_char(out, out_size, pos, '\n'); + + pos = clks_syscall_procfs_append_text(out, out_size, pos, "last_fault_vector="); + pos = clks_syscall_procfs_append_u64_dec(out, out_size, pos, snap->last_fault_vector); + pos = clks_syscall_procfs_append_char(out, out_size, pos, '\n'); + + pos = clks_syscall_procfs_append_text(out, out_size, pos, "last_fault_error="); + pos = clks_syscall_procfs_append_u64_hex(out, out_size, pos, snap->last_fault_error); + pos = clks_syscall_procfs_append_char(out, out_size, pos, '\n'); + + pos = clks_syscall_procfs_append_text(out, out_size, pos, "last_fault_rip="); + pos = clks_syscall_procfs_append_u64_hex(out, out_size, pos, snap->last_fault_rip); + pos = clks_syscall_procfs_append_char(out, out_size, pos, '\n'); + + pos = clks_syscall_procfs_append_text(out, out_size, pos, "path="); + pos = clks_syscall_procfs_append_text(out, out_size, pos, snap->path); + pos = clks_syscall_procfs_append_char(out, out_size, pos, '\n'); + + return pos; +} + +static usize clks_syscall_procfs_render_list(char *out, usize out_size) { + usize pos = 0U; + u64 proc_count = clks_exec_proc_count(); + u64 i; + + if (out == CLKS_NULL || out_size == 0U) { + return 0U; + } + + out[0] = '\0'; + pos = clks_syscall_procfs_append_text(out, out_size, pos, "pid state tty runtime mem path\n"); + + for (i = 0ULL; i < proc_count; i++) { + u64 pid = 0ULL; + struct clks_exec_proc_snapshot snap; + + if (clks_exec_proc_pid_at(i, &pid) == CLKS_FALSE || pid == 0ULL) { + continue; + } + + if (clks_exec_proc_snapshot(pid, &snap) == CLKS_FALSE) { + continue; + } + + pos = clks_syscall_procfs_append_u64_dec(out, out_size, pos, snap.pid); + pos = clks_syscall_procfs_append_char(out, out_size, pos, ' '); + pos = clks_syscall_procfs_append_text(out, out_size, pos, clks_syscall_proc_state_name(snap.state)); + pos = clks_syscall_procfs_append_char(out, out_size, pos, ' '); + pos = clks_syscall_procfs_append_u64_dec(out, out_size, pos, snap.tty_index); + pos = clks_syscall_procfs_append_char(out, out_size, pos, ' '); + pos = clks_syscall_procfs_append_u64_dec(out, out_size, pos, snap.runtime_ticks); + pos = clks_syscall_procfs_append_char(out, out_size, pos, ' '); + pos = clks_syscall_procfs_append_u64_dec(out, out_size, pos, snap.mem_bytes); + pos = clks_syscall_procfs_append_char(out, out_size, pos, ' '); + pos = clks_syscall_procfs_append_text(out, out_size, pos, snap.path); + pos = clks_syscall_procfs_append_char(out, out_size, pos, '\n'); + } + + return pos; +} + +static clks_bool clks_syscall_procfs_render_file(const char *path, + char *out, + usize out_size, + usize *out_len) { + struct clks_exec_proc_snapshot snap; + + if (out_len != CLKS_NULL) { + *out_len = 0U; + } + + if (path == CLKS_NULL || out == CLKS_NULL || out_size == 0U || out_len == CLKS_NULL) { + return CLKS_FALSE; + } + + if (clks_syscall_procfs_is_list(path) == CLKS_TRUE) { + *out_len = clks_syscall_procfs_render_list(out, out_size); + return CLKS_TRUE; + } + + if (clks_syscall_procfs_snapshot_for_path(path, &snap) == CLKS_TRUE) { + *out_len = clks_syscall_procfs_render_snapshot(out, out_size, &snap); + return CLKS_TRUE; + } + + return CLKS_FALSE; +} + static u64 clks_syscall_fs_child_count(u64 arg0) { char path[CLKS_SYSCALL_PATH_MAX]; @@ -173,6 +501,10 @@ static u64 clks_syscall_fs_child_count(u64 arg0) { return (u64)-1; } + if (clks_syscall_procfs_is_root(path) == CLKS_TRUE) { + return 2ULL + clks_exec_proc_count(); + } + return clks_fs_count_children(path); } @@ -187,6 +519,41 @@ static u64 clks_syscall_fs_get_child_name(u64 arg0, u64 arg1, u64 arg2) { return 0ULL; } + if (clks_syscall_procfs_is_root(path) == CLKS_TRUE) { + if (arg1 == 0ULL) { + clks_memset((void *)arg2, 0, CLKS_SYSCALL_NAME_MAX); + clks_memcpy((void *)arg2, "self", 5U); + return 1ULL; + } + + if (arg1 == 1ULL) { + clks_memset((void *)arg2, 0, CLKS_SYSCALL_NAME_MAX); + clks_memcpy((void *)arg2, "list", 5U); + return 1ULL; + } + + { + u64 pid = 0ULL; + char pid_text[32]; + usize len; + + if (clks_exec_proc_pid_at(arg1 - 2ULL, &pid) == CLKS_FALSE || pid == 0ULL) { + return 0ULL; + } + + clks_memset(pid_text, 0, sizeof(pid_text)); + len = clks_syscall_procfs_append_u64_dec(pid_text, sizeof(pid_text), 0U, pid); + + if (len + 1U > CLKS_SYSCALL_NAME_MAX) { + return 0ULL; + } + + clks_memset((void *)arg2, 0, CLKS_SYSCALL_NAME_MAX); + clks_memcpy((void *)arg2, pid_text, len + 1U); + return 1ULL; + } + } + if (clks_fs_get_child_name(path, arg1, (char *)arg2, (usize)CLKS_SYSCALL_NAME_MAX) == CLKS_FALSE) { return 0ULL; } @@ -208,6 +575,26 @@ static u64 clks_syscall_fs_read(u64 arg0, u64 arg1, u64 arg2) { return 0ULL; } + if (clks_syscall_procfs_is_list(path) == CLKS_TRUE || + clks_syscall_procfs_is_self(path) == CLKS_TRUE || + clks_syscall_procfs_parse_pid(path, &file_size) == CLKS_TRUE) { + char proc_text[CLKS_SYSCALL_PROCFS_TEXT_MAX]; + usize proc_len = 0U; + + if (clks_syscall_procfs_render_file(path, proc_text, sizeof(proc_text), &proc_len) == CLKS_FALSE) { + return 0ULL; + } + + copy_len = ((u64)proc_len < arg2) ? (u64)proc_len : arg2; + + if (copy_len == 0ULL) { + return 0ULL; + } + + clks_memcpy((void *)arg1, proc_text, (usize)copy_len); + return copy_len; + } + data = clks_fs_read_all(path, &file_size); if (data == CLKS_NULL || file_size == 0ULL) { @@ -452,11 +839,24 @@ static u64 clks_syscall_audio_stop(void) { static u64 clks_syscall_fs_stat_type(u64 arg0) { char path[CLKS_SYSCALL_PATH_MAX]; struct clks_fs_node_info info; + struct clks_exec_proc_snapshot snap; if (clks_syscall_copy_user_string(arg0, path, sizeof(path)) == CLKS_FALSE) { return (u64)-1; } + if (clks_syscall_procfs_is_root(path) == CLKS_TRUE) { + return (u64)CLKS_FS_NODE_DIR; + } + + if (clks_syscall_procfs_is_list(path) == CLKS_TRUE || clks_syscall_procfs_is_self(path) == CLKS_TRUE) { + return (u64)CLKS_FS_NODE_FILE; + } + + if (clks_syscall_procfs_snapshot_for_path(path, &snap) == CLKS_TRUE) { + return (u64)CLKS_FS_NODE_FILE; + } + if (clks_fs_stat(path, &info) == CLKS_FALSE) { return (u64)-1; } @@ -467,11 +867,21 @@ static u64 clks_syscall_fs_stat_type(u64 arg0) { static u64 clks_syscall_fs_stat_size(u64 arg0) { char path[CLKS_SYSCALL_PATH_MAX]; struct clks_fs_node_info info; + char proc_text[CLKS_SYSCALL_PROCFS_TEXT_MAX]; + usize proc_len = 0U; if (clks_syscall_copy_user_string(arg0, path, sizeof(path)) == CLKS_FALSE) { return (u64)-1; } + if (clks_syscall_procfs_is_root(path) == CLKS_TRUE) { + return 0ULL; + } + + if (clks_syscall_procfs_render_file(path, proc_text, sizeof(proc_text), &proc_len) == CLKS_TRUE) { + return (u64)proc_len; + } + if (clks_fs_stat(path, &info) == CLKS_FALSE) { return (u64)-1; } diff --git a/docs/syscall.md b/docs/syscall.md index 50c847b..b3edaba 100644 --- a/docs/syscall.md +++ b/docs/syscall.md @@ -44,6 +44,21 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); - bits `15:8` = CPU exception vector - bits `31:16` = exception error code 低 16 位 +进程状态值(`proc_snapshot.state`): + +- `0` = `UNUSED` +- `1` = `PENDING` +- `2` = `RUNNING` +- `3` = `EXITED` +- `4` = `STOPPED` + +常用信号值(`PROC_KILL`): + +- `SIGKILL` = `9` +- `SIGTERM` = `15` +- `SIGCONT` = `18` +- `SIGSTOP` = `19` + ## 3. 当前实现中的长度/路径限制 以下限制由内核 `clks/kernel/syscall.c` 当前实现决定: @@ -60,6 +75,14 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); - `FS_MKDIR` / `FS_WRITE` / `FS_APPEND` / `FS_REMOVE` 仅允许 `/temp` 树下路径。 +`/proc` 虚拟目录(由 syscall 层动态导出): + +- `/proc`:目录(children = `self`、`list`、以及全部 PID 名称) +- `/proc/self`:当前进程快照文本 +- `/proc/list`:所有进程列表文本 +- `/proc/`:指定 PID 快照文本 +- `/proc` 为只读;写入类 syscall 不支持。 + ## 4. Syscall 列表(0~64) ### 0 `CLEONOS_SYSCALL_LOG_WRITE` @@ -120,6 +143,7 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); - 参数: - `arg0`: `const char *dir_path` - 返回:子节点数量 +- 说明:当 `dir_path=/proc` 时,返回 `2 + proc_count`(`self`、`list`、PID 子项)。 ### 11 `CLEONOS_SYSCALL_FS_GET_CHILD_NAME` @@ -129,6 +153,7 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); - `arg2`: `char *out_name` - 返回:成功 `1`,失败 `0` - 说明:最多写入 96 字节(含终止符)。 +- 说明:当 `dir_path=/proc` 时,`index=0/1` 分别为 `self/list`,后续索引为 PID 文本。 ### 12 `CLEONOS_SYSCALL_FS_READ` @@ -138,6 +163,7 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); - `arg2`: `u64 buffer_size` - 返回:实际读取字节数,失败/文件空返回 `0` - 说明:不会自动追加 `\0`,调用方应自行处理文本终止。 +- 说明:支持读取 `/proc/self`、`/proc/list`、`/proc/`。 ### 13 `CLEONOS_SYSCALL_EXEC_PATH` @@ -224,12 +250,14 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); - 参数: - `arg0`: `const char *path` - 返回:`1=FILE`,`2=DIR`,失败 `-1` +- 说明:`/proc` 返回目录,`/proc/self`、`/proc/list`、`/proc/` 返回文件。 ### 28 `CLEONOS_SYSCALL_FS_STAT_SIZE` - 参数: - `arg0`: `const char *path` - 返回:文件大小;目录通常为 `0`;失败 `-1` +- 说明:`/proc/*` 文件大小按生成文本长度返回。 ### 29 `CLEONOS_SYSCALL_FS_MKDIR` @@ -323,7 +351,7 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); - `arg1`: `u64 *out_status`(可为 `0`) - 返回: - `-1`:PID 不存在 -- `0`:目标进程仍在运行 +- `0`:目标进程仍未退出(`PENDING` / `RUNNING` / `STOPPED`) - `1`:目标进程已退出 - 说明:当返回 `1` 且 `arg1!=0` 时,会写入退出码。 @@ -466,7 +494,7 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); - `arg1`: `struct cleonos_proc_snapshot *out_snapshot` - `arg2`: `u64 out_size`(需 `>= sizeof(cleonos_proc_snapshot)`) - 返回:成功 `1`,失败 `0` -- 说明:返回 PID/PPID/状态/运行 tick/内存估算/TTY/路径等快照信息。 +- 说明:返回 PID/PPID/状态(含 `STOPPED`)/运行 tick/内存估算/TTY/路径等快照信息。 ### 64 `CLEONOS_SYSCALL_PROC_KILL` @@ -477,6 +505,10 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); - `1`:请求成功 - `0`:当前不可终止(例如非当前上下文中的 running 进程) - `-1`:PID 不存在 +- 语义: +- `SIGTERM`/`SIGKILL`(以及其它非 STOP/CONT 信号):终止目标进程。 +- `SIGSTOP`:将 `PENDING` 进程置为 `STOPPED`;对已 `STOPPED` 目标返回成功。 +- `SIGCONT`:将 `STOPPED` 进程恢复为 `PENDING`。 ## 5. 用户态封装函数 @@ -506,6 +538,7 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); - 传入的字符串/缓冲指针目前按“同地址空间可直接访问”模型处理,后续若引入严格用户态地址隔离,需要补充用户内存校验。 - `FS_READ` 不保证文本终止符;读取文本请预留 1 字节并手动 `buf[n] = '\0'`。 - `FS_WRITE`/`FS_APPEND` 仅允许 `/temp`,并有单次长度上限。 +- `/proc` 由 syscall 层虚拟导出,不占用 RAMDISK 节点,也不能通过写入类 syscall 修改。 ## 7. Wine 兼容说明