Thu, 07 Dec 2023 21:29:43 +0100
10 files changed,
200 insertions(+),
26 deletions(-)
A
kernel/alarm.c
@@ -0,0 +1,65 @@
+#include "memory.h" +#include "alarm.h" +#include "core.h" +#include "sched/sched.h" + +struct alarm *first = NULL; +int alarm_id_next = 1000; + +int alarm_add(int ticks, PROCESS_ID pid, alarm_mode m) { + struct alarm *next = malloc(sizeof(struct alarm)); + + next->alarm_id = alarm_id_next++; + next->ticks_remaining = ticks; + next->pid = pid; + next->m = m; + + // add to list + next->next = first; + first = next; + + return next->alarm_id; +} + +void alarm_tick(void) { + struct alarm **next = &first; + + while (*next) { + if ((*next)->ticks_remaining-- <= 0) { + // send signal to process + switch((*next)->m) { + case ALARM_WAKEUP: + sched_unblock((*next)->pid); + break; + case ALARM_KILL: + sched_kill((*next)->pid); + break; + default: + printk("Unknown alarm mode: %i\n", (int)((*next)->m)); + break; + } + + // remove alarm + *next = (*next)->next; + } else { + next = &((*next)->next); + } + } +} + +int alarm_cancel(int alarm_id) { + struct alarm **next = &first; + + while (*next) { + if ((*next)->alarm_id == alarm_id) { + // remove alarm + *next = (*next)->next; + return 0; + } else { + next = &((*next)->next); + } + } + + // alarm was not found + return -1; +}
A
kernel/alarm.h
@@ -0,0 +1,48 @@
+#ifndef ALARM_H +#define ALARM_H + +#include "sched/process.h" + +typedef enum { + ALARM_UNDEF = 0, + ALARM_WAKEUP = 1, + ALARM_KILL = 2 +} alarm_mode; + +struct alarm { + int alarm_id; + int ticks_remaining; + PROCESS_ID pid; + alarm_mode m; + + struct alarm *next; +}; + +/** + * @brief Decrease all alarm counters and act on finished alarms. + * + * This function is called by sched_interrupt_c. + */ +void alarm_tick(void); + +/** + * @brief Add a new alarm. + * + * @param ticks Number of ticks to count down. + * @param pid Process ID of the process targeted by the alarm. + * @param m Alarm mode. Defines which action to take once the timer has run out. + * + * @return ID of the created alarm. + */ +int alarm_add(int ticks, PROCESS_ID pid, alarm_mode m); + +/** + * @brief Cancel an existing alarm. + * + * @param alarm_id ID of the alarm to be cancelled. + * + * @return 0 on success, -1 if the alarm was not found. + */ +int alarm_cancel(int alarm_id); + +#endif
M
kernel/sched/process.h
→
kernel/sched/process.h
@@ -86,6 +86,9 @@
//! Process priority. PROCESS_PRIO priority; + //! Process starvation counter + int starvation; + //! Stack checksum STACK_CHECKSUM checksum;
M
kernel/sched/sched.c
→
kernel/sched/sched.c
@@ -14,6 +14,7 @@ #include "pit.h"
#include "pic.h" #include "elf.h" #include "file.h" +#include "alarm.h" #include "assembly.h" #include "assert.h"@@ -90,7 +91,12 @@ }
// TODO: implement with malloc strcpy(p->name_buf, name); - strcpy(p->args_buf, args); + + if (args == 0) { + p->args_buf[0] = 0; + } else { + strcpy(p->args_buf, args); + } p->name = (const char*)&(p->name_buf); p->args = (const char*)&(p->args_buf);@@ -118,6 +124,7 @@ /* TODO: check if code exists at entry point */
// start the process p->state = PSTATE_READY; + p->starvation = 0; crit_exit();@@ -126,17 +133,16 @@ }
void sched_interrupt_c(SCHED_FRAME * volatile frame, uint32_t volatile ebp) { - //kpanic("SCHEDULER STACK INFO"); PROCESS* current = get_process(current_pid); + alarm_tick(); + if (current_pid != 0) { current->esp = (VIRT_ADDR)(frame); current->ebp = (VIRT_ADDR)(ebp); current->eip = (VIRT_ADDR)frame->eip; current->eflags = frame->eflags; - - // save stack checksum stack_compute_checksum(&(current->checksum), current->esp, current->ebp); }@@ -150,13 +156,6 @@ // select next process
PRINT_DBG("exiting %i, ", current_pid); current_pid = next_schedule(current_pid); PRINT_DBG("entering %i\n", current_pid); - - // unblock all blocked processes - for (PROCESS *p = get_first_process(); p != NULL; p = p->next) { - if (p->state == PSTATE_BLOCKED) { - p->state = PSTATE_READY; - } - } // prepare to return to process PROCESS* next = get_process(current_pid);@@ -208,19 +207,14 @@ current_pid = 0;
// create idle process PROCESS_ID idle = sched_spawn(NULL, NULL, 0); - assert(idle != -1); + assert(idle == 0); return 1; } void sched_yield(void) { crit_enter(); - //PRINT_DBG("yield.\n"); PROCESS *current = get_process(current_pid); - if (current != NULL && current->state != PSTATE_TERMINATED) { - current->state = PSTATE_READY; - //current->state = PSTATE_RUNNING; - } uint32_t csc = crit_stash(); sti();@@ -228,6 +222,29 @@ INT(0x20);
crit_restore(csc); crit_exit(); +} + +void sched_sleep(int ticks) { + // block the process. unblocking is done by the alarm. + PROCESS *process = get_process(current_pid); + process->state = PSTATE_BLOCKED; + + // create a wakeup-alarm to unblock the process. + alarm_add(ticks, current_pid, ALARM_WAKEUP); + + sched_yield(); + + // if this happens, it means the scheduler chose + // a blocked process as next process, which should + // never happen. + if (process->state == PSTATE_BLOCKED) { + kpanic("ERROR!"); + } +} + +void sched_unblock(int pid) { + PROCESS *process = get_process(pid); + process->state = PSTATE_READY; } int sched_kill(PROCESS_ID pid) {
M
kernel/sched/sched.h
→
kernel/sched/sched.h
@@ -64,6 +64,20 @@ * process for one unit of time.
*/ void sched_yield(void); +/** + * @brief Blocks the current process for the given number of ticks. + * + * @param ticks Number of ticks. + */ +void sched_sleep(int ticks); + +/** + * @brief Unblocks the process given by \p pid immediately. + * + * @param pid ID of the process to be unblocked. + */ +void sched_unblock(int pid); + /*! * Kills the process with the specified ID. * \param pid Process ID of the process to be killed.
M
kernel/sched/sched_strats.c
→
kernel/sched/sched_strats.c
@@ -3,12 +3,31 @@
#define NULL ((void*)0) PROCESS_ID next_schedule(PROCESS_ID current) { - PROCESS* process = get_process(current); + PROCESS* next = get_first_process(); + + PROCESS* choice = NULL; + + while (next) { + // skip idle process + if (next->id == 0) { + next = next->next; + continue; + } + + next->starvation++; + + // choose ready process with highest starvation value + if (next->state == PSTATE_READY && next->starvation > choice->starvation) { + choice = next; + } - if (process != NULL && process->next != NULL && process->next->state != PSTATE_TERMINATED) { - return process->next->id; + next = next->next; + } + + if (choice) { + choice->starvation = 0; + return choice->id; } else { - PROCESS *first = get_first_process(); - return first->id; + return 0; } }
M
kernel/syscall.c
→
kernel/syscall.c
@@ -25,7 +25,8 @@ file_lseek,
file_tell, time_get_ticks, mem_usage, - sched_kill + sched_kill, + sched_sleep }; extern void syscall_interrupt(void);
M
libcedos/cedos.c
→
libcedos/cedos.c
@@ -100,3 +100,8 @@ void process_kill(int pid) {
volatile uint32_t res = 0; interrupt(0x30, res, 14, pid, 0, 0); } + +void sleep(int ticks) { + volatile uint32_t res = 0; + interrupt(0x30, res, 15, ticks, 0, 0); +}
M
libcedos/cedos.h
→
libcedos/cedos.h
@@ -7,6 +7,7 @@ #include <stdint.h>
int sysprint(const char *fmt, int arg1, int arg2); int yield(); +void sleep(int ticks); int get_pid(); int process_spawn(const char *fname, const char *args); int process_spawn_pipe(const char *fname, const char *args, int stdin, int stdout);
M
shell/ticks.c
→
shell/ticks.c
@@ -8,7 +8,7 @@
#include "cedos.h" void main(char *args) { - if (strlen(args) == 0) { + if (args == NULL || strlen(args) == 0) { int ticks = sc_time_get_ticks(); printf("%i\n", ticks); } else {@@ -20,6 +20,7 @@ return;
} int pid = get_pid(); + int color = 40 + (pid % 8); int superticks = 0;@@ -27,11 +28,11 @@ while (1) {
int ticks = sc_time_get_ticks(); if (ticks / interval != superticks) { - printf("[%i] %i ticks\n", pid, ticks); + printf("\e[%im\e[0K[%i] %i ticks\e[0m\n", color, pid, ticks); superticks = ticks / interval; } - yield(); + sleep(interval); } } }