Merge branch 'BSD-merged' into 'master'

BSD Scheduler implementation

See merge request lab2425_autumn/pintos_22!9
This commit is contained in:
Dias Alberto, Ethan
2024-10-23 16:34:39 +00:00
5 changed files with 223 additions and 16 deletions

3
.gitignore vendored
View File

@@ -31,3 +31,6 @@
*.nav *.nav
*.toc *.toc
#ignore files from CLion/VSCode IDEs
.idea
.vscode

95
src/threads/fixed-point.h Normal file
View File

@@ -0,0 +1,95 @@
#include <stdint.h>
#ifndef FIXED_POINT_H
#define FIXED_POINT_H
typedef struct
{
int32_t raw;
} fp32_t;
/* Fixed Point Arithmetic bit count constants */
#define NUM_FRAC_BITS 14
#define NUM_INT_BITS (31 - NUM_FRAC_BITS)
#define CONVERSION_CONST (1 << NUM_FRAC_BITS) /* f = 2^q, (2^20) */
/* Fixed Point Arithmetic conversion operations */
/* Converts an integer n to a fixed point number */
inline fp32_t
int_to_fp (int32_t n)
{
return (fp32_t){ n * CONVERSION_CONST };
}
/* Handles conversion of fixed point to integer. First version truncates, second one rounds */
inline int32_t
fp_floor (fp32_t x)
{
return x.raw / CONVERSION_CONST;
}
inline int32_t
fp_round (fp32_t x)
{
if (x.raw >= 0)
return (x.raw + CONVERSION_CONST / 2) / CONVERSION_CONST;
else
return (x.raw - CONVERSION_CONST / 2) / CONVERSION_CONST;
}
/* Add two fixed points */
inline fp32_t
fp_add (fp32_t x, fp32_t y)
{
return (fp32_t){ x.raw + y.raw };
}
/* Subtract two fixed points */
inline fp32_t
fp_sub (fp32_t x, fp32_t y)
{
return (fp32_t){ x.raw - y.raw };
}
/* Add fixed point to integer */
inline fp32_t
fp_add_int (fp32_t x, int32_t n)
{
return (fp32_t){ x.raw + n * CONVERSION_CONST };
}
/* Subtract integer from fixed point */
inline fp32_t
fp_sub_int (fp32_t x, int32_t n)
{
return (fp32_t){ x.raw - n * CONVERSION_CONST };
}
/* Multiple two fixed points */
inline fp32_t
fp_mul (fp32_t x, fp32_t y)
{
return (fp32_t){ ((int64_t)x.raw) * y.raw / CONVERSION_CONST };
}
/* Divide two fixed points */
inline fp32_t
fp_div (fp32_t x, fp32_t y)
{
return (fp32_t){ ((int64_t)x.raw) * CONVERSION_CONST / y.raw };
}
/* Multiply fixed point and integer */
inline fp32_t
fp_mul_int (fp32_t x, int32_t n)
{
return (fp32_t){ x.raw * n };
}
/* Divide fixed point by integer */
inline fp32_t
fp_div_int (fp32_t x, int32_t n)
{
return (fp32_t){ x.raw / n };
}
#endif //FIXED_POINT_H

View File

@@ -344,6 +344,7 @@ lock_release (struct lock *lock)
lock->holder = NULL; lock->holder = NULL;
sema_up (&lock->semaphore); sema_up (&lock->semaphore);
thread_yield ();
} }
/* Returns true if the current thread holds LOCK, false /* Returns true if the current thread holds LOCK, false

View File

@@ -4,6 +4,8 @@
#include <random.h> #include <random.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "devices/timer.h"
#include "threads/fixed-point.h"
#include "threads/flags.h" #include "threads/flags.h"
#include "threads/interrupt.h" #include "threads/interrupt.h"
#include "threads/intr-stubs.h" #include "threads/intr-stubs.h"
@@ -49,6 +51,7 @@ struct kernel_thread_frame
static long long idle_ticks; /* # of timer ticks spent idle. */ static long long idle_ticks; /* # of timer ticks spent idle. */
static long long kernel_ticks; /* # of timer ticks in kernel threads. */ static long long kernel_ticks; /* # of timer ticks in kernel threads. */
static long long user_ticks; /* # of timer ticks in user programs. */ static long long user_ticks; /* # of timer ticks in user programs. */
static fp32_t load_avg = { 0 }; /* System load average. */
/* Scheduling. */ /* Scheduling. */
#define TIME_SLICE 4 /* # of timer ticks to give each thread. */ #define TIME_SLICE 4 /* # of timer ticks to give each thread. */
@@ -64,9 +67,14 @@ static void kernel_thread (thread_func *, void *aux);
static void idle (void *aux UNUSED); static void idle (void *aux UNUSED);
static struct thread *running_thread (void); static struct thread *running_thread (void);
static struct thread *next_thread_to_run (void); static struct thread *next_thread_to_run (void);
static void init_thread (struct thread *, const char *name, int priority); static void init_thread (struct thread *, const char *name, int nice,
int priority, fp32_t recent_cpu);
static bool is_thread (struct thread *) UNUSED; static bool is_thread (struct thread *) UNUSED;
static void *alloc_frame (struct thread *, size_t size); static void *alloc_frame (struct thread *, size_t size);
static int calculate_bsd_priority (fp32_t recent_cpu, int nice);
static void thread_update_recent_cpu (struct thread *t, void *aux UNUSED);
static bool thread_priority_less (const struct list_elem *a,
const struct list_elem *b, void *aux UNUSED);
static void schedule (void); static void schedule (void);
void thread_schedule_tail (struct thread *prev); void thread_schedule_tail (struct thread *prev);
static tid_t allocate_tid (void); static tid_t allocate_tid (void);
@@ -97,7 +105,9 @@ thread_init (void)
/* Set up a thread structure for the running thread. */ /* Set up a thread structure for the running thread. */
initial_thread = running_thread (); initial_thread = running_thread ();
init_thread (initial_thread, "main", PRI_DEFAULT); fp32_t initial_thread_recent_cpu = { 0 };
init_thread (initial_thread, "main", NICE_DEFAULT, PRI_DEFAULT,
initial_thread_recent_cpu);
initial_thread->status = THREAD_RUNNING; initial_thread->status = THREAD_RUNNING;
initial_thread->tid = allocate_tid (); initial_thread->tid = allocate_tid ();
} }
@@ -147,6 +157,28 @@ thread_tick (void)
else else
kernel_ticks++; kernel_ticks++;
/* Update system load_avg and all threads recent_cpu every second. */
int64_t ticks = timer_ticks ();
if (thread_mlfqs && (ticks % TIMER_FREQ == 0))
{
size_t ready = threads_ready ();
if (t != idle_thread)
ready++;
fp32_t old_coeff = fp_mul (fp_div_int (int_to_fp (59), 60), load_avg);
fp32_t new_coeff = fp_div_int (int_to_fp (ready), 60);
load_avg = fp_add (old_coeff, new_coeff);
thread_foreach (thread_update_recent_cpu, NULL);
}
/* Update current thread's recent_cpu. */
if (thread_mlfqs && (t != idle_thread))
{
t->recent_cpu = fp_add_int (t->recent_cpu, 1);
if (ticks % 4 == 0) // recent_cpu was updated, update priority.
t->priority = calculate_bsd_priority (t->recent_cpu, t->nice);
}
/* Enforce preemption. */ /* Enforce preemption. */
if (++thread_ticks >= TIME_SLICE) if (++thread_ticks >= TIME_SLICE)
intr_yield_on_return (); intr_yield_on_return ();
@@ -194,7 +226,8 @@ thread_create (const char *name, int priority,
return TID_ERROR; return TID_ERROR;
/* Initialize thread. */ /* Initialize thread. */
init_thread (t, name, priority); struct thread *pt = thread_current ();
init_thread (t, name, pt->nice, priority, pt->recent_cpu);
tid = t->tid = allocate_tid (); tid = t->tid = allocate_tid ();
/* Prepare thread for first run by initializing its stack. /* Prepare thread for first run by initializing its stack.
@@ -260,8 +293,11 @@ thread_unblock (struct thread *t)
old_level = intr_disable (); old_level = intr_disable ();
ASSERT (t->status == THREAD_BLOCKED); ASSERT (t->status == THREAD_BLOCKED);
/* Insert the thread back into the ready list in priority order. */ if (thread_mlfqs)
list_insert_ordered(&ready_list, &t->elem, priority_more, NULL); list_insert_ordered (&ready_list, &t->elem, thread_priority_less, NULL);
else
/* Insert the thread back into the ready list in priority order. */
list_insert_ordered(&ready_list, &t->elem, priority_more, NULL);
t->status = THREAD_READY; t->status = THREAD_READY;
intr_set_level (old_level); intr_set_level (old_level);
@@ -333,9 +369,15 @@ thread_yield (void)
old_level = intr_disable (); old_level = intr_disable ();
/* Insert the thread back into the ready list in priority order. */
if (cur != idle_thread) if (cur != idle_thread)
list_insert_ordered(&ready_list, &cur->elem, priority_more, NULL); {
if (thread_mlfqs)
list_insert_ordered (&ready_list, &cur->elem, thread_priority_less,
NULL);
else
/* Insert the thread back into the ready list in priority order. */
list_insert_ordered(&ready_list, &cur->elem, priority_more, NULL);
}
cur->status = THREAD_READY; cur->status = THREAD_READY;
schedule (); schedule ();
@@ -366,10 +408,12 @@ bool
priority_more (const struct list_elem *a_, const struct list_elem *b_, priority_more (const struct list_elem *a_, const struct list_elem *b_,
void *aux UNUSED) void *aux UNUSED)
{ {
struct thread *a = list_entry (a_, struct thread, elem); struct thread *a = list_entry (a_, struct thread, elem);
struct thread *b = list_entry (b_, struct thread, elem); struct thread *b = list_entry (b_, struct thread, elem);
return a->priority > b->priority; return a->priority > b->priority;
} }
/* Function that compares the two threads associated with the provided /* Function that compares the two threads associated with the provided
@@ -390,6 +434,10 @@ donor_priority_less (const struct list_elem *a_, const struct list_elem *b_,
void void
thread_set_priority (int new_base_priority) thread_set_priority (int new_base_priority)
{ {
if (thread_mlfqs)
return;
ASSERT (new_base_priority >= PRI_MIN); ASSERT (new_base_priority >= PRI_MIN);
ASSERT (new_base_priority <= PRI_MAX); ASSERT (new_base_priority <= PRI_MAX);
@@ -412,6 +460,20 @@ thread_get_priority (void)
return thread_current ()->priority; return thread_current ()->priority;
} }
/* Updates recent_cpu for a thread. */
static void
thread_update_recent_cpu (struct thread *t, void *aux UNUSED)
{
fp32_t curr_recent_cpu = t->recent_cpu;
fp32_t dbl_load_avg = fp_mul_int (load_avg, 2);
fp32_t recent_cpu_coeff
= fp_div (dbl_load_avg, fp_add_int (dbl_load_avg, 1));
t->recent_cpu
= fp_add_int (fp_mul (recent_cpu_coeff, curr_recent_cpu), t->nice);
// recent_cpu was updated, update priority.
t->priority = calculate_bsd_priority (t->recent_cpu, t->nice);
/* Recalculates the effective priority of the current thread. */ /* Recalculates the effective priority of the current thread. */
void void
thread_recalculate_priority (void) thread_recalculate_priority (void)
@@ -435,37 +497,43 @@ thread_recalculate_priority (void)
t->priority = max_donated_priority; t->priority = max_donated_priority;
} }
intr_set_level (old_level); intr_set_level (old_level);
} }
/* Sets the current thread's nice value to NICE. */ /* Sets the current thread's nice value to NICE. */
void void
thread_set_nice (int nice UNUSED) thread_set_nice (int nice)
{ {
/* Not yet implemented. */ ASSERT (NICE_MIN <= nice && nice <= NICE_MAX);
struct thread *t = thread_current ();
t->nice = nice;
int priority = calculate_bsd_priority (t->recent_cpu, t->nice);
struct thread *next_t
= list_entry (list_begin (&ready_list), struct thread, elem);
if (priority < next_t->priority)
thread_yield ();
} }
/* Returns the current thread's nice value. */ /* Returns the current thread's nice value. */
int int
thread_get_nice (void) thread_get_nice (void)
{ {
/* Not yet implemented. */ return thread_current ()->nice;
return 0;
} }
/* Returns 100 times the system load average. */ /* Returns 100 times the system load average. */
int int
thread_get_load_avg (void) thread_get_load_avg (void)
{ {
/* Not yet implemented. */ return fp_round (fp_mul_int (load_avg, 100));
return 0;
} }
/* Returns 100 times the current thread's recent_cpu value. */ /* Returns 100 times the current thread's recent_cpu value. */
int int
thread_get_recent_cpu (void) thread_get_recent_cpu (void)
{ {
/* Not yet implemented. */ return fp_round (fp_mul_int (thread_current ()->recent_cpu, 100));
return 0;
} }
/* Reinsert thread t into the ready list at its correct position /* Reinsert thread t into the ready list at its correct position
@@ -557,7 +625,8 @@ is_thread (struct thread *t)
/* Does basic initialization of T as a blocked thread named /* Does basic initialization of T as a blocked thread named
NAME. */ NAME. */
static void static void
init_thread (struct thread *t, const char *name, int priority) init_thread (struct thread *t, const char *name, int nice, int priority,
fp32_t recent_cpu)
{ {
enum intr_level old_level; enum intr_level old_level;
@@ -569,7 +638,14 @@ init_thread (struct thread *t, const char *name, int priority)
t->status = THREAD_BLOCKED; t->status = THREAD_BLOCKED;
strlcpy (t->name, name, sizeof t->name); strlcpy (t->name, name, sizeof t->name);
t->stack = (uint8_t *) t + PGSIZE; t->stack = (uint8_t *) t + PGSIZE;
t->priority
= thread_mlfqs ? calculate_bsd_priority (recent_cpu, nice) : priority;
t->nice = nice;
t->recent_cpu = recent_cpu;
t->base_priority = priority; t->base_priority = priority;
t->magic = THREAD_MAGIC; t->magic = THREAD_MAGIC;
list_init (&t->donors_list); list_init (&t->donors_list);
@@ -654,6 +730,29 @@ thread_schedule_tail (struct thread *prev)
} }
} }
/* Calculates BSD priority for a thread */
static int
calculate_bsd_priority (fp32_t recent_cpu, int nice)
{
int priority = PRI_MAX - (fp_round (recent_cpu) / 4) - (nice * 2);
if (priority < PRI_MIN)
return PRI_MIN;
if (priority > PRI_MAX)
return PRI_MAX;
return priority;
}
/* Returns true if thread a's priority is strictly greater than
thread b's priority. */
static bool
thread_priority_less (const struct list_elem *a, const struct list_elem *b,
void *aux UNUSED)
{
struct thread *ta = list_entry (a, struct thread, elem);
struct thread *tb = list_entry (b, struct thread, elem);
return ta->priority > tb->priority;
}
/* Schedules a new process. At entry, interrupts must be off and /* Schedules a new process. At entry, interrupts must be off and
the running process's state must have been changed from the running process's state must have been changed from
running to some other state. This function finds another running to some other state. This function finds another

View File

@@ -4,6 +4,7 @@
#include <debug.h> #include <debug.h>
#include <list.h> #include <list.h>
#include <stdint.h> #include <stdint.h>
#include "threads/fixed-point.h"
/* States in a thread's life cycle. */ /* States in a thread's life cycle. */
enum thread_status enum thread_status
@@ -24,6 +25,10 @@ typedef int tid_t;
#define PRI_DEFAULT 31 /* Default priority. */ #define PRI_DEFAULT 31 /* Default priority. */
#define PRI_MAX 63 /* Highest priority. */ #define PRI_MAX 63 /* Highest priority. */
#define NICE_MIN -20 /* Lowest niceness. */
#define NICE_DEFAULT 0 /* Default niceness. */
#define NICE_MAX 20 /* Highest niceness. */
/* A kernel thread or user process. /* A kernel thread or user process.
Each thread structure is stored in its own 4 kB page. The Each thread structure is stored in its own 4 kB page. The
@@ -102,6 +107,10 @@ struct thread
/* Shared between thread.c and synch.c. */ /* Shared between thread.c and synch.c. */
struct list_elem elem; /* List element. */ struct list_elem elem; /* List element. */
/* MLFQS items */
int nice; /* Nice value for this thread */
fp32_t recent_cpu; /* Amount of time this process received */
#ifdef USERPROG #ifdef USERPROG
/* Owned by userprog/process.c. */ /* Owned by userprog/process.c. */
uint32_t *pagedir; /* Page directory. */ uint32_t *pagedir; /* Page directory. */