Compare commits
2 Commits
d17daf7aac
...
9b1a70e3a5
| Author | SHA1 | Date | |
|---|---|---|---|
| 9b1a70e3a5 | |||
| 7ecf26cbd9 |
15
README.md
15
README.md
@@ -104,7 +104,7 @@ The loader expects `kernel.elf` at the root of the EFI partition (next to the `E
|
||||
|
||||
### Shell Commands
|
||||
|
||||
Once the OS boots, an interactive prompt (`->`) is displayed. The following commands are available:
|
||||
Once the OS boots, the Starling Terminal displays an interactive prompt (`starling>`). The following built-in commands are available:
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
@@ -115,10 +115,17 @@ Once the OS boots, an interactive prompt (`->`) is displayed. The following comm
|
||||
| `about` | Display system information |
|
||||
| `mem` | Display memory statistics (PMM, heap, paging) |
|
||||
| `ps` | List all active tasks with PID, state, and name |
|
||||
| `spawn [name]` | Create a demo background task |
|
||||
| `memtest` | Run memory allocation/deallocation tests |
|
||||
| `spawn [name]` | Create a demo background task (optional argument sets the task name) |
|
||||
| `memtest` | Run memory allocation/deallocation and PMM tests |
|
||||
| `tasktest` | Spawn multiple concurrent tasks to test the scheduler |
|
||||
|
||||
In addition, the Starling Terminal itself recognises:
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `starling` | Spawn a nested Starling Terminal at increased depth |
|
||||
| `exit` | Exit the current nested Starling Terminal (not allowed at depth 0) |
|
||||
|
||||
### Example Session
|
||||
|
||||
```
|
||||
@@ -186,7 +193,7 @@ UEFI Firmware
|
||||
├─ idt_init() install exception handlers
|
||||
├─ memory_init() PMM → paging → heap
|
||||
├─ task_init() scheduler + task 0
|
||||
└─ shell loop read-eval-print with cooperative yield
|
||||
└─ Starling Terminal task interactive read-eval-print loop
|
||||
```
|
||||
|
||||
### Memory Management
|
||||
|
||||
101
commands.c
101
commands.c
@@ -40,6 +40,32 @@ static void cmd_spawn(BootInfo *Boot, CHAR16 *Args);
|
||||
static void cmd_memtest(BootInfo *Boot, CHAR16 *Args);
|
||||
static void cmd_tasktest(BootInfo *Boot, CHAR16 *Args);
|
||||
|
||||
/* Small helper struct used to pass arguments into per-command tasks. */
|
||||
typedef struct {
|
||||
BootInfo *Boot;
|
||||
CommandHandlerFn handler;
|
||||
CHAR16 args[128];
|
||||
} CommandTaskContext;
|
||||
|
||||
static void command_task_entry(void *arg);
|
||||
|
||||
/* Local string copy helper (wide-char, bounded). */
|
||||
static void wstrcpy16_local(CHAR16 *dst, const CHAR16 *src, UINTN max)
|
||||
{
|
||||
UINTN i = 0;
|
||||
|
||||
if (dst == NULL || max == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (src != NULL && src[i] != L'\0' && i < (max - 1)) {
|
||||
dst[i] = src[i];
|
||||
i++;
|
||||
}
|
||||
|
||||
dst[i] = L'\0';
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* Command registry
|
||||
* ================================================================ */
|
||||
@@ -96,13 +122,22 @@ static Command commands[] = {
|
||||
{
|
||||
L"memtest",
|
||||
L"Test memory allocation and deallocation",
|
||||
L"Usage: memtest\n\r Allocates and frees heap and page memory to verify\n\r the memory manager is working correctly.",
|
||||
L"Usage: memtest\n\r"
|
||||
L" Run memory tests in four phases:\n\r"
|
||||
L" 1) Heap allocation of 8 blocks (16–4096 bytes) via kmalloc()\n\r"
|
||||
L" 2) Heap free and coalescing verification via kfree()\n\r"
|
||||
L" 3) Single-page PMM allocate/free via pmm_alloc_page()/pmm_free_page()\n\r"
|
||||
L" 4) Multi-page (4-page) PMM allocate/free via pmm_alloc_pages()/pmm_free_pages()\n\r",
|
||||
cmd_memtest
|
||||
},
|
||||
{
|
||||
L"tasktest",
|
||||
L"Test task scheduler with multiple tasks",
|
||||
L"Usage: tasktest\n\r Spawns several concurrent tasks that run cooperatively\n\r and report their progress, demonstrating context switching.",
|
||||
L"Usage: tasktest\n\r"
|
||||
L" Spawns three worker tasks (worker-A/B/C) that run cooperatively,\n\r"
|
||||
L" each printing three progress steps and yielding between them.\n\r"
|
||||
L" After the workers finish, prints the final task list to\n\r"
|
||||
L" demonstrate the cooperative round-robin scheduler.",
|
||||
cmd_tasktest
|
||||
},
|
||||
{NULL, NULL, NULL, NULL} /* sentinel */
|
||||
@@ -448,22 +483,29 @@ void show_help(BootInfo *Boot)
|
||||
|
||||
/*
|
||||
* Parse a line of user input into command + arguments and dispatch
|
||||
* to the matching handler. Unknown commands print an error.
|
||||
* by spawning a dedicated task for the matching handler. Unknown
|
||||
* commands print an error.
|
||||
*
|
||||
* Returns:
|
||||
* - Pointer to the spawned Task for the command, or NULL if the
|
||||
* command was not found or had to run synchronously.
|
||||
*/
|
||||
void execute_command(BootInfo *Boot, CHAR16 *Input)
|
||||
Task *execute_command(BootInfo *Boot, CHAR16 *Input)
|
||||
{
|
||||
CHAR16 *cmd_start = NULL;
|
||||
CHAR16 *args_start = NULL;
|
||||
UINTN i = 0;
|
||||
CommandTaskContext *ctx;
|
||||
Task *t;
|
||||
|
||||
if (Boot == NULL || Input == NULL) {
|
||||
return;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
trim_spaces_inplace(Input);
|
||||
|
||||
if (Input[0] == L'\0') {
|
||||
return;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Split input into command and argument strings */
|
||||
@@ -489,12 +531,57 @@ void execute_command(BootInfo *Boot, CHAR16 *Input)
|
||||
/* Look up and dispatch the command */
|
||||
for (i = 0; commands[i].name != NULL; i++) {
|
||||
if (ascii_streq_ci(cmd_start, commands[i].name)) {
|
||||
/* Allocate a context block for the command task. */
|
||||
ctx = (CommandTaskContext *)kmalloc(sizeof(CommandTaskContext));
|
||||
if (ctx == NULL) {
|
||||
SAFE_PRINT(Boot, L"Failed to allocate command context; running in core thread.\n\r");
|
||||
commands[i].handler(Boot, args_start);
|
||||
return;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ctx->Boot = Boot;
|
||||
ctx->handler = commands[i].handler;
|
||||
wstrcpy16_local(ctx->args, args_start, sizeof(ctx->args) / sizeof(ctx->args[0]));
|
||||
|
||||
t = task_create(commands[i].name, command_task_entry, ctx);
|
||||
if (t == NULL) {
|
||||
SAFE_PRINT(Boot, L"Failed to create task for command '%s'; running in core thread.\n\r",
|
||||
commands[i].name);
|
||||
kfree(ctx);
|
||||
commands[i].handler(Boot, args_start);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SAFE_PRINT(Boot, L"[starling] spawned '%s' as PID %d\n\r", t->name, t->pid);
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
/* Command not found */
|
||||
SAFE_PRINT(Boot, L"Unknown command: %s\n\r", cmd_start);
|
||||
SAFE_PRINT(Boot, L"Type 'help' for a list of available commands.\n\r");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* Command task entry – executes one command in its own task context
|
||||
* ---------------------------------------------------------------- */
|
||||
|
||||
static void command_task_entry(void *arg)
|
||||
{
|
||||
CommandTaskContext *ctx = (CommandTaskContext *)arg;
|
||||
BootInfo *Boot = NULL;
|
||||
|
||||
if (ctx == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
Boot = ctx->Boot;
|
||||
|
||||
if (ctx->handler != NULL) {
|
||||
ctx->handler(Boot, ctx->args);
|
||||
}
|
||||
|
||||
/* Context was heap-allocated in execute_command. */
|
||||
kfree(ctx);
|
||||
}
|
||||
|
||||
10
commands.h
10
commands.h
@@ -9,6 +9,7 @@
|
||||
#define COMMANDS_H
|
||||
|
||||
#include "boot_info.h"
|
||||
#include "task.h"
|
||||
|
||||
/* Handler function signature: receives BootInfo and any argument text. */
|
||||
typedef void (*CommandHandlerFn)(BootInfo *Boot, CHAR16 *Args);
|
||||
@@ -26,8 +27,13 @@ typedef struct {
|
||||
CommandHandlerFn handler; /* function that executes the cmd */
|
||||
} Command;
|
||||
|
||||
/* Parse and dispatch a line of user input. */
|
||||
void execute_command(BootInfo *Boot, CHAR16 *Input);
|
||||
/* Parse and dispatch a line of user input.
|
||||
*
|
||||
* Returns:
|
||||
* - Pointer to a Task representing the spawned command process,
|
||||
* or NULL if the command was not found or ran synchronously.
|
||||
*/
|
||||
Task *execute_command(BootInfo *Boot, CHAR16 *Input);
|
||||
|
||||
/* Print a formatted list of all registered commands. */
|
||||
void show_help(BootInfo *Boot);
|
||||
|
||||
218
kernel.c
218
kernel.c
@@ -1,12 +1,13 @@
|
||||
/*
|
||||
* kernel.c – Kernel entry point and interactive shell loop.
|
||||
* kernel.c – Kernel entry point and Starling Terminal task.
|
||||
*
|
||||
* kmain() is called by the loader (main.c) after the ELF kernel has been
|
||||
* mapped into memory. It initialises subsystems (IDT, memory, tasks),
|
||||
* prints a welcome banner, and enters an interactive read-eval-print
|
||||
* prints a welcome banner, and then spawns the Starling Terminal as a
|
||||
* dedicated task. The terminal task runs the interactive read-eval-print
|
||||
* loop that dispatches typed commands via commands.c.
|
||||
*
|
||||
* While waiting for keyboard input, the shell yields to the cooperative
|
||||
* While the terminal waits for keyboard input it yields to the cooperative
|
||||
* scheduler so that background tasks can make progress.
|
||||
*/
|
||||
|
||||
@@ -16,6 +17,7 @@
|
||||
#include "idt.h"
|
||||
#include "memory.h"
|
||||
#include "task.h"
|
||||
#include "string_utils.h"
|
||||
|
||||
/* Null-safe print helper used throughout the kernel. */
|
||||
#define SAFE_PRINT(Boot, ...) \
|
||||
@@ -32,15 +34,143 @@
|
||||
#define COLOR_BLACK 0x0
|
||||
#define COLOR_LIGHTGREEN 0xA
|
||||
|
||||
/* Simple context passed to each Starling Terminal instance. */
|
||||
typedef struct {
|
||||
BootInfo *Boot;
|
||||
UINTN depth; /* 0 = top-level, 1+ = nested shells */
|
||||
} StarlingContext;
|
||||
|
||||
/* ================================================================
|
||||
* Starling Terminal task – interactive command loop
|
||||
* ================================================================ */
|
||||
|
||||
static void starling_terminal_task(void *arg)
|
||||
{
|
||||
StarlingContext *ctx = (StarlingContext *)arg;
|
||||
BootInfo *Boot = NULL;
|
||||
KeyEvent Key;
|
||||
KSTATUS Status;
|
||||
UINTN read_errors = 0;
|
||||
CHAR16 line[128];
|
||||
UINTN len = 0;
|
||||
UINTN depth = 0;
|
||||
|
||||
if (ctx == NULL || ctx->Boot == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
Boot = ctx->Boot;
|
||||
depth = ctx->depth;
|
||||
|
||||
SAFE_PRINT(Boot, L"\n\r[Starling Terminal depth %d] ready.\n\r\n\r", depth);
|
||||
SAFE_PRINT(Boot, L"starling> ");
|
||||
|
||||
while (TRUE) {
|
||||
/* Try non-blocking read first; yield to other tasks while idle */
|
||||
if (Boot->try_read_key != NULL) {
|
||||
Status = Boot->try_read_key(&Key);
|
||||
if (Status != 0) {
|
||||
task_yield();
|
||||
continue;
|
||||
}
|
||||
} else if (Boot->read_key != NULL) {
|
||||
Status = Boot->read_key(&Key);
|
||||
} else {
|
||||
SAFE_PRINT(Boot, L"Console input unavailable.\n\r");
|
||||
break;
|
||||
}
|
||||
|
||||
if (Status != 0) {
|
||||
read_errors++;
|
||||
if (read_errors == 1 || (read_errors % 64) == 0) {
|
||||
SAFE_PRINT(Boot, L"read_key failed (status=%ld)\n\r",
|
||||
(UINT64)Status);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
read_errors = 0;
|
||||
|
||||
if (Key.unicode_char == L'\r' || Key.unicode_char == L'\n') {
|
||||
/* Enter pressed: execute the buffered command */
|
||||
line[len] = L'\0';
|
||||
SAFE_PRINT(Boot, L"\n\r");
|
||||
trim_spaces_inplace(line);
|
||||
|
||||
/* Built-in terminal controls: exit / spawn nested Starling. */
|
||||
if (ascii_streq_ci(line, L"exit")) {
|
||||
if (depth == 0) {
|
||||
SAFE_PRINT(Boot, L"Starling: cannot exit top-level terminal.\n\r");
|
||||
SAFE_PRINT(Boot, L"starling> ");
|
||||
} else {
|
||||
SAFE_PRINT(Boot, L"Exiting Starling Terminal depth %d...\n\r", depth);
|
||||
break;
|
||||
}
|
||||
} else if (ascii_streq_ci(line, L"starling")) {
|
||||
StarlingContext *child_ctx;
|
||||
Task *child_task;
|
||||
|
||||
child_ctx = (StarlingContext *)kmalloc(sizeof(StarlingContext));
|
||||
if (child_ctx == NULL) {
|
||||
SAFE_PRINT(Boot, L"Starling: failed to allocate nested terminal context.\n\r");
|
||||
SAFE_PRINT(Boot, L"starling> ");
|
||||
} else {
|
||||
child_ctx->Boot = Boot;
|
||||
child_ctx->depth = depth + 1;
|
||||
|
||||
child_task = task_create(L"starling-term", starling_terminal_task, child_ctx);
|
||||
if (child_task == NULL) {
|
||||
SAFE_PRINT(Boot, L"Starling: failed to spawn nested terminal.\n\r");
|
||||
kfree(child_ctx);
|
||||
SAFE_PRINT(Boot, L"starling> ");
|
||||
} else {
|
||||
SAFE_PRINT(Boot, L"[starling] spawned nested terminal (PID %d, depth %d)\n\r",
|
||||
child_task->pid, child_ctx->depth);
|
||||
/* Block this shell until the child terminal exits. */
|
||||
task_wait(child_task);
|
||||
SAFE_PRINT(Boot, L"[starling] returned from nested terminal (depth %d)\n\r", depth);
|
||||
SAFE_PRINT(Boot, L"starling> ");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Task *cmd_task = execute_command(Boot, line);
|
||||
|
||||
/* If a command task was spawned, wait for it to finish. */
|
||||
if (cmd_task != NULL) {
|
||||
task_wait(cmd_task);
|
||||
}
|
||||
|
||||
/* Reset for next command */
|
||||
len = 0;
|
||||
SAFE_PRINT(Boot, L"starling> ");
|
||||
}
|
||||
} else if (Key.scan_code == 0x08 || Key.unicode_char == L'\b' || Key.unicode_char == 0x7F) {
|
||||
/* Backspace */
|
||||
if (len > 0) {
|
||||
len--;
|
||||
SAFE_PRINT(Boot, L"\b \b");
|
||||
}
|
||||
} else if (Key.unicode_char >= 32 && Key.unicode_char < 127) {
|
||||
/* Printable ASCII */
|
||||
if (len < (sizeof(line) / sizeof(line[0]) - 1)) {
|
||||
line[len++] = Key.unicode_char;
|
||||
SAFE_PRINT(Boot, L"%c", Key.unicode_char);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Free our context on exit (allocated by the spawner). */
|
||||
kfree(ctx);
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* Kernel entry point
|
||||
* ================================================================ */
|
||||
|
||||
void kmain(BootInfo *Boot)
|
||||
{
|
||||
KeyEvent Key;
|
||||
KSTATUS Status;
|
||||
UINTN read_errors = 0;
|
||||
Task *terminal_task = NULL;
|
||||
StarlingContext *ctx = NULL;
|
||||
|
||||
if (Boot == NULL) {
|
||||
return;
|
||||
@@ -84,61 +214,41 @@ void kmain(BootInfo *Boot)
|
||||
SAFE_PRINT(Boot, L"Available Services:\n\r");
|
||||
SAFE_PRINT(Boot, L" - Console Input/Output: %s\n\r",
|
||||
(Boot->read_key != NULL && Boot->print != NULL) ? L"Active" : L"Unavailable");
|
||||
SAFE_PRINT(Boot, L" - Terminal: Starling Terminal\n\r");
|
||||
SAFE_PRINT(Boot, L"\n\r");
|
||||
SAFE_PRINT(Boot, L"Type 'help' for a list of commands.\n\r\n\r");
|
||||
|
||||
/* ---- Interactive shell loop ---- */
|
||||
CHAR16 line[128];
|
||||
UINTN len = 0;
|
||||
/* ---- Spawn Starling Terminal as its own task ---- */
|
||||
ctx = (StarlingContext *)kmalloc(sizeof(StarlingContext));
|
||||
if (ctx == NULL) {
|
||||
SAFE_PRINT(Boot, L"Failed to allocate Starling Terminal context; starting inline.\n\r");
|
||||
StarlingContext inline_ctx;
|
||||
inline_ctx.Boot = Boot;
|
||||
inline_ctx.depth = 0;
|
||||
starling_terminal_task(&inline_ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
SAFE_PRINT(Boot, L"-> ");
|
||||
ctx->Boot = Boot;
|
||||
ctx->depth = 0;
|
||||
|
||||
terminal_task = task_create(L"starling-term", starling_terminal_task, ctx);
|
||||
if (terminal_task == NULL) {
|
||||
SAFE_PRINT(Boot, L"Failed to start Starling Terminal task; falling back to kernel loop.\n\r");
|
||||
|
||||
/*
|
||||
* Fall back to running the terminal loop directly in the core
|
||||
* thread so the system remains usable even if task creation
|
||||
* fails for some reason.
|
||||
*/
|
||||
starling_terminal_task(Boot);
|
||||
return;
|
||||
}
|
||||
|
||||
SAFE_PRINT(Boot, L"[core] Started Starling Terminal (PID %d).\n\r", terminal_task->pid);
|
||||
|
||||
/* Core thread becomes an idle loop, yielding to the terminal and others. */
|
||||
while (TRUE) {
|
||||
/* Try non-blocking read first; yield to other tasks while idle */
|
||||
if (Boot->try_read_key != NULL) {
|
||||
Status = Boot->try_read_key(&Key);
|
||||
if (Status != 0) {
|
||||
task_yield();
|
||||
continue;
|
||||
}
|
||||
} else if (Boot->read_key != NULL) {
|
||||
Status = Boot->read_key(&Key);
|
||||
} else {
|
||||
SAFE_PRINT(Boot, L"Console input unavailable.\n\r");
|
||||
break;
|
||||
}
|
||||
|
||||
if (Status != 0) {
|
||||
read_errors++;
|
||||
if (read_errors == 1 || (read_errors % 64) == 0) {
|
||||
SAFE_PRINT(Boot, L"read_key failed (status=%ld)\n\r",
|
||||
(UINT64)Status);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
read_errors = 0;
|
||||
|
||||
if (Key.unicode_char == L'\r' || Key.unicode_char == L'\n') {
|
||||
/* Enter pressed: execute the buffered command */
|
||||
line[len] = L'\0';
|
||||
SAFE_PRINT(Boot, L"\n\r");
|
||||
execute_command(Boot, line);
|
||||
|
||||
/* Reset for next command */
|
||||
len = 0;
|
||||
SAFE_PRINT(Boot, L"-> ");
|
||||
} else if (Key.scan_code == 0x08 || Key.unicode_char == L'\b' || Key.unicode_char == 0x7F) {
|
||||
/* Backspace */
|
||||
if (len > 0) {
|
||||
len--;
|
||||
SAFE_PRINT(Boot, L"\b \b");
|
||||
}
|
||||
} else if (Key.unicode_char >= 32 && Key.unicode_char < 127) {
|
||||
/* Printable ASCII */
|
||||
if (len < (sizeof(line) / sizeof(line[0]) - 1)) {
|
||||
line[len++] = Key.unicode_char;
|
||||
SAFE_PRINT(Boot, L"%c", Key.unicode_char);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
27
task.c
27
task.c
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* task.c – Cooperative multitasking: PCB pool, scheduler, yield/exit.
|
||||
*
|
||||
* Task 0 is the kernel/shell thread (uses the boot stack).
|
||||
* Task 0 is the always-present kernel core thread (uses the boot stack).
|
||||
* Additional tasks are created with task_create(), which allocates a
|
||||
* stack from the PMM and sets up a fake context-switch frame so that
|
||||
* context_switch() can "return" into a trampoline that calls the real
|
||||
@@ -57,7 +57,7 @@ static void wstrcpy16(CHAR16 *dst, const CHAR16 *src, UINTN max)
|
||||
|
||||
/*
|
||||
* Initialise the scheduler: clear all PCB slots and register the
|
||||
* currently running kernel thread as task 0.
|
||||
* currently running kernel core thread as task 0.
|
||||
*/
|
||||
void task_init(BootInfo *Boot)
|
||||
{
|
||||
@@ -79,7 +79,7 @@ void task_init(BootInfo *Boot)
|
||||
}
|
||||
|
||||
/*
|
||||
* Task 0 = the currently running kernel thread (the shell).
|
||||
* Task 0 = the currently running kernel core thread.
|
||||
* It already has a stack (the kernel's boot stack), so we don't
|
||||
* allocate one. Its saved_rsp will be filled in during the
|
||||
* first context_switch call in task_yield().
|
||||
@@ -87,7 +87,7 @@ void task_init(BootInfo *Boot)
|
||||
tasks[0].pid = next_pid++;
|
||||
tasks[0].state = TASK_STATE_RUNNING;
|
||||
tasks[0].switches = 1;
|
||||
wstrcpy16(tasks[0].name, L"kernel", TASK_NAME_LEN);
|
||||
wstrcpy16(tasks[0].name, L"core", TASK_NAME_LEN);
|
||||
|
||||
current_task = &tasks[0];
|
||||
task_ready = TRUE;
|
||||
@@ -329,6 +329,25 @@ UINTN task_count(void)
|
||||
return count;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* Wait for another task to finish
|
||||
* ---------------------------------------------------------------- */
|
||||
|
||||
void task_wait(Task *t)
|
||||
{
|
||||
if (!task_ready || t == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Busy-wait cooperatively until the target task's PCB slot has
|
||||
* been recycled back to FREE by task_exit().
|
||||
*/
|
||||
while (t->state != TASK_STATE_FREE) {
|
||||
task_yield();
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* Print task list (implements the `ps` command)
|
||||
* ---------------------------------------------------------------- */
|
||||
|
||||
3
task.h
3
task.h
@@ -74,6 +74,9 @@ Task *task_current(void); /* return the running task's PCB */
|
||||
UINTN task_count(void); /* number of non-FREE tasks */
|
||||
void task_print_list(BootInfo *Boot); /* print task table (for `ps`) */
|
||||
|
||||
/* Block the current task until the target task has finished. */
|
||||
void task_wait(Task *t);
|
||||
|
||||
/* Assembly context switch (defined in context_switch.S). */
|
||||
extern void context_switch(UINT64 *old_rsp, UINT64 new_rsp);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user