diff --git a/files/usr/share/man/start.man b/files/usr/share/man/start.man new file mode 100644 index 0000000..d9995ba --- /dev/null +++ b/files/usr/share/man/start.man @@ -0,0 +1,12 @@ +SYNOPSIS + start [OPTIONS] [FILE] + +DESCRIPTION + Start a recording session aimed at displaying an ordered list of 50 items + containing information about the processes that the CPU has just processed. + + +OPTIONS + -h: shows command help. //non funziona per ora lol + -p: start the session with generated children + -f [FILE]: start the session launching a specified program \ No newline at end of file diff --git a/mentos/CMakeLists.txt b/mentos/CMakeLists.txt index 31d54d8..932a7d9 100644 --- a/mentos/CMakeLists.txt +++ b/mentos/CMakeLists.txt @@ -127,6 +127,7 @@ set(KERNEL_SOURCES ${PROJECT_SOURCE_DIR}/src/io/stdio.c ${PROJECT_SOURCE_DIR}/src/io/proc_video.c ${PROJECT_SOURCE_DIR}/src/io/proc_running.c + ${PROJECT_SOURCE_DIR}/src/io/proc_feedback.c ${PROJECT_SOURCE_DIR}/src/io/proc_system.c ${PROJECT_SOURCE_DIR}/src/io/vga/vga.c ${PROJECT_SOURCE_DIR}/src/ipc/msg.c @@ -166,6 +167,7 @@ set(KERNEL_SOURCES ${PROJECT_SOURCE_DIR}/src/descriptor_tables/tss.c ${PROJECT_SOURCE_DIR}/src/descriptor_tables/tss.S ${PROJECT_SOURCE_DIR}/src/process/scheduler_algorithm.c + ${PROJECT_SOURCE_DIR}/src/process/scheduler_feedback.c ${PROJECT_SOURCE_DIR}/src/process/scheduler.c ${PROJECT_SOURCE_DIR}/src/process/process.c ${PROJECT_SOURCE_DIR}/src/process/wait.c diff --git a/mentos/inc/io/proc_modules.h b/mentos/inc/io/proc_modules.h index 0ad812a..367be00 100644 --- a/mentos/inc/io/proc_modules.h +++ b/mentos/inc/io/proc_modules.h @@ -14,3 +14,5 @@ int procv_module_init(); /// @brief Initialize the procfs system files. /// @return 0 on success, 1 on failure. int procs_module_init(); + +int procfb_module_init(); diff --git a/mentos/inc/process/scheduler_feedback.h b/mentos/inc/process/scheduler_feedback.h new file mode 100644 index 0000000..d18cd18 --- /dev/null +++ b/mentos/inc/process/scheduler_feedback.h @@ -0,0 +1,28 @@ +/// @file scheduler_feedback.h +/// @author Enrico Fraccaroli (enry.frak@gmail.com) +/// @brief + +#pragma once + +#include "sys/types.h" +#include "process/process.h" + +/// @brief Initialize the scheduler feedback system. +/// @return 1 on success, 0 on failure. +int scheduler_feedback_init(); + +/// @brief Add the given task to the feedback system. +/// @param task the task we need to add. +void scheduler_feedback_task_add(task_struct *task); + +/// @brief Removes the given task from the feedback system. +/// @param pid the pid of the task we need to remove. +void scheduler_feedback_task_remove(pid_t pid); + +/// @brief Updates the scheduling statistics for the given task. +/// @param task the task for which we update the statistics. +void scheduler_feedback_task_update(task_struct *task); + +/// @brief Updates the scheduling statistics for the given task. +/// @param task the task for which we update the statistics. +void scheduler_feedback_update(); diff --git a/mentos/src/fs/ext2.c b/mentos/src/fs/ext2.c index a597248..7831a21 100644 --- a/mentos/src/fs/ext2.c +++ b/mentos/src/fs/ext2.c @@ -488,6 +488,10 @@ static bool_t ext2_valid_permissions(int flags, mode_t mask, uid_t uid, gid_t gi { // Check the permissions. task_struct *task = scheduler_get_current_process(); + if (task == NULL) { + pr_warning("Failed to get the current running process, assuming we are booting.\n"); + return true; + } // The current task is the owner. if (task->uid == uid) { if (!bitmask_check(mask, S_IRUSR)) @@ -2151,11 +2155,6 @@ static int ext2_create_inode( pr_err("Received a null EXT2 inode.\n"); return -1; } - task_struct *task = scheduler_get_current_process(); - if (task == NULL) { - pr_err("Failed to get the current running process.\n"); - return -1; - } // Allocate an inode, inside the preferred_group if possible. int inode_index = ext2_allocate_inode(fs, preferred_group); if (inode_index == 0) { @@ -2169,10 +2168,19 @@ static int ext2_create_inode( pr_err("Failed to read the newly created inode.\n"); return -1; } + // Get the UID and GID. + uid_t uid = 0, gid = 0; + task_struct *task = scheduler_get_current_process(); + if (task != NULL) { + pr_warning("Failed to get the current running process, assuming we are booting.\n"); + } else { + uid = task->uid; + gid = task->gid; + } // Set the inode mode. inode->mode = mode; // Set the user identifiers of the owners. - inode->uid = task->uid; + inode->uid = uid; // Set the size of the file in bytes. inode->size = 0; // Set the time that the inode was accessed. @@ -2184,7 +2192,7 @@ static int ext2_create_inode( // Set the time that the inode was deleted. inode->dtime = 0; // Set the group identifiers of the owners. - inode->gid = task->gid; + inode->gid = gid; // Set the number of hard links. inode->links_count = 0; // Set the blocks count. @@ -2332,7 +2340,7 @@ static vfs_file_t *ext2_open(const char *path, int flags, mode_t mode) search.parent_inode = 0; // First check, if a file with the given name already exists. if (!ext2_resolve_path(fs->root, absolute_path, &search)) { - if (bitmask_check(flags, O_CREAT | O_EXCL)) { + if (bitmask_check(flags, O_CREAT) && bitmask_check(flags, O_EXCL)) { pr_err("A file or directory already exists at `%s` (O_CREAT | O_EXCL).\n", absolute_path); return NULL; } else if (bitmask_check(flags, O_DIRECTORY) && (direntry.file_type != ext2_file_type_directory)) { @@ -2720,15 +2728,15 @@ static int ext2_mkdir(const char *path, mode_t permission) pr_err("Failed to properly get the parent directory (%s == %s).\n", parent_path, path); return -ENOENT; } + // Set the inode mode. + uint32_t mode = EXT2_S_IFDIR; + mode |= 0xFFF & permission; // Get the parent VFS node. - vfs_file_t *parent = vfs_open(parent_path, O_RDONLY, 0); + vfs_file_t *parent = vfs_open(parent_path, O_RDONLY, mode); if (parent == NULL) { pr_err("Failed to open parent directory (%s).\n", parent_path); return -ENOENT; } - // Set the inode mode. - uint32_t mode = EXT2_S_IFDIR; - mode |= 0xFFF & permission; // Get the group index of the parent. uint32_t group_index = ext2_get_group_index_from_inode(fs, parent->ino); // Create and initialize the new inode. diff --git a/mentos/src/fs/vfs.c b/mentos/src/fs/vfs.c index 1ac79ac..daaa2c9 100644 --- a/mentos/src/fs/vfs.c +++ b/mentos/src/fs/vfs.c @@ -321,7 +321,7 @@ vfs_file_t *vfs_creat(const char *path, mode_t mode) // Retrieve the file. vfs_file_t *file = sb_root->sys_operations->creat_f(absolute_path, mode); if (file == NULL) { - pr_err("vfs_open(%s): Cannot find the given file (%s)!\n", path, strerror(errno)); + pr_err("vfs_creat(%s): Cannot find the given file (%s)!\n", path, strerror(errno)); errno = ENOENT; return NULL; } diff --git a/mentos/src/io/proc_feedback.c b/mentos/src/io/proc_feedback.c new file mode 100644 index 0000000..f2b6c3c --- /dev/null +++ b/mentos/src/io/proc_feedback.c @@ -0,0 +1,50 @@ +/// @file proc_feedback.c +/// @brief Contains callbacks for procfs system files. +/// @copyright (c) 2014-2022 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "fs/procfs.h" +#include "process/process.h" +#include "sys/errno.h" +#include "io/debug.h" +#include "string.h" + +static ssize_t procfb_read(vfs_file_t *file, char *buf, off_t offset, size_t nbyte) +{ + return 0; +} + +/// Filesystem general operations. +static vfs_sys_operations_t procfb_sys_operations = { + .mkdir_f = NULL, + .rmdir_f = NULL, + .stat_f = NULL +}; + +/// Filesystem file operations. +static vfs_file_operations_t procfb_fs_operations = { + .open_f = NULL, + .unlink_f = NULL, + .close_f = NULL, + .read_f = procfb_read, + .write_f = NULL, + .lseek_f = NULL, + .stat_f = NULL, + .ioctl_f = NULL, + .getdents_f = NULL +}; + +int procfb_module_init() +{ + // Create the file. + proc_dir_entry_t *file = proc_create_entry("feedback", NULL); + if (file == NULL) { + pr_err("Cannot create `/proc/feedback`.\n"); + return 1; + } + pr_debug("Created `/proc/feedback` (%p)\n", file); + // Set the specific operations. + file->sys_operations = &procfb_sys_operations; + file->fs_operations = &procfb_fs_operations; + return 0; +} diff --git a/mentos/src/kernel.c b/mentos/src/kernel.c index 2d4dba5..78ce7cf 100644 --- a/mentos/src/kernel.c +++ b/mentos/src/kernel.c @@ -29,6 +29,7 @@ #include "drivers/keyboard/keymap.h" #include "drivers/ps2.h" #include "process/scheduler.h" +#include "process/scheduler_feedback.h" #include "hardware/timer.h" #include "fs/vfs.h" #include "devices/fpu.h" @@ -342,6 +343,24 @@ int kmain(boot_info_t *boot_informations) } print_ok(); + //========================================================================== + pr_notice("Initialize scheduler feedback system...\n"); + printf("Initialize scheduler feedback system..."); + if (!scheduler_feedback_init()) { + print_fail(); + return 1; + } + print_ok(); + + //========================================================================== + pr_notice("Initialize scheduler feedback system (2)...\n"); + printf("Initialize scheduler feedback system (2)..."); + if (procfb_module_init()) { + print_fail(); + return 1; + } + print_ok(); + //========================================================================== pr_notice("Creating init process...\n"); printf("Creating init process..."); diff --git a/mentos/src/process/scheduler.c b/mentos/src/process/scheduler.c index c7a5a24..7a647f8 100644 --- a/mentos/src/process/scheduler.c +++ b/mentos/src/process/scheduler.c @@ -12,9 +12,10 @@ #include "assert.h" #include "strerror.h" #include "fs/vfs.h" -#include "process/scheduler.h" #include "descriptor_tables/tss.h" #include "devices/fpu.h" +#include "process/scheduler_feedback.h" +#include "process/scheduler.h" #include "process/prio.h" #include "process/wait.h" #include "mem/kheap.h" @@ -109,10 +110,13 @@ void scheduler_enqueue_task(task_struct *process) list_head_insert_before(&process->run_list, &runqueue.queue); // Increment the number of active processes. ++runqueue.num_active; + + scheduler_feedback_task_add(process); } void scheduler_dequeue_task(task_struct *process) { + scheduler_feedback_task_remove(process->pid); // Delete the process from the list of running processes. list_head_remove(&process->run_list); // Decrement the number of active processes. diff --git a/mentos/src/process/scheduler_algorithm.c b/mentos/src/process/scheduler_algorithm.c index 05dcc15..2e441fe 100644 --- a/mentos/src/process/scheduler_algorithm.c +++ b/mentos/src/process/scheduler_algorithm.c @@ -15,6 +15,7 @@ #include "klib/list_head.h" #include "process/wait.h" #include "process/scheduler.h" +#include "process/scheduler_feedback.h" /// @brief Updates task execution statistics. /// @param task the task to update. @@ -29,12 +30,12 @@ static inline bool_t __is_periodic_task(task_struct *task) return task->se.is_periodic && !task->se.is_under_analysis; } -/// @brief Employs time-sharing, giving each job a timeslice, and is also +/// @brief Employs time-sharing, giving each job a time-slot, and is also /// preemptive since the scheduler forces the task out of the CPU once -/// the timeslice expires. +/// the time-slot expires. /// @param runqueue list of all processes. -/// @param skip_periodic tells the algorithm if there are periodic processes in -/// the list, and in that case it needs to skip them. +/// @param skip_periodic tells the algorithm that periodic processes in the list +/// should be skipped. /// @return the next task on success, NULL on failure. static inline task_struct *__scheduler_rr(runqueue_t *runqueue, bool_t skip_periodic) { @@ -61,6 +62,7 @@ static inline task_struct *__scheduler_rr(runqueue_t *runqueue, bool_t skip_peri // We have our next entry. return entry; } + return NULL; } @@ -95,10 +97,10 @@ static inline task_struct *__scheduler_priority(runqueue_t *runqueue, bool_t ski #ifdef SCHEDULER_PRIORITY // Get the first element of the list. task_struct *next = list_entry(runqueue->queue.next, struct task_struct, run_list); - // Get the static priority of the first element of the list we just extracted. - time_t min = /*...*/; // This will hold a given entry, while iterating the list of tasks. task_struct *entry = NULL; + // Get the static priority of the first element of the list we just extracted. + time_t min = /*...*/; // Search for the task with the smallest static priority. list_for_each_decl(it, &runqueue->queue) @@ -140,10 +142,10 @@ static inline task_struct *__scheduler_cfs(runqueue_t *runqueue, bool_t skip_per #ifdef SCHEDULER_CFS // Get the first element of the list. task_struct *next = list_entry(runqueue->queue.next, struct task_struct, run_list); - // Get the virtual runtime of the first element of the list we just extracted. - time_t min = /*...*/; // This will hold a given entry, while iterating the list of tasks. task_struct *entry = NULL; + // Get the virtual runtime of the first element of the list we just extracted. + time_t min = /*...*/; // Search for the task with the smallest vruntime value. list_for_each_decl(it, &runqueue->queue) @@ -163,6 +165,7 @@ static inline task_struct *__scheduler_cfs(runqueue_t *runqueue, bool_t skip_per // Check if the element in the list has a smaller vruntime value. /* ... */ } + return next; #else return __scheduler_rr(runqueue, skip_periodic); @@ -177,7 +180,7 @@ static inline task_struct *__scheduler_aedf(runqueue_t *runqueue) { #ifdef SCHEDULER_AEDF // This will hold the pointer to the next task to schedule. - task_struct *next = NULL; + task_struct *next = list_entry(runqueue->queue.next, struct task_struct, run_list); // This will hold a given entry, while iterating the list of tasks. task_struct *entry = NULL; // Initialize the nearest "next deadline". @@ -385,6 +388,11 @@ task_struct *scheduler_pick_next_task(runqueue_t *runqueue) // Update the last context switch time of the next task. next->se.exec_start = timer_get_ticks(); + // Update the feedback statistics for the new scheduled task. + scheduler_feedback_task_update(next); + // Update the overall feedback system. + scheduler_feedback_update(); + return next; } @@ -410,16 +418,18 @@ static void __update_task_statistics(task_struct *task) // If the task is not a periodic task we have to update the virtual runtime. if (!task->se.is_periodic) { // Get the weight of the current task. - time_t weight = /* ... */; + time_t weight = GET_WEIGHT((task)->se.prio); /* ... */ + ; // If the weight is different from the default load, compute it. if (weight != NICE_0_LOAD) { // Get the multiplicative factor for its delta_exec. - double factor = /* ... */; + double factor = ((double)NICE_0_LOAD / (double)weight); /* ... */ + ; // Weight the delta_exec with the multiplicative factor. - task->se.exec_runtime = /* ... */; + task->se.exec_runtime = ((int)(((double)task->se.exec_runtime) * factor)); /* ... */ } // Update vruntime of the current task. - task->se.vruntime += /* ... */; + task->se.vruntime += task->se.exec_runtime; } #endif } diff --git a/mentos/src/process/scheduler_feedback.c b/mentos/src/process/scheduler_feedback.c new file mode 100644 index 0000000..5644844 --- /dev/null +++ b/mentos/src/process/scheduler_feedback.c @@ -0,0 +1,195 @@ +/// @file scheduler_feedback.c +/// @brief Manage the current PID for the scheduler feedback session +/// @copyright (c) 2014-2022 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "process/scheduler_feedback.h" +#include "hardware/timer.h" +#include "strerror.h" +#include "fs/vfs.h" +#include "assert.h" +#include "string.h" +#include "fcntl.h" +#include "stdio.h" + +// Include the kernel log levels. +#include "sys/kernel_levels.h" +/// Change the header. +#define __DEBUG_HEADER__ "[SCHFBK]" +/// Set the log level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE +#include "io/debug.h" + +/// @brief How often the feedback is shown. +#define LOG_INTERVAL_SEC 0.5 + +/// @brief The name of the scheduling policy. +#if defined(SCHEDULER_RR) +#define POLICY_NAME "RR " +#elif defined(SCHEDULER_PRIORITY) +#define POLICY_NAME "PRIO " +#elif defined(SCHEDULER_CFS) +#define POLICY_NAME "CFS " +#elif defined(SCHEDULER_EDF) +#define POLICY_NAME "EDF " +#elif defined(SCHEDULER_RM) +#define POLICY_NAME "RM " +#elif defined(SCHEDULER_AEDF) +#define POLICY_NAME "AEDF " +#else +#error "You should enable a scheduling algorithm!" +#endif + +/// @brief If uncommented, it writes the logging on file. +//#define WRITE_ON_FILE + +#ifdef WRITE_ON_FILE +/// @brief Name of the file where the feedback statistics are saved. +#define FEEDBACK_FILENAME "/var/schedfb" +/// @brief The header shown +#define FEEDBACK_HEADER "\n[PID[] | NAME | -> (CPU UTILIZATION)\n\0" +/// @brief +ssize_t offset; +#endif + +/// @brief When the next log should be displayed/saved, in CPU ticks. +unsigned long next_log; +/// @brief The total number of context-switches since the starting of the log +/// session. +size_t total_occurrences; + +/// @brief A structure that keeps track of scheduling statistics. +struct statistic { + task_struct *task; + unsigned long occur; +} arr_stats[PID_MAX_LIMIT]; + +/// @brief Updates when the logging should happen. +static inline void __scheduler_feedback_deadline_advance() +{ + next_log = timer_get_ticks() + (LOG_INTERVAL_SEC * TICKS_PER_SECOND); +} + +/// @brief Checks if the deadline is passed. +/// @return 1 if the deadline is passed, 0 otherwise. +static inline int __scheduler_feedback_deadline_check() +{ + return (next_log < timer_get_ticks()); +} + +/// @brief Logs the scheduling statistics either on file or on the terminal. +static inline void __scheduler_feedback_log() +{ + pr_info("Scheduling Statistics (%s)\n", POLICY_NAME); +#ifdef WRITE_ON_FILE + // Open the feedback file. + vfs_file_t *feedback = vfs_open(FEEDBACK_FILENAME, O_WRONLY, 0644); + if (feedback == NULL) { + pr_err("Failed to create the feedback file.\n"); + pr_err("Error: %s\n", strerror(errno)); + return; + } + char buffer[BUFSIZ]; + int written = 0; +#endif + for (size_t i = 0; i < PID_MAX_LIMIT; ++i) { + if (arr_stats[i].task) { + float tcpu = ((float)arr_stats[i].occur * 100.0) / total_occurrences; + pr_info("[%3d] | %-24s | -> TCPU: %.2f%% \n", + arr_stats[i].task->pid, + arr_stats[i].task->name, + tcpu); +#ifdef WRITE_ON_FILE + written = sprintf(buffer, "[%3d](%s)[%f], ", arr_stats[i].pid, arr_stats[i].name, tcpu); + vfs_write(feedback, buffer, offset, written); + offset += written; +#endif + } + } +#ifdef WRITE_ON_FILE + vfs_write(feedback, "/n", offset, 1); + offset++; + vfs_close(feedback); +#endif +} + +int scheduler_feedback_init() +{ +#ifdef WRITE_ON_FILE + // Create the feedback file, if necessary. + int ret = vfs_mkdir("/var", 0644); + if ((ret < 0) && (-ret != EEXIST)) { + pr_err("Failed to create the `/var` directory.\n"); + pr_err("Error: %s\n", strerror(errno)); + return 0; + } + // Create the feedback file, if necessary. + vfs_file_t *feedback = vfs_open(FEEDBACK_FILENAME, O_WRONLY | O_TRUNC | O_CREAT, 0644); + if (feedback == NULL) { + pr_err("Failed to create the feedback file.\n"); + pr_err("Error: %s\n", strerror(errno)); + return 0; + } + // Get the length of the headr. + ssize_t header_len = strlen(FEEDBACK_HEADER); + // Reset the offset. + offset = 0; + // Write the header. + vfs_write(feedback, FEEDBACK_HEADER, offset, header_len); + // Move the offset. + offset += header_len; + // Close the file. + vfs_close(feedback); +#endif + // Initialize the stat array. + for (size_t i = 0; i < PID_MAX_LIMIT; ++i) { + arr_stats[i].task = NULL; + arr_stats[i].occur = 0; + } + // Update when in the future, the logging should happen. + __scheduler_feedback_deadline_advance(); + // Initialize the number of occurrences. + total_occurrences = 0; + return 1; +} + +void scheduler_feedback_task_add(task_struct *task) +{ + assert(task && "Received a NULL task."); + arr_stats[task->pid].occur = 1; + arr_stats[task->pid].task = task; +} + +void scheduler_feedback_task_remove(pid_t pid) +{ + assert(pid < PID_MAX_LIMIT && "We received a wrong pid."); + total_occurrences -= arr_stats[pid].occur; + arr_stats[pid].occur = 0; + arr_stats[pid].task = NULL; +} + +void scheduler_feedback_task_update(task_struct *task) +{ + assert(task && "Received a NULL task."); + arr_stats[task->pid].occur += 1; + total_occurrences += 1; +} + +void scheduler_feedback_update() +{ + // If it is not yet time for the next reset, skip. + if (!__scheduler_feedback_deadline_check()) { + return; + } + // Dump on the feedback before reset. + __scheduler_feedback_log(); + // Reset the occurences. + for (size_t i = 0; i < PID_MAX_LIMIT; ++i) { + if (arr_stats[i].task) + arr_stats[i].occur = 0; + } + // Update when in the future, the logging should happen. + __scheduler_feedback_deadline_advance(); + // Reset the number of occurrences. + total_occurrences = 0; +} diff --git a/programs/tests/CMakeLists.txt b/programs/tests/CMakeLists.txt index c19252b..e13ff44 100644 --- a/programs/tests/CMakeLists.txt +++ b/programs/tests/CMakeLists.txt @@ -61,17 +61,17 @@ set(MENTOS_TESTS_DIR ${CMAKE_SOURCE_DIR}/files/bin/tests) set(TESTS t_mem.c t_fork.c - # Real-time programs + # Test real-time programs. t_periodic1.c t_periodic2.c t_periodic3.c - # Exec + # Testing task execution. t_exec.c t_exec_callee.c # Test environmental variables. t_setenv.c t_getenv.c - # Test: signals. + # Test signals. t_stopcont.c t_sigusr.c t_abort.c @@ -85,8 +85,11 @@ set(TESTS t_kill.c t_itimer.c t_gid.c + # Test semaphores. t_semget.c t_sem1.c + # Test scheduling feedback tests. + t_schedfb.c ) foreach(TEST ${TESTS}) diff --git a/programs/tests/t_schedfb.c b/programs/tests/t_schedfb.c new file mode 100644 index 0000000..81cf020 --- /dev/null +++ b/programs/tests/t_schedfb.c @@ -0,0 +1,22 @@ +/// @file t_schedfb.c +/// @brief Start the scheduler feedback session. +/// @copyright (c) 2014-2022 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include + +int main(int argc, char *argv[]) +{ + pid_t cpid; + printf("First test, the child will sleep, thus, they will not be scheduled.\n"); + for (int i = 0; i < 10; ++i) { + if ((cpid = fork()) == 0) { + execl("/bin/tests/t_alarm", "t_alarm", NULL); + return 0; + } + } + while (wait(NULL) != -1) continue; + return 0; +} \ No newline at end of file