๐กAWS EC2 Ubuntu 18.04 (x86_64) ์์ ์งํ!
$ sudo apt update
$ sudo apt install -y gcc make qemu-system-x86 python3
pyotato
๋ก ๋ฆฌํฌ ๋ณต์ master
์ ์ฌ๋ฆฌ๋๊ฒ ์๋๋ผ ๋ณต์ ํ ๋ด ๋ฆฌํฌ์์ ์์
ํ๊ธฐ$ git clone --bare https://github.com/casys-kaist/pintos-kaist.git
$ cd pintos-kaist.git
$ git push --mirror https://github.com/pyotato/pintos-kaist.git
$ cd ..
$ rm -rf pintos-kaist.git
$ git clone https://github.com/pyotato/pintos-kaist.git
20 of 27 tests failed
๋จ๋ฉด ์ฑ๊ณต์ !$ cd pintos-kaist
$ source ./activate
$ cd threads
$ make check
# .............. tons of operation results ............
20 of 27 tests failed.
๐ ๊ณผ์
- Alarm clock
- Priority scheduling
- Advanced scheduler (์ ํ)
timer_alarm(int ticks)
timer_alarm(int ticks)
: A system call that wakes up a process in ticks amount of time.busy waiting
device/thread.c
/* Yields the CPU. The current thread is not put to sleep and
may be scheduled again immediately at the scheduler's whim. */
void thread_yield (void)
{
struct thread *curr = thread_current (); /* 1๏ธโฃgets the pointer to the current thread struct "curr"*/
enum intr_level old_level;
ASSERT (!intr_context ());
old_level = intr_disable (); /*2๏ธโฃdisables disrupt*/
if (curr != idle_thread)
list_push_back (&ready_list, &curr->elem); /*3๏ธโฃ puts the current struc to the end of the ready_list*/
do_schedule (THREAD_READY);/* 4๏ธโฃchanges the state of the currently running thread to "THREAD_READY" & context switch*/
intr_set_level (old_level);
}
(YOUTUBE) pintos/src/device/timer.c
void thread_yield (void)
{
struct thread *cur = thread_current ();/* 1๏ธโฃgets the pointer to the current thread struct "curr"*/
enum intr_level old_level;
old_level = intr_disable (); /*2๏ธโฃdisables disrupt*/
if (cur != idle_thread)
list_push_back (&ready_list, &cur->elem);/*3๏ธโฃ puts the current struc to the end of the ready_list*/
cur->status = THREAD_READY;/* 4๏ธโฃchanges the state of the currently running thread to "THREAD_READY"*/
schedule(); /* 5๏ธโฃswitch context */
intr_set_level (old_level); /* 6๏ธโฃset intr level back to original state */
}
functions in
thread_yield()
thread_current()
: return the current threadintr_disable()
: disable the interrupt & return previous interrupt stateintr_set_level(old_level)
: set a state of interrupt to the state passed to parameter & return previous interrupt statelist_push_back(&ready_list, &cur->elem)
: insert the given entry to the last of list- put the current structure (&cur->elem) at the end of the ready_list
schedule()
: do context switch
1. OS(Operating System) puts itself to the blocked state
2. OS is responsible to wake the blocked process, frequently checking the timer interrupt
๐ค This approach saves the CPU cycle & power consumption
all_list
ready_list
: data structure that represents a set of processes that are waiting for a CPU to be executed sleep_list
: list of blocked threads, represent a set of threads that are in the blocked statewakes up
and moves the thread from the sleep_list to the read_liststatic struct list sleep_list;
Where
to declare the list When
to initialise ittimer_sleep()
is called, check the tickready_list
and insert it to the sleep queue./* Suspends execution for approximately TICKS timer ticks. */
void
timer_sleep (int64_t ticks) {
int64_t start = timer_ticks ();
ASSERT (intr_get_level () == INTR_ON);
// while (timer_elapsed (start) < ticks)
// thread_yield ();
if(timer_elapsed(start) < ticks)
thread_sleep(start+ticks); /*start+ticks : pass alarm time it needs to wake up */ /* ๐คimplement yourself*/
}
Challenges : start
time might not be valid since context switchinh occurs before line (2)
timer interrupt()
Timer interrupt is heart of everything!
Determine which threads to wake up everytime
For the threads to wake up, remove them from the sleep queue and insert it to the ready_list
(Don't forget to change the state of the thread from sleep to ready!!)
Depending on the Organisation the sleep_list, the time to identify the threads to wake up might differ!
SUMMARY OF FUNCTIONS TO MODIFY
thread_init()
: add the code to initialise the sleep queue data structuretimer_sleep()
: call the function that insert thread to the sleep queuetimer_interrupt()
: at every tick, check whether some thread mush wake up from sleep queue and call wake up function
$ pintos -- -q run alarm-multiple
FIFO
schedulingpreemption
select
the one with the highest priority
PRI_MIN(=0)
to PRI_MAX(=63)
PRI_DEFAULT(=31)
/* Change priority of the current thread to new_priority */
void thread_set_priority(int new_priority)
/* Return priority of the current thread*/
int thread_get_priority(void)
tid_t thread_create(const char *name, int priority, thread_func * function, void * aux)
schedule()
(the current thread yields CPU)void thread_unblock(struct thread *t)
: When the thread is unblocked, it is inserted to ready_list in the priority ordervoid thread_yield(void)
: The current thread yields CPU and it is inserted to ready_list in priority ordervoid thread_set_priority(int new_priority)
: Set priority of the current thread, reorder the ready_list
current thread_set_priority()
only sets value to new_priority.
we need to modify thread_set_priority()
value, position of the thread within ready_list
๐ค ํ์ฌ ์ฝ๋์์๋ cmp_priority
๊ฐ undefined ์ธ๋ฐ ๋ญ๋ก ๋ฐ๊ฟ์ค์ผํ์ง?
/* Transitions a blocked thread T to the ready-to-run state.
This is an error if T is not blocked. (Use thread_yield() to
make the running thread ready.)
This function does not preempt the running thread. This can
be important: if the caller had disabled interrupts itself,
it may expect that it can atomically unblock a thread and
update other data. */
void
thread_unblock (struct thread *t) {
enum intr_level old_level;
ASSERT (is_thread (t));
old_level = intr_disable ();
ASSERT (t->status == THREAD_BLOCKED);
// list_push_back (&ready_list, &t->elem); /*delete this code*/
list_insert_ordered(& ready_list, & t->elem,cmp_priority,NULL); /*put the newly unblocked thread in the ready_list, according to the priority */
t->status = THREAD_READY;
intr_set_level (old_level);
}
Wake the waiting thread with respect to the thread's priority
FIFO
/* initialise semaphore to the given value */
void sema_init(struct semaphore *sema, unsigned value)
/* request the semaphore.
Acquired the semaphore? decrease the value by 1 : process has to block */
void sema_down(struct semaphore *sema)
/* release the semaphore and increase the value by 1 */
void sema_up(struct semaphore *sema)
โ
void sema_down(struct semaphore *sema)
โ
void sema_up(struct semaphore *sema)
/* initialise the lock data structure*/
void lock_init(struct lock *loc)
/* request the lock */
void lock_acquire(struct lock *loc)
/* release the lock*/
void lock_release(struct lock *loc)
lock_init()
is already implemented by semaphore, in order to update lock
data structure according to priority, it is efficient to modify semaphore!
/* initialise the condition variable data structure*/
void cond_init(struct lock *loc)
/*
once process calls cond_wait(), the process is set to block state
and waits for signal by the condition variable
*/
void cond_wait(struct lock *loc)
/* send a signal to thread of the highes priority wiating in the condition variable*/
void cond_signal(struct lock *loc)
/* send a signal to all threads waiting in the condition variable*/
void cond_broadcast(struct lock *loc)
waiters
list in order of priorityvoid sema_down(struct semaphore * sema)
void cond_wait(struct condition * cond,struct lock * lock)
priority
waiters
listvoid sema_up(struct semaphore *sema)
void cond_signal(struct condition *cond, struct lock *lock UNUSED)
LOCK
inherits the highest priority of the incoming threadsalways have the updated HIGHEST PRIORITY
(not it's original priority)wait_on_lock
: lock that it waits for /* initialises data structure for priority donation */
static void init_thread(struct thread *t, const char *name, int priority)
/*
1๏ธโฃ if the lock is not available, store address of the lock
2๏ธโฃ store the current priority & maintain donated threads on list (multiple donation)
3๏ธโฃ donate priority
*/
void lock_acquire(struct lock * lock)
/* when the lock is released , REMOVE the thread that holds the lock on donation list & set priority properly
*/
void lock_release(struct lock * lock)
/* set priority considering the donation */
void thread_set_priority(int new_priority)
TICKS
๋จ์ ๋์ (ํ์ด๋จธ๊ฐ ์งธ๊น๊ฑฐ๋ฆด ๋์) ์คํ ๋ฏธ๋ฃจ๊ธฐ/*
Suspends execution for approximately TICKS timer ticks.
*/
void
timer_sleep (int64_t ticks) {
int64_t start = timer_ticks ();
ASSERT (intr_get_level () == INTR_ON);
while (timer_elapsed (start) < ticks)
thread_yield ();
}
busy wait
์ค์!busy wait
: loop๋ฅผ ๋๋ฉด์ ํ์ฌ ์๊ฐ์ ์ฒดํฌํ๋ฉด์ ์๊ฐ์ด ์ถฉ๋ถํ ํ๋ฅผ ๋๊น์ง thread_yield()
ํจ์ ํธ์ถvoid thread_yield (void) {
struct thread *curr = thread_current (); /* ํ์ฌ run(์คํ)์ค์ธ thread ๋ฆฌํด.*/
enum intr_level old_level; /*interrupt ํ์ฑํ ์ฌ๋ถ*/
ASSERT (!intr_context ()); /*#####test######*/
old_level = intr_disable (); /* interrupt ๋นํ์ฑํํ๊ณ ์ด์ interrupt ์ํ ๋ฆฌํดํด์ค */
if (curr != idle_thread)/* Returns true during processing of an external interrupt and false at all other times. */
list_push_back (&ready_list, &curr->elem); /* ready_list ๋์ ํ์ฌ elem ์ฝ์
. */
do_schedule (THREAD_READY); /* ์๋ก์ด process ์ค์ผ์ค๋ง ํด์ฃผ๋ ํจ์. ํ์ฌ thread์ ์ํ๋ฅผ THREAD_READY๋ก ๋ฐ๊ฟ์ฃผ๊ณ run(์คํ)ํด์ผํ ๋ค๋ฅธ thread์ฐพ๊ณ ๊ทธ thread๋ก switchํจ โ ๏ธ์ง์
์ง์ ์ interrupt ๋นํ์ฑํ๋ ์ํ์ฌ์ผํจ. โ ๏ธ */
intr_set_level (old_level); /* old_level์์ ์์ธํ ๋๋ก interrupt์ ํ์ฑ/๋นํ์ฑํํด์ฃผ๊ณ ์ด์ interrupt ์ํ๋ฅผ ๋ฆฌํดํด์ค */
}
๋ฏ์ฐ๋ค ํํ๋ฏผ