Finish v0.3.0
This commit is contained in:
@@ -0,0 +1,308 @@
|
||||
/// MentOS, The Mentoring Operating system project
|
||||
/// @file process.c
|
||||
/// @brief Process data structures and functions.
|
||||
/// @copyright (c) 2019 This file is distributed under the MIT License.
|
||||
/// See LICENSE.md for details.
|
||||
|
||||
#include "process.h"
|
||||
#include "prio.h"
|
||||
#include "init.h"
|
||||
#include "panic.h"
|
||||
#include "kheap.h"
|
||||
#include "debug.h"
|
||||
#include "unistd.h"
|
||||
#include "string.h"
|
||||
#include "list_head.h"
|
||||
#include "stdatomic.h"
|
||||
#include "scheduler.h"
|
||||
|
||||
#define PUSH_ON_STACK(stack, type, item) \
|
||||
*((type *)(stack -= sizeof(type))) = item
|
||||
|
||||
/// @brief The task_struct of the init process.
|
||||
static task_struct *init_proc;
|
||||
|
||||
void exit_handler()
|
||||
{
|
||||
exit(1);
|
||||
kernel_panic("I should not be here.\n");
|
||||
}
|
||||
|
||||
task_struct *create_init_process()
|
||||
{
|
||||
dbg_print("Building init process...\n");
|
||||
// Create a new task_struct.
|
||||
init_proc = kmalloc(sizeof(task_struct));
|
||||
// TODO: process is IN USER SPACE! it should be in KERNEL SPACE!
|
||||
memset(init_proc, 0, sizeof(task_struct));
|
||||
// Set the id of the process.
|
||||
init_proc->pid = get_new_pid();
|
||||
// Set the name of the process.
|
||||
strcpy(init_proc->name, "init");
|
||||
// Set the statistics of the process.
|
||||
init_proc->se.prio = DEFAULT_PRIO;
|
||||
init_proc->se.start_runtime = 0;
|
||||
init_proc->se.exec_start = 0;
|
||||
init_proc->se.sum_exec_runtime = 0;
|
||||
init_proc->se.vruntime = 0;
|
||||
// Initialize the list_head.
|
||||
list_head_init(&init_proc->run_list);
|
||||
// Initialize the children list_head.
|
||||
list_head_init(&init_proc->children);
|
||||
// Initialize the sibling list_head.
|
||||
list_head_init(&init_proc->sibling);
|
||||
// Create a new stack segment.
|
||||
init_proc->mm = create_process_image(DEFAULT_STACK_SIZE);
|
||||
char *stack = (char *)init_proc->mm->start_stack;
|
||||
// Clean stack space.
|
||||
memset(stack, 0, DEFAULT_STACK_SIZE);
|
||||
// Set the base address of the stack.
|
||||
char *ebp = (char *)(stack + DEFAULT_STACK_SIZE);
|
||||
// Create a pointer to keep track of the top of the stack.
|
||||
char *esp = ebp;
|
||||
// Set exit_handler as terminating function for init.
|
||||
PUSH_ON_STACK(esp, uintptr_t, (uintptr_t)&exit_handler);
|
||||
// Set the top address of the stack.
|
||||
init_proc->thread.useresp = (uintptr_t)esp;
|
||||
// Set the base address of the stack.
|
||||
init_proc->thread.ebp = (uintptr_t)ebp;
|
||||
// Set the program counter.
|
||||
init_proc->thread.eip = (uintptr_t)&main_init;
|
||||
// Enable the interrupts.
|
||||
init_proc->thread.eflags = init_proc->thread.eflags | EFLAG_IF;
|
||||
// Clear the current working directory.
|
||||
memset(init_proc->cwd, '\0', MAX_PATH_LENGTH);
|
||||
// Set the state of the process as running.
|
||||
init_proc->state = TASK_RUNNING;
|
||||
// Active the current process.
|
||||
enqueue_task(init_proc);
|
||||
|
||||
dbg_print("--------------------------------------------------\n");
|
||||
dbg_print("- %s process (PID: %d, eflags: %d)\n", init_proc->name,
|
||||
init_proc->pid, init_proc->thread.eflags);
|
||||
dbg_print("\tStack: [0x%p - 0x%p]\n", init_proc->mm->start_stack,
|
||||
init_proc->mm->start_stack + DEFAULT_STACK_SIZE);
|
||||
dbg_print("\tebp: 0x%p\n", init_proc->thread.ebp);
|
||||
dbg_print("\tesp: 0x%p\n", init_proc->thread.useresp);
|
||||
dbg_print("\teip: 0x%p\n", init_proc->thread.eip);
|
||||
dbg_print("--------------------------------------------------\n");
|
||||
|
||||
return init_proc;
|
||||
}
|
||||
|
||||
char *get_current_dir_name()
|
||||
{
|
||||
task_struct *current_process = kernel_get_current_process();
|
||||
if (current_process != NULL) {
|
||||
return strdup(current_process->cwd);
|
||||
}
|
||||
|
||||
return kstrdup("/");
|
||||
}
|
||||
|
||||
void sys_getcwd(char *path, size_t size)
|
||||
{
|
||||
task_struct *current_process = kernel_get_current_process();
|
||||
if ((current_process != NULL) && (path != NULL)) {
|
||||
strncpy(path, current_process->cwd, size);
|
||||
}
|
||||
}
|
||||
|
||||
void sys_chdir(char const *path)
|
||||
{
|
||||
task_struct *current_process = kernel_get_current_process();
|
||||
if ((current_process != NULL) && (path != NULL)) {
|
||||
strcpy(current_process->cwd, path);
|
||||
}
|
||||
}
|
||||
|
||||
pid_t sys_vfork(pt_regs *r)
|
||||
{
|
||||
task_struct *current = kernel_get_current_process();
|
||||
if (current == NULL) {
|
||||
kernel_panic("There is no current process!");
|
||||
}
|
||||
|
||||
dbg_print("Forking '%s'(%d) process...\n", current->name, current->pid);
|
||||
|
||||
// Create a new task_struct.
|
||||
// TODO: process is IN USER SPACE! it should be in KERNEL SPACE!
|
||||
task_struct *new_process = kmalloc(sizeof(task_struct));
|
||||
|
||||
// TODO: this is NOT a deep copy. should a deep copy be used here?
|
||||
memcpy(new_process, current, sizeof(task_struct));
|
||||
|
||||
// Set the id of the process.
|
||||
new_process->pid = get_new_pid();
|
||||
|
||||
// Set the statistics of the process.
|
||||
new_process->se.prio = DEFAULT_PRIO;
|
||||
new_process->se.start_runtime = 0;
|
||||
new_process->se.exec_start = 0;
|
||||
new_process->se.sum_exec_runtime = 0;
|
||||
// TODO: vruntime should be the scheduled highest values so far.
|
||||
new_process->se.vruntime = current->se.vruntime;
|
||||
|
||||
// Create a new stack segment.
|
||||
new_process->mm = create_process_image(DEFAULT_STACK_SIZE);
|
||||
char *stack = (char *)new_process->mm->start_stack;
|
||||
// Copy the father's stack.
|
||||
memcpy((char *)new_process->mm->start_stack,
|
||||
(char *)current->mm->start_stack, DEFAULT_STACK_SIZE);
|
||||
// Set the base address of the stack.
|
||||
char *ebp = stack + DEFAULT_STACK_SIZE; // TODO: da controllare
|
||||
// Create a pointer to keep track of the top of the stack.
|
||||
char *esp = stack + (r->useresp - current->mm->start_stack);
|
||||
|
||||
// Set the top address of the stack.
|
||||
new_process->thread.useresp = (uintptr_t)esp;
|
||||
// Set the base address of the stack.
|
||||
new_process->thread.ebp = (uintptr_t)ebp;
|
||||
// Set the program counter.
|
||||
new_process->thread.eip = r->eip;
|
||||
|
||||
// Set the base registers.
|
||||
new_process->thread.eax = 0;
|
||||
new_process->thread.ebx = r->ebx;
|
||||
new_process->thread.ecx = r->ecx;
|
||||
new_process->thread.edx = r->edx;
|
||||
|
||||
// Enable the interrupts.
|
||||
new_process->thread.eflags = new_process->thread.eflags | EFLAG_IF;
|
||||
|
||||
// Set the state of the process as running.
|
||||
new_process->state = TASK_RUNNING;
|
||||
|
||||
// Set current as parent for the new process
|
||||
new_process->parent = current;
|
||||
|
||||
// Initialize the list_head.
|
||||
list_head_init(&new_process->run_list);
|
||||
|
||||
// Initialize the children list_head.
|
||||
list_head_init(&new_process->children);
|
||||
|
||||
// Initialize the children list_head.
|
||||
list_head_init(&new_process->sibling);
|
||||
|
||||
// Set the new_process as child of current.
|
||||
list_head_add_tail(¤t->children, &new_process->sibling);
|
||||
|
||||
// Active the new process.
|
||||
enqueue_task(new_process);
|
||||
|
||||
dbg_print("--------------------------------------------------\n");
|
||||
dbg_print("- %s process (PID: %d, eflags: %d)\n", new_process->name,
|
||||
new_process->pid, new_process->thread.eflags);
|
||||
dbg_print("\teip : 0x%p\n", new_process->thread.eip);
|
||||
dbg_print("\tebp : 0x%p\n", new_process->thread.ebp);
|
||||
dbg_print("\tesp : 0x%p\n", new_process->thread.useresp);
|
||||
dbg_print("\tStack : 0x%p\n", new_process->mm->start_stack);
|
||||
dbg_print("\tRunList: 0x%p\n", &new_process->run_list);
|
||||
dbg_print("--------------------------------------------------\n");
|
||||
|
||||
dbg_print("Fork of '%s' (child pid: %d) process completed.\n",
|
||||
current->name, current->pid);
|
||||
|
||||
// Return PID of child process to parent.
|
||||
return new_process->pid;
|
||||
}
|
||||
|
||||
static inline int push_args_on_stack(uintptr_t *esp, char *args[],
|
||||
char ***argsptr)
|
||||
{
|
||||
int argc = 0;
|
||||
char *args_ptr[256];
|
||||
// Count the number of arguments.
|
||||
while (args[argc] != NULL) {
|
||||
++argc;
|
||||
}
|
||||
// Push terminating NULL.
|
||||
PUSH_ON_STACK((*esp), char *, (char *)NULL);
|
||||
// Prepare args with space for the terminating NULL.
|
||||
for (int i = argc - 1; i >= 0; --i) {
|
||||
for (int j = strlen(args[i]); j >= 0; --j) {
|
||||
PUSH_ON_STACK((*esp), char, args[i][j]);
|
||||
}
|
||||
args_ptr[i] = (char *)(*esp);
|
||||
}
|
||||
// Push terminating NULL.
|
||||
PUSH_ON_STACK((*esp), char *, (char *)NULL);
|
||||
// Push array of pointers to the arguments.
|
||||
for (int i = argc - 1; i >= 0; --i) {
|
||||
PUSH_ON_STACK((*esp), char *, args_ptr[i]);
|
||||
}
|
||||
(*argsptr) = (char **)(*esp);
|
||||
|
||||
return argc;
|
||||
}
|
||||
|
||||
int sys_execve(pt_regs *r)
|
||||
{
|
||||
char **argv, **_argv, **envp, **_envp;
|
||||
// Check the current process.
|
||||
task_struct *current = kernel_get_current_process();
|
||||
if (current == NULL) {
|
||||
kernel_panic("There is no current process!");
|
||||
}
|
||||
|
||||
// Get the filename.
|
||||
uintptr_t *filename = (uintptr_t *)r->ebx;
|
||||
if (filename == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Get the arguments.
|
||||
argv = (char **)r->ecx;
|
||||
// Get the environment.
|
||||
envp = (char **)r->edx;
|
||||
|
||||
// Check the argument and that at least the name is provided.
|
||||
if ((argv == NULL) || (argv[0] == NULL)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Check that the environment is provided.
|
||||
if (envp == NULL) {
|
||||
kernel_panic("You must provide at least an empty list for envp!");
|
||||
}
|
||||
|
||||
// Set the name.
|
||||
strcpy(current->name, argv[0]);
|
||||
|
||||
// Set the top address of the stack.
|
||||
current->thread.useresp = (uintptr_t)current->thread.ebp;
|
||||
|
||||
// Set the program counter.
|
||||
current->thread.eip = (uintptr_t)filename;
|
||||
|
||||
int argc = push_args_on_stack(¤t->thread.useresp, argv, &_argv);
|
||||
push_args_on_stack(¤t->thread.useresp, envp, &_envp);
|
||||
|
||||
PUSH_ON_STACK(current->thread.useresp, char **, _envp);
|
||||
PUSH_ON_STACK(current->thread.useresp, char **, _argv);
|
||||
PUSH_ON_STACK(current->thread.useresp, int, argc);
|
||||
PUSH_ON_STACK(current->thread.useresp, uintptr_t, (uintptr_t)exit_handler);
|
||||
|
||||
// dbg_print("_ARGV:0x%09x {\n", _argv);
|
||||
// for (int i = 0; _argv[i] != NULL; ++i) {
|
||||
// dbg_print("\t[%d][0x%09x]%s\n", i, _argv[i], _argv[i]);
|
||||
// }
|
||||
// dbg_print("}\n");
|
||||
//
|
||||
// if (_envp != NULL) {
|
||||
// dbg_print("_ENVP:0x%09x {\n", _envp);
|
||||
// for (int i = 0; _envp[i] != NULL; ++i) {
|
||||
// dbg_print("\t[%d][0x%09x]%s\n", i, _envp[i], _envp[i]);
|
||||
// }
|
||||
// dbg_print("}\n");
|
||||
// }
|
||||
|
||||
// Perform the switch to the new process.
|
||||
do_switch(current, r);
|
||||
|
||||
dbg_print("Executing '0x%p' for process %d with %d arguments (0x%p)...\n",
|
||||
filename, current->pid, argc, argv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,383 @@
|
||||
/// MentOS, The Mentoring Operating system project
|
||||
/// @file scheduler.c
|
||||
/// @brief Scheduler structures and functions.
|
||||
/// @copyright (c) 2019 This file is distributed under the MIT License.
|
||||
/// See LICENSE.md for details.
|
||||
|
||||
#include "scheduler.h"
|
||||
#include "tss.h"
|
||||
#include "fpu.h"
|
||||
#include "prio.h"
|
||||
#include "wait.h"
|
||||
#include "kheap.h"
|
||||
#include "panic.h"
|
||||
#include "debug.h"
|
||||
#include "clock.h"
|
||||
#include "errno.h"
|
||||
#include "rbtree.h"
|
||||
#include "stdlib.h"
|
||||
#include "list_head.h"
|
||||
|
||||
/// @brief Assembly function setting the kernel stack to jump into
|
||||
/// location in Ring 3 mode (USER mode).
|
||||
/// @param location The location where to jump.
|
||||
/// @param stack The stack to use.
|
||||
extern void enter_userspace(uintptr_t location, uintptr_t stack);
|
||||
|
||||
/// The list of processes.
|
||||
runqueue_t runqueue;
|
||||
|
||||
uint32_t get_new_pid(void)
|
||||
{
|
||||
/// The current unused PID.
|
||||
static unsigned long int tid = 1;
|
||||
|
||||
// Return the pid and increment.
|
||||
return tid++;
|
||||
}
|
||||
|
||||
task_struct *kernel_get_current_process()
|
||||
{
|
||||
return runqueue.curr;
|
||||
}
|
||||
|
||||
task_struct *kernel_get_running_process(pid_t pid)
|
||||
{
|
||||
list_head *it;
|
||||
list_for_each (it, &runqueue.queue) {
|
||||
task_struct *entry = list_entry(it, task_struct, run_list);
|
||||
if (entry != NULL) {
|
||||
if (entry->pid == pid) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t kernel_get_active_processes()
|
||||
{
|
||||
return runqueue.num_active;
|
||||
}
|
||||
|
||||
void kernel_initialize_scheduler()
|
||||
{
|
||||
// Initialize the runqueue list of tasks.
|
||||
list_head_init(&runqueue.queue);
|
||||
// Reset the current task.
|
||||
runqueue.curr = NULL;
|
||||
// Reset the number of active tasks.
|
||||
runqueue.num_active = 0;
|
||||
}
|
||||
|
||||
void enqueue_task(task_struct *process)
|
||||
{
|
||||
// If current_process is NULL, then process is the current process.
|
||||
if (runqueue.curr == NULL) {
|
||||
runqueue.curr = process;
|
||||
}
|
||||
// Add the new process at the end.
|
||||
list_head_add_tail(&process->run_list, &runqueue.queue);
|
||||
// Increment the number of active processes.
|
||||
++runqueue.num_active;
|
||||
}
|
||||
|
||||
void dequeue_task(task_struct *process)
|
||||
{
|
||||
// Delete the process from the list of running processes.
|
||||
list_head_del(&process->run_list);
|
||||
// Decrement the number of active processes.
|
||||
--runqueue.num_active;
|
||||
}
|
||||
|
||||
void kernel_schedule(pt_regs *f)
|
||||
{
|
||||
// Check if there is a running process.
|
||||
if (runqueue.curr == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
//==== Update Statistics ===================================================
|
||||
time_t delta_exec = get_millisecond() - runqueue.curr->se.exec_start;
|
||||
// dbg_print("[%3d] %d = %d - %d\n", runqueue.curr->pid, delta_exec,
|
||||
// get_millisecond(), runqueue.curr->se.exec_start);
|
||||
// set the sum_exec_runtime
|
||||
runqueue.curr->se.sum_exec_runtime += delta_exec;
|
||||
//==========================================================================
|
||||
|
||||
//==== Handle Zombies ======================================================
|
||||
task_struct *next_process = NULL;
|
||||
if (runqueue.curr->state == EXIT_ZOMBIE) {
|
||||
// get the next process after the current one
|
||||
list_head *nNode = runqueue.curr->run_list.next;
|
||||
// check if we reached the head of list_head
|
||||
if (nNode == &runqueue.queue)
|
||||
nNode = nNode->next;
|
||||
// get the task_struct
|
||||
next_process = list_entry(nNode, task_struct, run_list);
|
||||
// Remove the zombie task.
|
||||
dequeue_task(runqueue.curr);
|
||||
} else {
|
||||
//==== Scheduling ======================================================
|
||||
// Pointer to the next process to be executed.
|
||||
next_process = pick_next_task(&runqueue, delta_exec);
|
||||
//======================================================================
|
||||
}
|
||||
//==========================================================================
|
||||
|
||||
// Print, for debugging purpose, data about the current process.
|
||||
if (runqueue.num_active > 2) {
|
||||
dbg_print("PID:%3d, PRIO:%3d, VRUNTIME:%9d, SUM_EXEC:%9d\n",
|
||||
next_process->pid, next_process->se.prio,
|
||||
next_process->se.vruntime, next_process->se.sum_exec_runtime);
|
||||
}
|
||||
|
||||
//==== Context switch ======================================================
|
||||
// Update the context of the current process.
|
||||
update_context(f, runqueue.curr);
|
||||
// Check if the next and current processes are different.
|
||||
if (next_process != runqueue.curr) {
|
||||
// Copy into Kernel stack the next process's context.
|
||||
do_switch(next_process, f);
|
||||
runqueue.curr->se.sum_exec_runtime = get_millisecond();
|
||||
// Update the last context switch time of the next process.
|
||||
next_process->se.exec_start = get_millisecond();
|
||||
}
|
||||
//==========================================================================
|
||||
|
||||
// Update the start execution time if it is executed for the first time
|
||||
if (next_process->se.start_runtime == 0)
|
||||
next_process->se.start_runtime = get_millisecond();
|
||||
}
|
||||
|
||||
void update_context(pt_regs *f, task_struct *process)
|
||||
{
|
||||
// Store the registers.
|
||||
process->thread.gs = f->gs;
|
||||
process->thread.fs = f->fs;
|
||||
process->thread.es = f->es;
|
||||
process->thread.ds = f->ds;
|
||||
process->thread.edi = f->edi;
|
||||
process->thread.esi = f->esi;
|
||||
process->thread.ebp = f->ebp;
|
||||
process->thread.ebx = f->ebx;
|
||||
process->thread.edx = f->edx;
|
||||
process->thread.ecx = f->ecx;
|
||||
process->thread.eax = f->eax;
|
||||
process->thread.eip = f->eip;
|
||||
process->thread.eflags = f->eflags;
|
||||
process->thread.useresp = f->useresp;
|
||||
// TODO: Check if the following registers should be saved.
|
||||
// process->thread.cs = f->cs;
|
||||
// process->thread.ss = f->ss;
|
||||
// Store the FPU.
|
||||
switch_fpu();
|
||||
}
|
||||
|
||||
void do_switch(task_struct *process, pt_regs *f)
|
||||
{
|
||||
// Switch to the next process.
|
||||
runqueue.curr = process;
|
||||
// Restore the registers.
|
||||
f->gs = process->thread.gs;
|
||||
f->fs = process->thread.fs;
|
||||
f->es = process->thread.es;
|
||||
f->ds = process->thread.ds;
|
||||
f->edi = process->thread.edi;
|
||||
f->esi = process->thread.esi;
|
||||
f->ebp = process->thread.ebp;
|
||||
f->ebx = process->thread.ebx;
|
||||
f->edx = process->thread.edx;
|
||||
f->ecx = process->thread.ecx;
|
||||
f->eax = process->thread.eax;
|
||||
f->eip = process->thread.eip;
|
||||
f->eflags = process->thread.eflags;
|
||||
f->useresp = process->thread.useresp;
|
||||
// TODO: Check if the following registers should be restored.
|
||||
// f->cs = process->thread.cs;
|
||||
// f->ss = process->thread.ss;
|
||||
// Restore the FPU.
|
||||
unswitch_fpu();
|
||||
}
|
||||
|
||||
int set_user_nice(task_struct *p, long nice)
|
||||
{
|
||||
if (PRIO_TO_NICE(p->se.prio) != nice && nice >= MIN_NICE &&
|
||||
nice <= MAX_NICE) {
|
||||
p->se.prio = NICE_TO_PRIO(nice);
|
||||
}
|
||||
|
||||
return PRIO_TO_NICE(p->se.prio);
|
||||
}
|
||||
|
||||
void enter_user_jmp(uintptr_t location, uintptr_t stack)
|
||||
{
|
||||
// Reset stack pointer for kernel.
|
||||
tss_set_stack(0x10, initial_esp);
|
||||
|
||||
// update start execution time.
|
||||
runqueue.curr->se.start_runtime = get_millisecond();
|
||||
|
||||
// last context switch time.
|
||||
runqueue.curr->se.exec_start = get_millisecond();
|
||||
|
||||
// Jump in location.
|
||||
enter_userspace(location, stack);
|
||||
}
|
||||
|
||||
pid_t sys_getpid()
|
||||
{
|
||||
// Get the current task.
|
||||
if (runqueue.curr == NULL) {
|
||||
kernel_panic("There is no current process!");
|
||||
}
|
||||
|
||||
// Return the process identifer of the process.
|
||||
return runqueue.curr->pid;
|
||||
}
|
||||
|
||||
pid_t sys_getppid()
|
||||
{
|
||||
// Get the current task.
|
||||
if (runqueue.curr == NULL) {
|
||||
kernel_panic("There is no current process!");
|
||||
}
|
||||
if (runqueue.curr->parent == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Return the parent process identifer of the process.
|
||||
return runqueue.curr->parent->pid;
|
||||
}
|
||||
|
||||
int sys_nice(int increment)
|
||||
{
|
||||
// Get the current task.
|
||||
if (runqueue.curr == NULL) {
|
||||
kernel_panic("There is no current process!");
|
||||
}
|
||||
|
||||
if (increment < -40) {
|
||||
increment = -40;
|
||||
}
|
||||
if (increment > 40) {
|
||||
increment = 40;
|
||||
}
|
||||
|
||||
int newNice = PRIO_TO_NICE(runqueue.curr->se.prio) + increment;
|
||||
dbg_print("New nice value would be : %d\n", newNice);
|
||||
|
||||
if (newNice < MIN_NICE) {
|
||||
newNice = MIN_NICE;
|
||||
}
|
||||
if (newNice > MAX_NICE) {
|
||||
newNice = MAX_NICE;
|
||||
}
|
||||
|
||||
int actualNice = set_user_nice(runqueue.curr, newNice);
|
||||
dbg_print("Actual new nice value is: %d\n", actualNice);
|
||||
|
||||
return actualNice;
|
||||
}
|
||||
|
||||
pid_t sys_waitpid(pid_t pid, int *status, int options)
|
||||
{
|
||||
// Get the current task.
|
||||
if (runqueue.curr == NULL) {
|
||||
kernel_panic("There is no current process!");
|
||||
}
|
||||
|
||||
/* For now we do not support waiting for processes inside the given
|
||||
* process group (pid < -1).
|
||||
*/
|
||||
if ((pid < -1) || (pid == 0)) {
|
||||
errno = ESRCH;
|
||||
|
||||
return (-1);
|
||||
}
|
||||
if (pid == runqueue.curr->pid) {
|
||||
errno = ECHILD;
|
||||
|
||||
return (-1);
|
||||
}
|
||||
if (options != 0 && options != WNOHANG) {
|
||||
errno = EINVAL;
|
||||
|
||||
return (-1);
|
||||
}
|
||||
if (status == NULL) {
|
||||
errno = EFAULT;
|
||||
|
||||
return (-1);
|
||||
}
|
||||
list_head *it;
|
||||
list_for_each (it, &runqueue.curr->children) {
|
||||
task_struct *entry = list_entry(it, task_struct, sibling);
|
||||
if (entry == NULL) {
|
||||
continue;
|
||||
}
|
||||
if (entry->state != EXIT_ZOMBIE) {
|
||||
continue;
|
||||
}
|
||||
if ((pid > 1) && (entry->pid != pid)) {
|
||||
continue;
|
||||
}
|
||||
// Save the pid to return.
|
||||
pid_t ppid = entry->pid;
|
||||
// Save the state.
|
||||
(*status) = entry->state; //TODO: da rivedere
|
||||
// Remove entry from children of parent.
|
||||
list_head_del(&entry->sibling);
|
||||
// Delete the task_struct.
|
||||
kfree(entry);
|
||||
dbg_print("Freeing memory of process %d.\n", ppid);
|
||||
|
||||
return ppid;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sys_exit(int exit_code)
|
||||
{
|
||||
// Get the current task.
|
||||
if (runqueue.curr == NULL) {
|
||||
kernel_panic("There is no current process!");
|
||||
}
|
||||
|
||||
task_struct *init_proc = kernel_get_running_process(1);
|
||||
if (runqueue.curr == init_proc) {
|
||||
kernel_panic("Init process cannot call sys_exit!");
|
||||
}
|
||||
// Set the termination code of the process.
|
||||
runqueue.curr->exit_code = (exit_code << 8) & 0xFF00;
|
||||
// Set the state of the process to zombie.
|
||||
runqueue.curr->state = EXIT_ZOMBIE;
|
||||
// If it has children, then init process has to take care of them.
|
||||
if (!list_head_empty(&runqueue.curr->children)) {
|
||||
dbg_print("Moving children of %s(%d) to init(%d): {\n",
|
||||
runqueue.curr->name, runqueue.curr->pid, init_proc->pid);
|
||||
// TODO: Try to plug the list of children instead of iterating.
|
||||
list_head *it;
|
||||
list_for_each (it, &runqueue.curr->children) {
|
||||
task_struct *entry = list_entry(it, task_struct, sibling);
|
||||
dbg_print(" [%d] %s\n", entry->pid, entry->name);
|
||||
it = entry->sibling.prev;
|
||||
list_head_del(&entry->sibling);
|
||||
list_head_add_tail(&init_proc->children, &entry->sibling);
|
||||
entry->parent = init_proc;
|
||||
}
|
||||
dbg_print("}\n");
|
||||
dbg_print("Listing children of init(%d): {\n", init_proc->pid);
|
||||
list_for_each (it, &init_proc->children) {
|
||||
task_struct *entry = list_entry(it, task_struct, sibling);
|
||||
dbg_print(" [%d] %s\n", entry->pid, entry->name);
|
||||
}
|
||||
dbg_print("}\n");
|
||||
}
|
||||
// Free the space occupied by the stack.
|
||||
destroy_process_image(runqueue.curr->mm);
|
||||
// Debugging message.
|
||||
dbg_print("Process %d exited with value %d\n", runqueue.curr->pid,
|
||||
exit_code);
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/// @file scheduler_algorithm.c
|
||||
/// @brief Round Robin algorithm.
|
||||
/// @date Mar 2019.
|
||||
|
||||
#include "prio.h"
|
||||
#include "debug.h"
|
||||
#include "assert.h"
|
||||
#include "list_head.h"
|
||||
#include "scheduler.h"
|
||||
|
||||
#define GET_WEIGHT(prio) prio_to_weight[USER_PRIO((prio))]
|
||||
#define NICE_0_LOAD GET_WEIGHT(DEFAULT_PRIO)
|
||||
|
||||
task_struct *pick_next_task(runqueue_t *runqueue, time_t delta_exec)
|
||||
{
|
||||
// Pointer to the next task to schedule.
|
||||
task_struct *next = NULL;
|
||||
|
||||
#if defined(SCHEDULER_RR)
|
||||
//==== Implementatin of the Round-Robin Scheduling algorithm ============
|
||||
|
||||
|
||||
|
||||
//=======================================================================
|
||||
#elif defined(SCHEDULER_PRIORITY)
|
||||
//==== Implementatin of the Priority Scheduling algorithm ===============
|
||||
|
||||
// get the first element of the list
|
||||
next = list_entry(/*...*/);
|
||||
|
||||
// Get its static priority.
|
||||
time_t min = /*...*/
|
||||
|
||||
list_head *it;
|
||||
// Inter over the runqueue to find the task with the smallest priority value
|
||||
list_for_each (it, &runqueue->queue) {
|
||||
task_struct *entry = list_entry(/*...*/);
|
||||
// Check entry has a lower priority
|
||||
if (/*...*/) {
|
||||
/*...*/
|
||||
}
|
||||
}
|
||||
|
||||
//=======================================================================
|
||||
#elif defined(SCHEDULER_CFS)
|
||||
//==== Implementatin of the Completely Fair Scheduling ==================
|
||||
|
||||
// Get the weight of the current process.
|
||||
// (use GET_WEIGHT macro!)
|
||||
int weight = /*...*/
|
||||
|
||||
if (weight != NICE_0_LOAD) {
|
||||
// get the multiplicative factor for its delta_exec.
|
||||
double factor = /*...*/
|
||||
|
||||
// weight the delta_exec with the multiplicative factor.
|
||||
delta_exec = // ...
|
||||
}
|
||||
|
||||
// Update vruntime of the current process.
|
||||
// ...
|
||||
|
||||
// Inter over the runqueue to find the task with the smallest vruntime value
|
||||
// ...
|
||||
|
||||
//========================================================================
|
||||
#else
|
||||
#error "You should enable a scheduling algorithm!"
|
||||
#endif
|
||||
assert(next && "No valid task selected. Have you implemented a scheduling algorithm?");
|
||||
|
||||
return next;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
; MentOS, The Mentoring Operating system project
|
||||
; @file user.asm
|
||||
; @brief
|
||||
; @copyright (c) 2019 This file is distributed under the MIT License.
|
||||
; See LICENSE.md for details.
|
||||
|
||||
; Enter userspace (ring3) (from Ring 0, namely Kernel)
|
||||
; Usage: enter_userspace(uintptr_t location, uintptr_t stack);
|
||||
; On stack
|
||||
; | stack |
|
||||
; | location |
|
||||
; | return address |
|
||||
; | EBP | EBP
|
||||
; | SS |
|
||||
; | ESP |
|
||||
; | EFLAGS |
|
||||
; | CS |
|
||||
; | EIP |
|
||||
|
||||
global enter_userspace ; Allows the C code to call enter_userspace(...).
|
||||
enter_userspace:
|
||||
|
||||
push ebp ; Save current ebp
|
||||
mov ebp, esp ; open a new stack frame
|
||||
|
||||
;==== Segment selector =====================================================
|
||||
mov ax, 0x23
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
; we don't need to worry about SS. it's handled by iret
|
||||
;---------------------------------------------------------------------------
|
||||
|
||||
; We have to prepare the following stack before executing iret
|
||||
; SS --> Segment selector
|
||||
; ESP --> Stack address
|
||||
; EFLAGS --> CPU state flgas
|
||||
; CS --> Code segment
|
||||
; EIP --> Entry point
|
||||
;
|
||||
|
||||
;==== User data segmenet with bottom 2 bits set for ring3 ?=================
|
||||
push 0x23 ; push SS on Kernel's stack
|
||||
;---------------------------------------------------------------------------
|
||||
|
||||
;==== (ESP) Stack address ==================================================
|
||||
mov eax, [ebp + 0xC] ; get uintptr_t stack
|
||||
push eax ; push process's stack address on Kernel's stack
|
||||
;---------------------------------------------------------------------------
|
||||
|
||||
;==== (EFLAGS) =============================================================
|
||||
pushf ; push EFLAGS into Kernel's stack
|
||||
pop eax ; pop EFLAGS into eax
|
||||
or eax, 0x200 ; enable interrupt ?request ring3
|
||||
push eax ; push new EFLAGS on Kernel's stack
|
||||
;---------------------------------------------------------------------------
|
||||
|
||||
;==== (CS) Code Segment ====================================================
|
||||
push 0x1B ;
|
||||
;---------------------------------------------------------------------------
|
||||
|
||||
;==== (EIP) Entry point ====================================================
|
||||
mov eax, [ebp + 0x8] ; get uintptr_t location
|
||||
push eax ; push uintptr_t location on Kernel's stack
|
||||
;---------------------------------------------------------------------------
|
||||
|
||||
iret ; interrupt return
|
||||
|
||||
; WE SHOULD NOT STILL BE HERE! :(
|
||||
|
||||
;==== Reset segment selector ===============================================
|
||||
mov ax, 0x10
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
;---------------------------------------------------------------------------
|
||||
|
||||
add esp, 0x14 ; reset stack pointer (20 bytes)
|
||||
pop ebp ; reset value of ebp
|
||||
ret ; return to kernel code
|
||||
Reference in New Issue
Block a user