Update MentOs code to the latest development version.
This commit is contained in:
+447
-257
@@ -1,308 +1,498 @@
|
||||
/// 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.
|
||||
/// @copyright (c) 2014-2021 This file is distributed under the MIT License.
|
||||
/// See LICENSE.md for details.
|
||||
|
||||
|
||||
#include "kernel_levels.h"
|
||||
|
||||
//#ifndef __DEBUG_LEVEL__
|
||||
//#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG
|
||||
//#endif
|
||||
|
||||
#include <errno.h>
|
||||
#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"
|
||||
#include "assert.h"
|
||||
#include "libgen.h"
|
||||
#include "string.h"
|
||||
#include "timer.h"
|
||||
#include "fcntl.h"
|
||||
#include "panic.h"
|
||||
#include "debug.h"
|
||||
#include "wait.h"
|
||||
#include "prio.h"
|
||||
#include "vfs.h"
|
||||
#include "elf.h"
|
||||
|
||||
#define PUSH_ON_STACK(stack, type, item) \
|
||||
*((type *)(stack -= sizeof(type))) = item
|
||||
|
||||
/// Cache for creating the task structs.
|
||||
static kmem_cache_t *task_struct_cache;
|
||||
/// @brief The task_struct of the init process.
|
||||
static task_struct *init_proc;
|
||||
|
||||
void exit_handler()
|
||||
static inline int __push_args_on_stack(uintptr_t *esp, char *args[], char ***argsptr)
|
||||
{
|
||||
exit(1);
|
||||
kernel_panic("I should not be here.\n");
|
||||
int argc = 0;
|
||||
char *args_ptr[256];
|
||||
// Count the number of arguments.
|
||||
while (args[argc] != NULL) {
|
||||
++argc;
|
||||
}
|
||||
|
||||
// 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_ARG((*esp), char, args[i][j]);
|
||||
}
|
||||
args_ptr[i] = (char *)(*esp);
|
||||
}
|
||||
// Push terminating NULL.
|
||||
PUSH_ARG((*esp), char *, (char *)NULL);
|
||||
// Push array of pointers to the arguments.
|
||||
for (int i = argc - 1; i >= 0; --i) {
|
||||
PUSH_ARG((*esp), char *, args_ptr[i]);
|
||||
}
|
||||
(*argsptr) = (char **)(*esp);
|
||||
|
||||
return argc;
|
||||
}
|
||||
|
||||
task_struct *create_init_process()
|
||||
static int __reset_process(task_struct *task)
|
||||
{
|
||||
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', PATH_MAX);
|
||||
// Set the state of the process as running.
|
||||
init_proc->state = TASK_RUNNING;
|
||||
// Active the current process.
|
||||
enqueue_task(init_proc);
|
||||
pr_debug("__reset_process(%p `%s`)\n", task, task->name);
|
||||
// Create a new stack segment.
|
||||
task->mm = create_blank_process_image(DEFAULT_STACK_SIZE);
|
||||
if (task->mm == NULL) {
|
||||
pr_err("Failed to initialize process mm structure.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
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");
|
||||
// Save the current page directory.
|
||||
page_directory_t *crtdir = paging_get_current_directory();
|
||||
// FIXME: Now to clear the stack a pgdir switch is made, it should be a kernel mmapping.
|
||||
paging_switch_directory_va(task->mm->pgd);
|
||||
|
||||
return init_proc;
|
||||
// Clean stack space.
|
||||
memset((char *)task->mm->start_stack, 0, DEFAULT_STACK_SIZE);
|
||||
// Set the base address of the stack.
|
||||
task->thread.regs.ebp = (uintptr_t)(task->mm->start_stack + DEFAULT_STACK_SIZE);
|
||||
// Set the top address of the stack.
|
||||
task->thread.regs.useresp = task->thread.regs.ebp;
|
||||
// Enable the interrupts.
|
||||
task->thread.regs.eflags = task->thread.regs.eflags | EFLAG_IF;
|
||||
|
||||
// Restore previous pgdir
|
||||
paging_switch_directory(crtdir);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
char *get_current_dir_name()
|
||||
static int __load_executable(const char *path, task_struct *task, uint32_t *entry)
|
||||
{
|
||||
task_struct *current_process = kernel_get_current_process();
|
||||
if (current_process != NULL) {
|
||||
return strdup(current_process->cwd);
|
||||
}
|
||||
|
||||
return kstrdup("/");
|
||||
pr_debug("__load_executable(`%s`, %p `%s`, %p)\n", path, task, task->name, entry);
|
||||
vfs_file_t *file = vfs_open(path, O_RDONLY, 0);
|
||||
if (file == NULL) {
|
||||
pr_err("Cannot find executable!\n");
|
||||
return 0;
|
||||
}
|
||||
// Check that the file is actually an executable before destroying the `mm`.
|
||||
if (!elf_check_file_type(file, ET_EXEC)) {
|
||||
pr_err("This is not a valid ELF executable `%s`!\n", path);
|
||||
return 0;
|
||||
}
|
||||
// FIXME: When threads will be implemented
|
||||
// they should share the mm, so the destroy_process_image must be called
|
||||
// only when all the threads are terminated. This can be accomplished by using
|
||||
// an internal counter on the mm.
|
||||
if (task->mm)
|
||||
destroy_process_image(task->mm);
|
||||
// Return code variable.
|
||||
int ret = 0;
|
||||
// Recreate the memory of the process.
|
||||
if (__reset_process(task)) {
|
||||
// Load the elf file, check if 0 is returned and print the error.
|
||||
if (!(ret = elf_load_file(task, file, entry))) {
|
||||
pr_err("Failed to load ELF file `%s`!\n", path);
|
||||
}
|
||||
}
|
||||
// Close the file.
|
||||
vfs_close(file);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void sys_getcwd(char *path, size_t size)
|
||||
static inline int __count_args_bytes(char **args)
|
||||
{
|
||||
task_struct *current_process = kernel_get_current_process();
|
||||
if ((current_process != NULL) && (path != NULL)) {
|
||||
strncpy(path, current_process->cwd, size);
|
||||
}
|
||||
int argc = 0;
|
||||
int bytes = 0;
|
||||
|
||||
// Count the number of arguments.
|
||||
while (args[argc] != NULL) {
|
||||
++argc;
|
||||
}
|
||||
|
||||
for (int i = 0; i < argc; i++) {
|
||||
bytes += strlen(args[i]) + 1;
|
||||
}
|
||||
|
||||
return bytes + (argc + 1 /* The NULL terminator */) * sizeof(char *);
|
||||
}
|
||||
|
||||
static inline task_struct *__alloc_task(task_struct *source, task_struct *parent, const char *name)
|
||||
{
|
||||
// Create a new task_struct.
|
||||
task_struct *proc = kmem_cache_alloc(task_struct_cache, GFP_KERNEL);
|
||||
// Clear the memory.
|
||||
memset(proc, 0, sizeof(task_struct));
|
||||
// Set the id of the process.
|
||||
proc->pid = scheduler_getpid();
|
||||
// Set the state of the process as running.
|
||||
proc->state = TASK_RUNNING;
|
||||
// Set the current opened file descriptors and the maximum number of file descriptors.
|
||||
if (source)
|
||||
vfs_dup_task(proc, source);
|
||||
else
|
||||
vfs_init_task(proc);
|
||||
// Set the pointer to process's parent.
|
||||
proc->parent = parent;
|
||||
// Initialize the list_head.
|
||||
list_head_init(&proc->run_list);
|
||||
// Initialize the children list_head.
|
||||
list_head_init(&proc->children);
|
||||
// Initialize the sibling list_head.
|
||||
list_head_init(&proc->sibling);
|
||||
// If we have a parent, set the sibling child relation.
|
||||
if (parent) {
|
||||
// Set the new_process as child of current.
|
||||
list_head_add_tail(&proc->sibling, &parent->children);
|
||||
}
|
||||
if (source)
|
||||
memcpy(&proc->thread, &source->thread, sizeof(thread_struct_t));
|
||||
// Set the statistics of the process.
|
||||
proc->uid = 0;
|
||||
proc->sid = 0;
|
||||
proc->gid = 0;
|
||||
proc->se.prio = DEFAULT_PRIO;
|
||||
proc->se.start_runtime = timer_get_ticks();
|
||||
proc->se.exec_start = timer_get_ticks();
|
||||
proc->se.exec_runtime = 0;
|
||||
proc->se.sum_exec_runtime = 0;
|
||||
proc->se.vruntime = 0;
|
||||
proc->se.period = 0;
|
||||
proc->se.deadline = 0;
|
||||
proc->se.arrivaltime = timer_get_ticks();
|
||||
proc->se.executed = false;
|
||||
proc->se.is_periodic = false;
|
||||
proc->se.is_under_analysis = false;
|
||||
proc->se.next_period = 0;
|
||||
proc->se.worst_case_exec = 0;
|
||||
proc->se.utilization_factor = 0;
|
||||
// Initialize the exit code of the process.
|
||||
proc->exit_code = 0;
|
||||
// Copy the name.
|
||||
if (name)
|
||||
strcpy(proc->name, name);
|
||||
// Do not touch the task's segments.
|
||||
proc->mm = NULL;
|
||||
// Initialize the error number.
|
||||
proc->error_no = 0;
|
||||
// Initialize the current working directory.
|
||||
if (source)
|
||||
strcpy(proc->cwd, source->cwd);
|
||||
else
|
||||
strcpy(proc->cwd, "/");
|
||||
// Clear the signal handler.
|
||||
memset(&proc->sighand, 0x00, sizeof(sighand_t));
|
||||
spinlock_init(&proc->sighand.siglock);
|
||||
atomic_set(&proc->sighand.count, 0);
|
||||
for (int i = 0; i < NSIG; ++i) {
|
||||
proc->sighand.action[i].sa_handler = SIG_DFL;
|
||||
sigemptyset(&proc->sighand.action[i].sa_mask);
|
||||
proc->sighand.action[i].sa_flags = 0;
|
||||
}
|
||||
// Clear the masks.
|
||||
sigemptyset(&proc->blocked);
|
||||
sigemptyset(&proc->real_blocked);
|
||||
sigemptyset(&proc->saved_sigmask);
|
||||
// Initialzie the data structure storing the pending signals.
|
||||
list_head_init(&proc->pending.list);
|
||||
sigemptyset(&proc->pending.signal);
|
||||
|
||||
// Initalize real_timer for intervals
|
||||
proc->real_timer = NULL;
|
||||
|
||||
return proc;
|
||||
}
|
||||
|
||||
int init_tasking()
|
||||
{
|
||||
if ((task_struct_cache = KMEM_CREATE(task_struct)) == NULL) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
task_struct *process_create_init(const char *path)
|
||||
{
|
||||
pr_debug("Building init process...\n");
|
||||
// Allocate the memory for the process.
|
||||
init_proc = __alloc_task(NULL, NULL, "init");
|
||||
|
||||
// == INITIALIZE `/proc/video` ============================================
|
||||
// Check that the fd_list is initialized.
|
||||
assert(init_proc->fd_list && "File descriptor list not initialized.");
|
||||
assert((init_proc->max_fd > 3) && "File descriptor list cannot contain the standard IOs.");
|
||||
|
||||
// Create STDIN descriptor.
|
||||
vfs_file_t *stdin = vfs_open("/proc/video", O_RDONLY, 0);
|
||||
stdin->count++;
|
||||
init_proc->fd_list[STDIN_FILENO].file_struct = stdin;
|
||||
init_proc->fd_list[STDIN_FILENO].flags_mask = O_RDONLY;
|
||||
pr_debug("`/proc/video` stdin : %p\n", stdin);
|
||||
|
||||
// Create STDOUT descriptor.
|
||||
vfs_file_t *stdout = vfs_open("/proc/video", O_WRONLY, 0);
|
||||
stdout->count++;
|
||||
init_proc->fd_list[STDOUT_FILENO].file_struct = stdout;
|
||||
init_proc->fd_list[STDOUT_FILENO].flags_mask = O_WRONLY;
|
||||
pr_debug("`/proc/video` stdout : %p\n", stdout);
|
||||
|
||||
// Create STDERR descriptor.
|
||||
vfs_file_t *stderr = vfs_open("/proc/video", O_WRONLY, 0);
|
||||
stderr->count++;
|
||||
init_proc->fd_list[STDERR_FILENO].file_struct = stderr;
|
||||
init_proc->fd_list[STDERR_FILENO].flags_mask = O_WRONLY;
|
||||
pr_debug("`/proc/video` stderr : %p\n", stderr);
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
// == INITIALIZE TASK MEMORY ==============================================
|
||||
// Load the executable.
|
||||
if (!__load_executable(path, init_proc, &init_proc->thread.regs.eip)) {
|
||||
pr_err("Entry for init: %d\n", init_proc->thread.regs.eip);
|
||||
kernel_panic("Init not valid (%d)!");
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
// == INITIALIZE PROGRAM ARGUMENTS ========================================
|
||||
// Save the current page directory.
|
||||
page_directory_t *crtdir = paging_get_current_directory();
|
||||
// Switch to init page directory.
|
||||
paging_switch_directory_va(init_proc->mm->pgd);
|
||||
|
||||
// Prepare argv and envp for the init process.
|
||||
char **argv_ptr, **envp_ptr;
|
||||
int argc = 1;
|
||||
static char *argv[] = {
|
||||
"/bin/init",
|
||||
(char *)NULL
|
||||
};
|
||||
static char *envp[] = {
|
||||
(char *)NULL
|
||||
};
|
||||
// Save where the arguments start.
|
||||
init_proc->mm->arg_start = init_proc->thread.regs.useresp;
|
||||
// Push the arguments on the stack.
|
||||
__push_args_on_stack(&init_proc->thread.regs.useresp, argv, &argv_ptr);
|
||||
// Save where the arguments end.
|
||||
init_proc->mm->arg_end = init_proc->thread.regs.useresp;
|
||||
// Save where the environmental variables start.
|
||||
init_proc->mm->env_start = init_proc->thread.regs.useresp;
|
||||
// Push the environment on the stack.
|
||||
__push_args_on_stack(&init_proc->thread.regs.useresp, envp, &envp_ptr);
|
||||
// Save where the environmental variables end.
|
||||
init_proc->mm->env_end = init_proc->thread.regs.useresp;
|
||||
// Push the `main` arguments on the stack (argc, argv, envp).
|
||||
PUSH_ARG(init_proc->thread.regs.useresp, char **, envp_ptr);
|
||||
PUSH_ARG(init_proc->thread.regs.useresp, char **, argv_ptr);
|
||||
PUSH_ARG(init_proc->thread.regs.useresp, int, argc);
|
||||
|
||||
// Restore previous pgdir
|
||||
paging_switch_directory(crtdir);
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
// Active the current process.
|
||||
scheduler_enqueue_task(init_proc);
|
||||
|
||||
pr_debug("Executing '%s' (pid: %d)...\n", init_proc->name, init_proc->pid);
|
||||
|
||||
return init_proc;
|
||||
}
|
||||
|
||||
char *sys_getcwd(char *buf, size_t size)
|
||||
{
|
||||
task_struct *current_process = scheduler_get_current_process();
|
||||
if ((current_process != NULL) && (buf != NULL)) {
|
||||
strncpy(buf, current_process->cwd, size);
|
||||
return buf;
|
||||
}
|
||||
return (char *)-EACCES;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
task_struct *current_process = scheduler_get_current_process();
|
||||
if ((current_process != NULL) && (path != NULL)) {
|
||||
char absolute_path[PATH_MAX];
|
||||
realpath(path, absolute_path);
|
||||
strcpy(current_process->cwd, absolute_path);
|
||||
}
|
||||
}
|
||||
|
||||
pid_t sys_vfork(pt_regs *r)
|
||||
void sys_fchdir(int fd)
|
||||
{
|
||||
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;
|
||||
// Get the current task.
|
||||
task_struct *task = scheduler_get_current_process();
|
||||
// Check the current FD.
|
||||
if (fd >= 0 && fd < task->max_fd) {
|
||||
// Get the file descriptor.
|
||||
vfs_file_descriptor_t *vfd = &task->fd_list[fd];
|
||||
// Check the file.
|
||||
if (vfd->file_struct != NULL) {
|
||||
char absolute_path[PATH_MAX];
|
||||
realpath(vfd->file_struct->name, absolute_path);
|
||||
strcpy(task->cwd, absolute_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline int push_args_on_stack(uintptr_t *esp, char *args[],
|
||||
char ***argsptr)
|
||||
pid_t sys_fork(pt_regs *f)
|
||||
{
|
||||
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);
|
||||
task_struct *current = scheduler_get_current_process();
|
||||
if (current == NULL)
|
||||
kernel_panic("There is no current process!");
|
||||
|
||||
return argc;
|
||||
pr_debug("Forking '%s' (pid: %d)...\n", current->name, current->pid);
|
||||
|
||||
// Update current process registers, they should be equal
|
||||
// to the ones of the child process, except for eax.
|
||||
scheduler_store_context(f, current);
|
||||
// Allocate the memory for the process.
|
||||
task_struct *proc = __alloc_task(current, current, current->name);
|
||||
// Copy the father's stack, memory, heap etc... to the child process
|
||||
proc->mm = clone_process_image(current->mm);
|
||||
// Set the eax as 0, to indicate the child process
|
||||
proc->thread.regs.eax = 0;
|
||||
// Enable the interrupts.
|
||||
proc->thread.regs.eflags = proc->thread.regs.eflags | EFLAG_IF;
|
||||
|
||||
// Copy session and group id of the parent into the child
|
||||
proc->sid = current->sid;
|
||||
proc->gid = current->gid;
|
||||
|
||||
// Active the new process.
|
||||
scheduler_enqueue_task(proc);
|
||||
|
||||
pr_debug("Forked '%s' (pid: %d, gid: %d, sid: %d)...\n", proc->name, proc->pid, proc->gid, proc->sid);
|
||||
|
||||
// Return PID of child process to parent.
|
||||
return proc->pid;
|
||||
}
|
||||
|
||||
int sys_execve(pt_regs *r)
|
||||
int sys_execve(pt_regs *f)
|
||||
{
|
||||
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!");
|
||||
}
|
||||
// Check the current process.
|
||||
task_struct *current = scheduler_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;
|
||||
}
|
||||
char **origin_argv, **saved_argv, **final_argv;
|
||||
char **origin_envp, **saved_envp, **final_envp;
|
||||
char name_buffer[NAME_MAX];
|
||||
|
||||
// Get the arguments.
|
||||
argv = (char **)r->ecx;
|
||||
// Get the environment.
|
||||
envp = (char **)r->edx;
|
||||
// Get the filename.
|
||||
char *filename = (char *)f->ebx;
|
||||
if (filename == NULL) {
|
||||
pr_err("Received NULL filename.\n");
|
||||
return -1;
|
||||
}
|
||||
// Get the arguments
|
||||
origin_argv = (char **)f->ecx;
|
||||
// Get the environment.
|
||||
origin_envp = (char **)f->edx;
|
||||
// Check the argument, the environment, and that at least the name is provided.
|
||||
if (origin_argv == NULL) {
|
||||
pr_err("sys_execve failed: must provide argv.\n");
|
||||
return -1;
|
||||
}
|
||||
if (origin_argv[0] == NULL) {
|
||||
pr_err("sys_execve failed: must provide the name.\n");
|
||||
return -1;
|
||||
}
|
||||
if (origin_envp == NULL) {
|
||||
pr_err("sys_execve failed: must provide the environment.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Check the argument and that at least the name is provided.
|
||||
if ((argv == NULL) || (argv[0] == NULL)) {
|
||||
return -1;
|
||||
}
|
||||
// Save the name of the process.
|
||||
strcpy(name_buffer, origin_argv[0]);
|
||||
|
||||
// Check that the environment is provided.
|
||||
if (envp == NULL) {
|
||||
kernel_panic("You must provide at least an empty list for envp!");
|
||||
}
|
||||
// == COPY PROGRAM ARGUMENTS ==============================================
|
||||
// Copy argv and envp to kernel memory, because all the old process memory will be discarded.
|
||||
int argv_bytes = __count_args_bytes(origin_argv);
|
||||
int envp_bytes = __count_args_bytes(origin_envp);
|
||||
if ((argv_bytes < 0) || (envp_bytes < 0)) {
|
||||
pr_err("Failed to count required memory to store arguments and environment (%d + %d).\n",
|
||||
argv_bytes, envp_bytes);
|
||||
return -1;
|
||||
}
|
||||
void *args_mem = kmalloc(argv_bytes + envp_bytes);
|
||||
if (!args_mem) {
|
||||
pr_err("Failed to allocate memory for arguments and environment %d (%d + %d).\n",
|
||||
argv_bytes + envp_bytes, argv_bytes, envp_bytes);
|
||||
return -1;
|
||||
}
|
||||
// Copy the arguments.
|
||||
uint32_t args_mem_ptr = (uint32_t)args_mem + (argv_bytes + envp_bytes);
|
||||
__push_args_on_stack(&args_mem_ptr, origin_argv, &saved_argv);
|
||||
__push_args_on_stack(&args_mem_ptr, origin_envp, &saved_envp);
|
||||
// Check the memory pointer.
|
||||
assert(args_mem_ptr == (uint32_t)args_mem);
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
// Set the name.
|
||||
strcpy(current->name, argv[0]);
|
||||
// == INITIALIZE TASK MEMORY ==============================================
|
||||
if (!__load_executable(filename, current, ¤t->thread.regs.eip)) {
|
||||
pr_err("Failed to load executable!\n");
|
||||
// Free the temporary args memory.
|
||||
kfree(args_mem);
|
||||
return -1;
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
// Set the top address of the stack.
|
||||
current->thread.useresp = (uintptr_t)current->thread.ebp;
|
||||
// == INITIALIZE PROGRAM ARGUMENTS ========================================
|
||||
// Save the current page directory.
|
||||
page_directory_t *crtdir = paging_get_current_directory();
|
||||
|
||||
// Set the program counter.
|
||||
current->thread.eip = (uintptr_t)filename;
|
||||
// Change the page directory to point to the newly created process
|
||||
paging_switch_directory_va(current->mm->pgd);
|
||||
|
||||
int argc = push_args_on_stack(¤t->thread.useresp, argv, &_argv);
|
||||
push_args_on_stack(¤t->thread.useresp, envp, &_envp);
|
||||
// Save where the arguments start.
|
||||
current->mm->arg_start = current->thread.regs.useresp;
|
||||
// Push the arguments on the stack.
|
||||
int argc = __push_args_on_stack(¤t->thread.regs.useresp, saved_argv, &final_argv);
|
||||
// Save where the arguments end, and the env starts.
|
||||
current->mm->env_start = current->mm->arg_end = current->thread.regs.useresp;
|
||||
// Push the environment on the stack.
|
||||
int envc = __push_args_on_stack(¤t->thread.regs.useresp, saved_envp, &final_envp);
|
||||
// Save where the environmental variables end.
|
||||
current->mm->env_end = current->thread.regs.useresp;
|
||||
// Push the `main` arguments on the stack (argc, argv, envp).
|
||||
PUSH_ARG(current->thread.regs.useresp, char **, final_envp);
|
||||
PUSH_ARG(current->thread.regs.useresp, char **, final_argv);
|
||||
PUSH_ARG(current->thread.regs.useresp, int, argc);
|
||||
|
||||
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);
|
||||
// Restore previous pgdir
|
||||
paging_switch_directory(crtdir);
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
// 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");
|
||||
// }
|
||||
// Change the name of the process.
|
||||
strcpy(current->name, name_buffer);
|
||||
|
||||
// Perform the switch to the new process.
|
||||
do_switch(current, r);
|
||||
// Free the temporary args memory.
|
||||
kfree(args_mem);
|
||||
|
||||
dbg_print("Executing '0x%p' for process %d with %d arguments (0x%p)...\n",
|
||||
filename, current->pid, argc, argv);
|
||||
// Perform the switch to the new process.
|
||||
scheduler_restore_context(current, f);
|
||||
|
||||
return 0;
|
||||
pr_debug("Executing '%s' (pid: %d)...\n", current->name, current->pid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
+587
-289
@@ -1,9 +1,15 @@
|
||||
/// 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.
|
||||
/// @copyright (c) 2014-2021 This file is distributed under the MIT License.
|
||||
/// See LICENSE.md for details.
|
||||
|
||||
/// Change the header.
|
||||
#define __DEBUG_HEADER__ "[SCHED ]"
|
||||
|
||||
#include "assert.h"
|
||||
#include "strerror.h"
|
||||
#include "vfs.h"
|
||||
#include "scheduler.h"
|
||||
#include "tss.h"
|
||||
#include "fpu.h"
|
||||
@@ -12,11 +18,13 @@
|
||||
#include "kheap.h"
|
||||
#include "panic.h"
|
||||
#include "debug.h"
|
||||
#include "clock.h"
|
||||
#include "time.h"
|
||||
#include "errno.h"
|
||||
#include "rbtree.h"
|
||||
#include "stdlib.h"
|
||||
#include "list_head.h"
|
||||
#include "paging.h"
|
||||
#include "timer.h"
|
||||
#include "math.h"
|
||||
#include "stdio.h"
|
||||
|
||||
/// @brief Assembly function setting the kernel stack to jump into
|
||||
/// location in Ring 3 mode (USER mode).
|
||||
@@ -27,357 +35,647 @@ extern void enter_userspace(uintptr_t location, uintptr_t stack);
|
||||
/// The list of processes.
|
||||
runqueue_t runqueue;
|
||||
|
||||
uint32_t get_new_pid(void)
|
||||
void scheduler_initialize()
|
||||
{
|
||||
/// The current unused PID.
|
||||
static unsigned long int tid = 1;
|
||||
|
||||
// Return the pid and increment.
|
||||
return tid++;
|
||||
// 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;
|
||||
}
|
||||
|
||||
task_struct *kernel_get_current_process()
|
||||
uint32_t scheduler_getpid(void)
|
||||
{
|
||||
return runqueue.curr;
|
||||
/// The current unused PID.
|
||||
static unsigned long int tid = 1;
|
||||
|
||||
// Return the pid and increment.
|
||||
return tid++;
|
||||
}
|
||||
|
||||
task_struct *kernel_get_running_process(pid_t pid)
|
||||
task_struct *scheduler_get_current_process()
|
||||
{
|
||||
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;
|
||||
return runqueue.curr;
|
||||
}
|
||||
|
||||
size_t kernel_get_active_processes()
|
||||
time_t scheduler_get_maximum_vruntime()
|
||||
{
|
||||
return runqueue.num_active;
|
||||
time_t vruntime = 0;
|
||||
task_struct *entry;
|
||||
list_for_each_decl(it, &runqueue.queue)
|
||||
{
|
||||
// Check if we reached the head of list_head, and skip it.
|
||||
if (it == &runqueue.queue)
|
||||
continue;
|
||||
// Get the current entry.
|
||||
entry = list_entry(it, task_struct, run_list);
|
||||
// Skip the process if it is a periodic one, we are issued to skip
|
||||
// periodic tasks, and the entry is not a periodic task under
|
||||
// analysis.
|
||||
if (entry->se.is_periodic && !entry->se.is_under_analysis)
|
||||
continue;
|
||||
if (entry->se.vruntime > vruntime)
|
||||
vruntime = entry->se.vruntime;
|
||||
}
|
||||
return vruntime;
|
||||
}
|
||||
|
||||
void kernel_initialize_scheduler()
|
||||
size_t scheduler_get_active_processes()
|
||||
{
|
||||
// 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;
|
||||
return runqueue.num_active;
|
||||
}
|
||||
|
||||
void enqueue_task(task_struct *process)
|
||||
task_struct *scheduler_get_running_process(pid_t pid)
|
||||
{
|
||||
// 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;
|
||||
task_struct *entry;
|
||||
list_for_each_decl(it, &runqueue.queue)
|
||||
{
|
||||
entry = list_entry(it, task_struct, run_list);
|
||||
if (entry->pid == pid)
|
||||
return entry;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void dequeue_task(task_struct *process)
|
||||
void scheduler_enqueue_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;
|
||||
// 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 kernel_schedule(pt_regs *f)
|
||||
void scheduler_dequeue_task(task_struct *process)
|
||||
{
|
||||
// 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();
|
||||
// Delete the process from the list of running processes.
|
||||
list_head_del(&process->run_list);
|
||||
// Decrement the number of active processes.
|
||||
--runqueue.num_active;
|
||||
if (process->se.is_periodic)
|
||||
runqueue.num_periodic--;
|
||||
}
|
||||
|
||||
void update_context(pt_regs *f, task_struct *process)
|
||||
void scheduler_run(pt_regs *f)
|
||||
{
|
||||
// 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();
|
||||
// Check if there is a running process.
|
||||
if (runqueue.curr == NULL)
|
||||
return;
|
||||
|
||||
task_struct *next = NULL;
|
||||
|
||||
// Update the context of the current process.
|
||||
scheduler_store_context(f, runqueue.curr);
|
||||
|
||||
// We check the existence of pending signals every time we finish
|
||||
// handling an interrupt or an exception.
|
||||
if (!do_signal(f)) {
|
||||
#if 1
|
||||
if (runqueue.curr->state == EXIT_ZOMBIE) {
|
||||
//==== Handle Zombies =================================================
|
||||
//pr_debug("Handle zombie %d\n", runqueue.curr->pid);
|
||||
// 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 = list_entry(nNode, task_struct, run_list);
|
||||
// Remove the zombie task.
|
||||
scheduler_dequeue_task(runqueue.curr);
|
||||
assert(next && "No valid task selected after removing ZOMBIE.");
|
||||
//=====================================================================
|
||||
} else {
|
||||
#endif
|
||||
//==== Scheduling =====================================================
|
||||
// If we are currently executing a periodic process, and this process
|
||||
// has yet to complete, keep executing it.
|
||||
#ifdef SCHEDULER_EDF
|
||||
if (runqueue.curr->se.is_periodic)
|
||||
if (!runqueue.curr->se.executed)
|
||||
return;
|
||||
#endif
|
||||
// Pointer to the next process to be executed.
|
||||
next = scheduler_pick_next_task(&runqueue);
|
||||
//=====================================================================
|
||||
}
|
||||
// Check if the next and current processes are different.
|
||||
if (next != runqueue.curr) {
|
||||
// Copy into Kernel stack the next process's context.
|
||||
scheduler_restore_context(next, f);
|
||||
}
|
||||
}
|
||||
//==========================================================================
|
||||
}
|
||||
|
||||
void do_switch(task_struct *process, pt_regs *f)
|
||||
void scheduler_store_context(pt_regs *f, task_struct *process)
|
||||
{
|
||||
// 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();
|
||||
// Store the registers.
|
||||
process->thread.regs = *f;
|
||||
}
|
||||
|
||||
int set_user_nice(task_struct *p, long nice)
|
||||
void scheduler_restore_context(task_struct *process, pt_regs *f)
|
||||
{
|
||||
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);
|
||||
// Switch to the next process.
|
||||
runqueue.curr = process;
|
||||
// Restore the registers.
|
||||
*f = process->thread.regs;
|
||||
// TODO: Explain paging switch (ring 0 doesn't need page switching)
|
||||
// Switch to process page directory
|
||||
paging_switch_directory_va(process->mm->pgd);
|
||||
}
|
||||
|
||||
void enter_user_jmp(uintptr_t location, uintptr_t stack)
|
||||
void scheduler_enter_user_jmp(uintptr_t location, uintptr_t stack)
|
||||
{
|
||||
// Reset stack pointer for kernel.
|
||||
tss_set_stack(0x10, initial_esp);
|
||||
// Reset stack pointer for kernel.
|
||||
tss_set_stack(0x10, initial_esp);
|
||||
|
||||
// update start execution time.
|
||||
runqueue.curr->se.start_runtime = get_millisecond();
|
||||
// update start execution time.
|
||||
runqueue.curr->se.start_runtime = timer_get_ticks();
|
||||
|
||||
// last context switch time.
|
||||
runqueue.curr->se.exec_start = get_millisecond();
|
||||
// last context switch time.
|
||||
runqueue.curr->se.exec_start = timer_get_ticks();
|
||||
|
||||
// Jump in location.
|
||||
enter_userspace(location, stack);
|
||||
// Jump in location.
|
||||
enter_userspace(location, stack);
|
||||
}
|
||||
|
||||
/// @brief Awakens a sleeping process.
|
||||
/// @param process The process that should be awakened
|
||||
/// @param mode The type of wait (TASK_INTERRUPTIBLE or TASK_UNINTERRUPTIBLE).
|
||||
/// @param sync Specifies if the wakeup should be synchronous.
|
||||
/// @return 1 on success, 0 on failure.
|
||||
static inline int try_to_wake_up(task_struct *process, int mode, int sync)
|
||||
{
|
||||
// Only tasks in the state TASK_UNINTERRUPTIBLE can be woke up
|
||||
if (process->state == TASK_UNINTERRUPTIBLE || process->state == TASK_STOPPED) {
|
||||
//TODO: Recalc task priority
|
||||
process->state = TASK_RUNNING;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int default_wake_function(wait_queue_entry_t *wait, unsigned mode, int sync)
|
||||
{
|
||||
task_struct *p = wait->task;
|
||||
return try_to_wake_up(p, mode, sync);
|
||||
}
|
||||
|
||||
wait_queue_entry_t *sleep_on(wait_queue_head_t *wq)
|
||||
{
|
||||
// Save the sleeping process registers state
|
||||
task_struct *sleeping_task = scheduler_get_current_process();
|
||||
|
||||
#if 0
|
||||
pt_regs* f = get_current_interrupt_stack_frame();
|
||||
scheduler_store_context(f, sleeping_task);
|
||||
|
||||
// Select next process in the runqueue as the current, restore it's context,
|
||||
// we assume that the first process is init wich does not sleep (I hope).
|
||||
// This is necessary to make the scheduler_run() in syscall_handler work.
|
||||
task_struct *next = list_entry(runqueue.queue.next, task_struct, run_list);
|
||||
assert((next != sleeping_task) && "The next selected process in the runqueue is the sleeping process");
|
||||
scheduler_restore_context(next, f);
|
||||
#endif
|
||||
|
||||
// Stops task from runqueue making it unrunnable
|
||||
sleeping_task->state = TASK_UNINTERRUPTIBLE;
|
||||
|
||||
// Add sleeping process to sleep wait queue
|
||||
wait_queue_entry_t *wait_entry = kmalloc(sizeof(struct wait_queue_entry_t));
|
||||
init_waitqueue_entry(wait_entry, sleeping_task);
|
||||
add_wait_queue(wq, wait_entry);
|
||||
|
||||
return wait_entry;
|
||||
}
|
||||
|
||||
int is_orphaned_pgrp(pid_t gid)
|
||||
{
|
||||
pid_t sid = 0;
|
||||
|
||||
// Obtain SID of the group from a member
|
||||
list_head *it;
|
||||
list_for_each (it, &runqueue.queue) {
|
||||
task_struct *task = list_entry(it, task_struct, run_list);
|
||||
if (task->gid == gid) {
|
||||
sid = task->sid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the process leader of the session is alive
|
||||
list_for_each (it, &runqueue.queue) {
|
||||
task_struct *task = list_entry(it, task_struct, run_list);
|
||||
if (task->pid == sid) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
pid_t sys_getpid()
|
||||
{
|
||||
// Get the current task.
|
||||
if (runqueue.curr == NULL) {
|
||||
kernel_panic("There is no current process!");
|
||||
}
|
||||
// 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;
|
||||
// Return the process identifer of the process.
|
||||
return runqueue.curr->pid;
|
||||
}
|
||||
|
||||
pid_t sys_getsid(pid_t pid)
|
||||
{
|
||||
//If pid == 0 return SID of the calling process
|
||||
if (pid == 0) {
|
||||
if (runqueue.curr == NULL) {
|
||||
kernel_panic("There is no current process!");
|
||||
}
|
||||
// Return the session identifer of the process.
|
||||
return runqueue.curr->sid;
|
||||
}
|
||||
//If != 0 get SID of the specified process
|
||||
list_head *it;
|
||||
list_for_each (it, &runqueue.queue) {
|
||||
task_struct *task = list_entry(it, task_struct, run_list);
|
||||
if (task->pid == pid)
|
||||
{
|
||||
if(runqueue.curr->sid != task->sid)
|
||||
return -EPERM;
|
||||
|
||||
return task->sid;
|
||||
}
|
||||
}
|
||||
return -ESRCH;
|
||||
}
|
||||
|
||||
pid_t sys_setsid()
|
||||
{
|
||||
task_struct *task = runqueue.curr;
|
||||
if (task == NULL) {
|
||||
kernel_panic("There is no current process!");
|
||||
}
|
||||
if (task->sid == task->pid)
|
||||
{
|
||||
pr_debug("Process %d is already a session leader.", task->pid);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
task->sid = task->pid;
|
||||
task->gid = task->pid;
|
||||
|
||||
return task->sid;
|
||||
}
|
||||
|
||||
pid_t sys_getgid()
|
||||
{
|
||||
task_struct *curr = runqueue.curr;
|
||||
if (curr == NULL) {
|
||||
kernel_panic("There is no current process!");
|
||||
}
|
||||
|
||||
return curr->gid;
|
||||
}
|
||||
|
||||
int sys_setgid(pid_t gid)
|
||||
{
|
||||
task_struct *curr = runqueue.curr;
|
||||
if (curr == NULL) {
|
||||
kernel_panic("There is no current process!");
|
||||
}
|
||||
|
||||
if (curr->gid == curr->pid)
|
||||
pr_debug("Process %d is already a session leader.", task->pid);
|
||||
|
||||
curr->gid = curr->pid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
// 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;
|
||||
// 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!");
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
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);
|
||||
int newNice = PRIO_TO_NICE(runqueue.curr->se.prio) + increment;
|
||||
pr_debug("New nice value would be : %d\n", newNice);
|
||||
|
||||
if (newNice < MIN_NICE) {
|
||||
newNice = MIN_NICE;
|
||||
}
|
||||
if (newNice > MAX_NICE) {
|
||||
newNice = MAX_NICE;
|
||||
}
|
||||
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);
|
||||
if (PRIO_TO_NICE(runqueue.curr->se.prio) != newNice && newNice >= MIN_NICE && newNice <= MAX_NICE) {
|
||||
runqueue.curr->se.prio = NICE_TO_PRIO(newNice);
|
||||
}
|
||||
int actualNice = PRIO_TO_NICE(runqueue.curr->se.prio);
|
||||
|
||||
return actualNice;
|
||||
pr_debug("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!");
|
||||
}
|
||||
// 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
|
||||
/* 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;
|
||||
if ((pid < -1) || (pid == 0)) {
|
||||
return -ESRCH;
|
||||
}
|
||||
if (pid == runqueue.curr->pid) {
|
||||
return -ECHILD;
|
||||
}
|
||||
if (options != 0 && options != WNOHANG) {
|
||||
return -EINVAL;
|
||||
}
|
||||
#if 0
|
||||
if (status == NULL) {
|
||||
return -EFAULT;
|
||||
}
|
||||
#endif
|
||||
if (list_head_empty(&runqueue.curr->children)) {
|
||||
return -ECHILD;
|
||||
}
|
||||
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 (TODO: Improve status set).
|
||||
if (status)
|
||||
(*status) = entry->state;
|
||||
// Finalize the VFS structures.
|
||||
vfs_destroy_task(entry);
|
||||
// Remove entry from children of parent.
|
||||
list_head_del(&entry->sibling);
|
||||
// Remove entry from the scheduling queue.
|
||||
scheduler_dequeue_task(entry);
|
||||
// Delete the task_struct.
|
||||
kmem_cache_free(entry);
|
||||
pr_debug("Process %d is freeing memory of process %d.\n", runqueue.curr->pid, 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!");
|
||||
}
|
||||
// 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);
|
||||
// Get the process.
|
||||
task_struct *init_proc = scheduler_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;
|
||||
// Send a SIGCHLD to the parent process.
|
||||
if (runqueue.curr->parent) {
|
||||
int ret = sys_kill(runqueue.curr->parent->pid, SIGCHLD);
|
||||
if (ret == -1) {
|
||||
printf("[%d] %5d failed sending signal %d : %s\n", ret, runqueue.curr->parent->pid,
|
||||
SIGCHLD, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
// If it has children, then init process has to take care of them.
|
||||
if (!list_head_empty(&runqueue.curr->children)) {
|
||||
pr_debug("Moving children of %s(%d) to init(%d): {\n",
|
||||
runqueue.curr->name, runqueue.curr->pid, init_proc->pid);
|
||||
// Change the parent.
|
||||
pr_debug("Moving children (%d): {\n", init_proc->pid);
|
||||
list_for_each_decl(it, &runqueue.curr->children)
|
||||
{
|
||||
task_struct *entry = list_entry(it, task_struct, sibling);
|
||||
pr_debug(" [%d] %s\n", entry->pid, entry->name);
|
||||
entry->parent = init_proc;
|
||||
}
|
||||
pr_debug("}\n");
|
||||
// Plug the list of children.
|
||||
list_head_merge(&init_proc->children, &runqueue.curr->children);
|
||||
// Print the list of children.
|
||||
pr_debug("New list of init children (%d): {\n", init_proc->pid);
|
||||
list_for_each_decl(it, &init_proc->children)
|
||||
{
|
||||
task_struct *entry = list_entry(it, task_struct, sibling);
|
||||
pr_debug(" [%d] %s\n", entry->pid, entry->name);
|
||||
}
|
||||
pr_debug("}\n");
|
||||
}
|
||||
// Free the space occupied by the stack.
|
||||
destroy_process_image(runqueue.curr->mm);
|
||||
// Debugging message.
|
||||
pr_debug("Process %d exited with value %d\n", runqueue.curr->pid, exit_code);
|
||||
}
|
||||
|
||||
int sys_sched_setparam(pid_t pid, const sched_param_t *param)
|
||||
{
|
||||
list_head *it;
|
||||
// Iter over the runqueue to find the task
|
||||
list_for_each (it, &runqueue.queue) {
|
||||
task_struct *entry = list_entry(it, task_struct, run_list);
|
||||
if (entry->pid == pid) {
|
||||
if (!entry->se.is_periodic && param->is_periodic)
|
||||
runqueue.num_periodic++;
|
||||
else if (entry->se.is_periodic && !param->is_periodic)
|
||||
runqueue.num_periodic--;
|
||||
// Sets the parameters from param to the "se" struct parameters.
|
||||
entry->se.prio = param->sched_priority;
|
||||
entry->se.period = param->period;
|
||||
entry->se.arrivaltime = param->arrivaltime;
|
||||
entry->se.is_periodic = param->is_periodic;
|
||||
entry->se.deadline = timer_get_ticks() + param->deadline;
|
||||
entry->se.next_period = timer_get_ticks();
|
||||
|
||||
entry->se.is_under_analysis = true;
|
||||
entry->se.executed = false;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int sys_sched_getparam(pid_t pid, sched_param_t *param)
|
||||
{
|
||||
list_head *it;
|
||||
// Iter over the runqueue to find the task
|
||||
list_for_each (it, &runqueue.queue) {
|
||||
task_struct *entry = list_entry(it, task_struct, run_list);
|
||||
if (entry->pid == pid) {
|
||||
//Sets the parameters from the "se" struct to param
|
||||
param->sched_priority = entry->se.prio;
|
||||
param->period = entry->se.period;
|
||||
param->deadline = entry->se.deadline;
|
||||
param->arrivaltime = entry->se.arrivaltime;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// @brief Performs the response time analysis for the current list
|
||||
/// of periodic processes.
|
||||
/// @return 1 if scheduling periodic processes is feasable, 0 otherwise.
|
||||
static int __response_time_analysis()
|
||||
{
|
||||
task_struct *entry, *previous;
|
||||
time_t r, previous_r = 0;
|
||||
list_for_each_decl(it, &runqueue.queue)
|
||||
{
|
||||
entry = list_entry(it, task_struct, run_list);
|
||||
if (entry->se.is_periodic) {
|
||||
// Put r equal to worst case exec because is the first point in time that the task could possibly complete
|
||||
r = entry->se.worst_case_exec;
|
||||
previous_r = 0;
|
||||
// The analysis can be completed either missing the deadline or reaching a fixed point
|
||||
while (r < entry->se.deadline && r != previous_r) {
|
||||
previous_r = r;
|
||||
r = entry->se.worst_case_exec;
|
||||
list_for_each_decl(it2, &runqueue.queue)
|
||||
{
|
||||
previous = list_entry(it2, task_struct, run_list);
|
||||
// Check the interferences of higher priority processes
|
||||
if (previous->se.is_periodic && previous->se.period < entry->se.period) {
|
||||
r += (int)ceil((double)previous_r / (double)previous->se.period) * previous->se.worst_case_exec;
|
||||
pr_debug("%d += (%.2f / %.2f) * %d\n", r, (double)previous_r, (double)previous->se.period, previous->se.worst_case_exec);
|
||||
pr_debug("Response Time Analysis -> [%s]vs[%s] R = %d\n\n", entry->name, previous->name, r);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Feasibility of scheduler is guaranteed if and only if response time analysis is lower than deadline.
|
||||
if (r > entry->se.deadline)
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sys_waitperiod()
|
||||
{
|
||||
if (runqueue.curr) {
|
||||
if (runqueue.curr->se.is_periodic) {
|
||||
// Update the Worst Case Execution Time (WCET).
|
||||
time_t wcet = timer_get_ticks() - runqueue.curr->se.exec_start;
|
||||
if (runqueue.curr->se.worst_case_exec < wcet)
|
||||
runqueue.curr->se.worst_case_exec = wcet;
|
||||
// Update thye utilization factor.
|
||||
runqueue.curr->se.utilization_factor = ((double)runqueue.curr->se.worst_case_exec / (double)runqueue.curr->se.period);
|
||||
|
||||
// If the task is under analysis, we need to test if the process can be
|
||||
// placed with the other periodic tasks.
|
||||
if (runqueue.curr->se.is_under_analysis) {
|
||||
runqueue.curr->se.worst_case_exec = runqueue.curr->se.sum_exec_runtime;
|
||||
bool_t is_not_schedulable = false;
|
||||
#if defined(SCHEDULER_EDF)
|
||||
double u = 0;
|
||||
task_struct *entry;
|
||||
list_for_each_decl(it, &runqueue.queue)
|
||||
{
|
||||
entry = list_entry(it, task_struct, run_list);
|
||||
// Sum the utilization factor of all periodic tasks.
|
||||
if (entry->se.is_periodic)
|
||||
u += entry->se.utilization_factor;
|
||||
}
|
||||
if (u > 1) {
|
||||
is_not_schedulable = true;
|
||||
}
|
||||
pr_debug("utilization factor = %f\n", u);
|
||||
#elif defined(SCHEDULER_RM)
|
||||
// Calculating least upper bound of utilization factor.
|
||||
// For large amount of processes ulub asymptotically should reach ln(2).
|
||||
double ulub = (runqueue.num_periodic * (pow(2, (1.0 / runqueue.num_periodic)) - 1));
|
||||
double u = 0;
|
||||
task_struct *entry;
|
||||
list_for_each_decl(it, &runqueue.queue)
|
||||
{
|
||||
entry = list_entry(it, task_struct, run_list);
|
||||
// Sum the utilization factor of all periodic tasks.
|
||||
if (entry->se.is_periodic)
|
||||
u += entry->se.utilization_factor;
|
||||
}
|
||||
// If the sum of utilization factor is bounded between ulub and 1 we need to calculate
|
||||
// the response time analysis for each process.
|
||||
if (u > 1) {
|
||||
is_not_schedulable = true;
|
||||
} else if (u <= ulub)
|
||||
is_not_schedulable = false;
|
||||
else
|
||||
is_not_schedulable = __response_time_analysis();
|
||||
#endif
|
||||
// If it is not schedulable, we need to tell it to the process.
|
||||
if (is_not_schedulable)
|
||||
return -ENOTSCHEDULABLE;
|
||||
|
||||
// Otherwise, it is schedulable.
|
||||
runqueue.curr->se.is_under_analysis = false;
|
||||
|
||||
// The task has been executed as non-periodic process so that his deadline is not been updated
|
||||
// by the scheduling algorithm of periodic tasks. We need to update it manually.
|
||||
runqueue.curr->se.next_period = timer_get_ticks();
|
||||
runqueue.curr->se.deadline = timer_get_ticks() + runqueue.curr->se.period;
|
||||
}
|
||||
|
||||
if (timer_get_ticks() > runqueue.curr->se.deadline)
|
||||
pr_warning("%d > %d Missing deadline...\n", timer_get_ticks(), runqueue.curr->se.deadline);
|
||||
|
||||
// Tell the scheduler that we have executed the periodic process.
|
||||
runqueue.curr->se.executed = true;
|
||||
|
||||
} else
|
||||
pr_warning("An aperiodic task is calling `waitperiod`, ignoring...\n");
|
||||
return 0;
|
||||
}
|
||||
return -ESRCH;
|
||||
}
|
||||
@@ -1,73 +1,125 @@
|
||||
/// MentOS, The Mentoring Operating system project
|
||||
/// @file scheduler_algorithm.c
|
||||
/// @brief Round Robin algorithm.
|
||||
/// @date Mar 2019.
|
||||
/// @copyright (c) 2014-2021 This file is distributed under the MIT License.
|
||||
/// See LICENSE.md for details.
|
||||
|
||||
#include "timer.h"
|
||||
#include "prio.h"
|
||||
#include "debug.h"
|
||||
#include "assert.h"
|
||||
#include "list_head.h"
|
||||
#include "wait.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)
|
||||
static inline task_struct *scheduler_rr(runqueue_t *runqueue, bool_t skip_periodic)
|
||||
{
|
||||
// Pointer to the next task to schedule.
|
||||
task_struct *next = NULL;
|
||||
// If there is just one process, return it.
|
||||
if ((runqueue->curr->run_list.next == &runqueue->queue) &&
|
||||
(runqueue->curr->run_list.prev == &runqueue->queue)) {
|
||||
return runqueue->curr;
|
||||
}
|
||||
// By default, the next process is the current one.
|
||||
task_struct *next = NULL, *entry = NULL;
|
||||
// Search for the next process (BEWARE: We do not start from the head, so INSIDE skip the head).
|
||||
list_for_each_decl(it, &runqueue->curr->run_list)
|
||||
{
|
||||
// Check if we reached the head of list_head, and skip it.
|
||||
if (it == &runqueue->queue)
|
||||
continue;
|
||||
// Get the current entry.
|
||||
entry = list_entry(it, task_struct, run_list);
|
||||
|
||||
// We consider only runnable processes
|
||||
if (entry->state != TASK_RUNNING)
|
||||
continue;
|
||||
|
||||
// Skip the process if it is a periodic one, we are issued to skip
|
||||
// periodic tasks, and the entry is not a periodic task under
|
||||
// analysis.
|
||||
if (entry->se.is_periodic && skip_periodic && !entry->se.is_under_analysis)
|
||||
continue;
|
||||
|
||||
// We have our next entry.
|
||||
next = entry;
|
||||
break;
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
static inline task_struct *scheduler_priority(runqueue_t *runqueue, bool_t skip_periodic)
|
||||
{
|
||||
return scheduler_rr(runqueue, skip_periodic);
|
||||
}
|
||||
|
||||
static inline task_struct *scheduler_cfs(runqueue_t *runqueue, bool_t skip_periodic)
|
||||
{
|
||||
return scheduler_rr(runqueue, skip_periodic);
|
||||
}
|
||||
|
||||
static inline task_struct *scheduler_aedf(runqueue_t *runqueue)
|
||||
{
|
||||
return scheduler_rr(runqueue, false);
|
||||
}
|
||||
|
||||
static inline task_struct *scheduler_edf(runqueue_t *runqueue)
|
||||
{
|
||||
return scheduler_rr(runqueue, false);
|
||||
}
|
||||
|
||||
static inline task_struct *scheduler_rm(runqueue_t *runqueue)
|
||||
{
|
||||
return scheduler_rr(runqueue, false);
|
||||
}
|
||||
|
||||
task_struct *scheduler_pick_next_task(runqueue_t *runqueue)
|
||||
{
|
||||
// While periodic task is under analysis is executed with aperiodic
|
||||
// scheduler and can be preempted by a "true" periodic task.
|
||||
// We need to sum all the execution spots to calculate the WCET even
|
||||
// if is a more pessimistic evaluation.
|
||||
// Update the delta exec.
|
||||
runqueue->curr->se.exec_runtime = timer_get_ticks() - runqueue->curr->se.exec_start;
|
||||
update_process_profiling_timer(runqueue->curr);
|
||||
|
||||
// set the sum_exec_runtime.
|
||||
runqueue->curr->se.sum_exec_runtime += runqueue->curr->se.exec_runtime;
|
||||
|
||||
// If the task is not a periodic task we have to update the virtual runtime.
|
||||
if (!runqueue->curr->se.is_periodic) {
|
||||
// Get the weight of the current process.
|
||||
time_t weight = GET_WEIGHT(runqueue->curr->se.prio);
|
||||
if (weight != NICE_0_LOAD) {
|
||||
// get the multiplicative factor for its delta_exec.
|
||||
double factor = ((double)NICE_0_LOAD) / ((double)weight);
|
||||
// weight the delta_exec with the multiplicative factor.
|
||||
runqueue->curr->se.exec_runtime = (int)(((double)runqueue->curr->se.exec_runtime) * factor);
|
||||
}
|
||||
// Update vruntime of the current process.
|
||||
runqueue->curr->se.vruntime += runqueue->curr->se.exec_runtime;
|
||||
}
|
||||
|
||||
// Pointer to the next task to schedule.
|
||||
task_struct *next = NULL;
|
||||
#if defined(SCHEDULER_RR)
|
||||
//==== Implementatin of the Round-Robin Scheduling algorithm ============
|
||||
|
||||
|
||||
|
||||
//=======================================================================
|
||||
next = scheduler_rr(runqueue, false);
|
||||
#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 (/*...*/) {
|
||||
/*...*/
|
||||
}
|
||||
}
|
||||
|
||||
//=======================================================================
|
||||
next = scheduler_priority(runqueue, false);
|
||||
#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
|
||||
// ...
|
||||
|
||||
//========================================================================
|
||||
next = scheduler_cfs(runqueue, false);
|
||||
#elif defined(SCHEDULER_EDF)
|
||||
next = scheduler_edf(runqueue);
|
||||
#elif defined(SCHEDULER_RM)
|
||||
next = scheduler_rm(runqueue);
|
||||
#elif defined(SCHEDULER_AEDF)
|
||||
next = scheduler_aedf(runqueue);
|
||||
#else
|
||||
#error "You should enable a scheduling algorithm!"
|
||||
#endif
|
||||
assert(next && "No valid task selected. Have you implemented a scheduling algorithm?");
|
||||
assert(next && "No valid task selected by the scheduling algorithm.");
|
||||
|
||||
return next;
|
||||
// Update the last context switch time of the next process.
|
||||
next->se.exec_start = timer_get_ticks();
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
@@ -1,23 +1,38 @@
|
||||
; MentOS, The Mentoring Operating system project
|
||||
; @file user.asm
|
||||
; @brief
|
||||
; @copyright (c) 2019 This file is distributed under the MIT License.
|
||||
; @copyright (c) 2014-2021 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
|
||||
; | stack | [ebp + 0x0C] ARG1
|
||||
; | location | [ebp + 0x08] ARG0
|
||||
; | return address | [ebp + 0x04]
|
||||
; | EBP | [ebp + 0x00]
|
||||
; | SS |
|
||||
; | ESP |
|
||||
; | EFLAGS |
|
||||
; | CS |
|
||||
; | EIP |
|
||||
|
||||
; We can use the following macros to access the arguments ONLY AFTER 0x23 is
|
||||
; pushed onto the stack, in fact, the first argument is after 0x08 because
|
||||
; we just pushed first `ebp` and then `0x23`.
|
||||
%define ARG0 [ebp + 0x08] ; Argument 0
|
||||
%define ARG1 [ebp + 0x0C] ; Argument 1
|
||||
%define ARG2 [ebp + 0x10] ; Argument 2
|
||||
%define ARG3 [ebp + 0x14] ; Argument 3
|
||||
%define ARG4 [ebp + 0x18] ; Argument 4
|
||||
|
||||
; -----------------------------------------------------------------------------
|
||||
; SECTION (text)
|
||||
; -----------------------------------------------------------------------------
|
||||
section .text
|
||||
|
||||
global enter_userspace ; Allows the C code to call enter_userspace(...).
|
||||
|
||||
enter_userspace:
|
||||
|
||||
push ebp ; Save current ebp
|
||||
@@ -45,38 +60,28 @@ enter_userspace:
|
||||
;---------------------------------------------------------------------------
|
||||
|
||||
;==== (ESP) Stack address ==================================================
|
||||
mov eax, [ebp + 0xC] ; get uintptr_t stack
|
||||
mov eax, ARG1 ; 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
|
||||
or eax, 0x200 ; enable interrupt
|
||||
push eax ; push new EFLAGS on Kernel's stack
|
||||
;---------------------------------------------------------------------------
|
||||
|
||||
;==== (CS) Code Segment ====================================================
|
||||
push 0x1B ;
|
||||
push 0x1b ;
|
||||
;---------------------------------------------------------------------------
|
||||
|
||||
;==== (EIP) Entry point ====================================================
|
||||
mov eax, [ebp + 0x8] ; get uintptr_t location
|
||||
mov eax, ARG0 ; get uintptr_t location
|
||||
push eax ; push uintptr_t location on Kernel's stack
|
||||
;---------------------------------------------------------------------------
|
||||
|
||||
iret ; interrupt return
|
||||
pop ebp
|
||||
ret
|
||||
|
||||
; 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
|
||||
; WE SHOULD NOT STILL BE HERE! :(p
|
||||
@@ -0,0 +1,42 @@
|
||||
/// MentOS, The Mentoring Operating system project
|
||||
/// @file wait.c
|
||||
/// @brief wait functions.
|
||||
/// @copyright (c) 2014-2021 This file is distributed under the MIT License.
|
||||
/// See LICENSE.md for details.
|
||||
|
||||
/// Change the header.
|
||||
#define __DEBUG_HEADER__ "[WAIT ]"
|
||||
|
||||
#include "wait.h"
|
||||
|
||||
static inline void __add_wait_queue(wait_queue_head_t *head, wait_queue_entry_t *wq)
|
||||
{
|
||||
list_head_add_tail(&wq->task_list, &head->task_list);
|
||||
}
|
||||
|
||||
static inline void __remove_wait_queue(wait_queue_head_t *head, wait_queue_entry_t *wq)
|
||||
{
|
||||
list_head_del(&wq->task_list);
|
||||
}
|
||||
|
||||
void init_waitqueue_entry(wait_queue_entry_t *wq, struct task_struct *task)
|
||||
{
|
||||
wq->flags = 0;
|
||||
wq->task = task;
|
||||
wq->func = default_wake_function;
|
||||
}
|
||||
|
||||
void add_wait_queue(wait_queue_head_t *head, wait_queue_entry_t *wq)
|
||||
{
|
||||
wq->flags &= ~WQ_FLAG_EXCLUSIVE;
|
||||
spinlock_lock(&head->lock);
|
||||
__add_wait_queue(head, wq);
|
||||
spinlock_unlock(&head->lock);
|
||||
}
|
||||
|
||||
void remove_wait_queue(wait_queue_head_t *head, wait_queue_entry_t *wq)
|
||||
{
|
||||
spinlock_lock(&head->lock);
|
||||
__remove_wait_queue(head, wq);
|
||||
spinlock_unlock(&head->lock);
|
||||
}
|
||||
Reference in New Issue
Block a user