X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fthreads%2Fthread.c;h=0a606b76d19dc4374fd84566e9bf9d861d346caa;hb=f0ad7eb8b43516c7a2999fd217ec85d954dfc791;hp=8a55699ded46cf7165715966950641aa7a2aca49;hpb=4ae5b813663cd56fcbe1f1f547c9d445e5dc6107;p=pintos-anon diff --git a/src/threads/thread.c b/src/threads/thread.c index 8a55699..0a606b7 100644 --- a/src/threads/thread.c +++ b/src/threads/thread.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include "threads/flags.h" #include "threads/interrupt.h" @@ -11,7 +12,7 @@ #include "threads/switch.h" #include "threads/synch.h" #ifdef USERPROG -#include "userprog/gdt.h" +#include "userprog/process.h" #endif /* Random value for struct thread's `magic' member. @@ -40,25 +41,29 @@ struct kernel_thread_frame void *aux; /* Auxiliary data for function. */ }; +/* Statistics. */ +static long long idle_ticks; /* # of timer ticks spent idle. */ +static long long kernel_ticks; /* # of timer ticks in kernel threads. */ +static long long user_ticks; /* # of timer ticks in user programs. */ + static void kernel_thread (thread_func *, void *aux); static void idle (void *aux UNUSED); static struct thread *running_thread (void); static struct thread *next_thread_to_run (void); -static struct thread *new_thread (const char *name, int priority); static void init_thread (struct thread *, const char *name, int priority); static bool is_thread (struct thread *); static void *alloc_frame (struct thread *, size_t size); -static void destroy_thread (struct thread *); static void schedule (void); void schedule_tail (struct thread *prev); static tid_t allocate_tid (void); /* Initializes the threading system by transforming the code - that's currently running into a thread. Note that this is - possible only because the loader was careful to put the bottom - of the stack at a page boundary; it won't work in general. - Also initializes the run queue. + that's currently running into a thread. This can't work in + general and it is possible in this case only because loader.S + was careful to put the bottom of the stack at a page boundary. + + Also initializes the run queue and the tid lock. After calling this function, be sure to initialize the page allocator before trying to create any threads with @@ -69,15 +74,13 @@ thread_init (void) ASSERT (intr_get_level () == INTR_OFF); lock_init (&tid_lock, "tid"); + list_init (&ready_list); /* Set up a thread structure for the running thread. */ initial_thread = running_thread (); init_thread (initial_thread, "main", PRI_DEFAULT); initial_thread->status = THREAD_RUNNING; initial_thread->tid = allocate_tid (); - - /* Initialize run queue. */ - list_init (&ready_list); } /* Starts preemptive thread scheduling by enabling interrupts. @@ -89,6 +92,30 @@ thread_start (void) intr_enable (); } +/* Called by the timer interrupt handler at each timer tick to + update statistics. */ +void +thread_tick (void) +{ + struct thread *t = thread_current (); + if (t == idle_thread) + idle_ticks++; +#ifdef USERPROG + else if (t->pagedir != NULL) + user_ticks++; +#endif + else + kernel_ticks++; +} + +/* Prints thread statistics. */ +void +thread_print_stats (void) +{ + printf ("Thread: %lld idle ticks, %lld kernel ticks, %lld user ticks\n", + idle_ticks, kernel_ticks, user_ticks); +} + /* Creates a new kernel thread named NAME with the given initial PRIORITY, which executes FUNCTION passing AUX as the argument, and adds it to the ready queue. If thread_start() has been @@ -114,10 +141,14 @@ thread_create (const char *name, int priority, ASSERT (function != NULL); - t = new_thread (name, priority); + /* Allocate thread. */ + t = palloc_get_page (PAL_ZERO); if (t == NULL) return TID_ERROR; - tid = t->tid; + + /* Initialize thread. */ + init_thread (t, name, priority); + tid = t->tid = allocate_tid (); /* Stack frame for kernel_thread(). */ kf = alloc_frame (t, sizeof *kf); @@ -139,59 +170,25 @@ thread_create (const char *name, int priority, return tid; } -#ifdef USERPROG -/* Starts a new thread running a user program loaded from - FILENAME, and adds it to the ready queue. If thread_start() - has been called, then new thread may be scheduled before - thread_execute() returns.*/ -tid_t -thread_execute (const char *filename) -{ - struct thread *t; - struct intr_frame *if_; - struct switch_entry_frame *ef; - struct switch_threads_frame *sf; - void (*start) (void); - tid_t tid; - - ASSERT (filename != NULL); - - t = new_thread (filename, PRI_DEFAULT); - if (t == NULL) - return TID_ERROR; - tid = t->tid; - - if (!addrspace_load (t, filename, &start)) - PANIC ("%s: program load failed", filename); - - /* Interrupt frame. */ - if_ = alloc_frame (t, sizeof *if_); - if_->es = SEL_UDSEG; - if_->ds = SEL_UDSEG; - if_->eip = start; - if_->cs = SEL_UCSEG; - if_->eflags = FLAG_IF | FLAG_MBS; - if_->esp = PHYS_BASE; - if_->ss = SEL_UDSEG; - - /* Stack frame for switch_entry(). */ - ef = alloc_frame (t, sizeof *ef); - ef->eip = intr_exit; - - /* Stack frame for switch_threads(). */ - sf = alloc_frame (t, sizeof *sf); - sf->eip = switch_entry; +/* Puts the current thread to sleep. It will not be scheduled + again until awoken by thread_unblock(). - /* Add to run queue. */ - thread_unblock (t); + This function must be called with interrupts turned off. It + is usually a better idea to use one of the synchronization + primitives in synch.h. */ +void +thread_block (void) +{ + ASSERT (!intr_context ()); + ASSERT (intr_get_level () == INTR_OFF); - return tid; + thread_current ()->status = THREAD_BLOCKED; + schedule (); } -#endif -/* Transitions a blocked thread T from its current state to the - ready-to-run state. This is an error if T is not blocked. - (Use thread_yield() to make the running thread ready.) */ +/* 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.) */ void thread_unblock (struct thread *t) { @@ -246,6 +243,10 @@ thread_exit (void) { ASSERT (!intr_context ()); +#ifdef USERPROG + process_exit (); +#endif + /* Just set our status to dying and schedule another process. We will be destroyed during the call to schedule_tail(). */ intr_disable (); @@ -270,22 +271,6 @@ thread_yield (void) schedule (); intr_set_level (old_level); } - -/* Puts the current thread to sleep. It will not be scheduled - again until awoken by thread_unblock(). - - This function must be called with interrupts turned off. It - is usually a better idea to use one of the synchronization - primitives in synch.h. */ -void -thread_block (void) -{ - ASSERT (!intr_context ()); - ASSERT (intr_get_level () == INTR_OFF); - - thread_current ()->status = THREAD_BLOCKED; - schedule (); -} /* Idle thread. Executes when no other thread is ready to run. */ static void @@ -300,7 +285,8 @@ idle (void *aux UNUSED) thread_block (); intr_enable (); - /* Use CPU `hlt' instruction to wait for interrupt. */ + /* Use CPU `hlt' instruction to wait for interrupt. + See [IA32-v2a] "HLT" and [IA32-v3] 7.7. */ asm ("hlt"); } } @@ -337,23 +323,7 @@ is_thread (struct thread *t) return t != NULL && t->magic == THREAD_MAGIC; } -/* Creates a new thread named NAME as a child of the running - thread. Returns the new thread if successful or a null - pointer on failure. */ -static struct thread * -new_thread (const char *name, int priority) -{ - struct thread *t = palloc_get (PAL_ZERO); - if (t != NULL) - { - init_thread (t, name, priority); - t->tid = allocate_tid (); - } - - return t; -} - -/* Does basic initialization of T as a new, blocked thread named +/* Does basic initialization of T as a blocked thread named NAME. */ static void init_thread (struct thread *t, const char *name, int priority) @@ -397,22 +367,6 @@ next_thread_to_run (void) return list_entry (list_pop_front (&ready_list), struct thread, elem); } -/* Destroys T, which must be in the dying state and must not be - the running thread. */ -static void -destroy_thread (struct thread *t) -{ - ASSERT (is_thread (t)); - ASSERT (t->status == THREAD_DYING); - ASSERT (t != thread_current ()); - -#ifdef USERPROG - addrspace_destroy (t); -#endif - if (t != initial_thread) - palloc_free (t); -} - /* Completes a thread switch by activating the new thread's page tables, and, if the previous thread is dying, destroying it. @@ -423,6 +377,10 @@ destroy_thread (struct thread *t) the first time a thread is scheduled it is called by switch_entry() (see switch.S). + It's not safe to call printf() until the thread switch is + complete. In practice that means that printf()s should be + added at the end of the function. + After this function and its caller returns, the thread switch is complete. */ void @@ -437,20 +395,27 @@ schedule_tail (struct thread *prev) #ifdef USERPROG /* Activate the new address space. */ - addrspace_activate (cur); + process_activate (); #endif - /* If the thread we switched from is dying, destroy it. - This must happen late because it's not a good idea to - e.g. destroy the page table you're currently using. */ + /* If the thread we switched from is dying, destroy its struct + thread. This must happen late so that thread_exit() doesn't + pull out the rug under itself. */ if (prev != NULL && prev->status == THREAD_DYING) - destroy_thread (prev); + { + ASSERT (prev != cur); + if (prev != initial_thread) + palloc_free_page (prev); + } } /* Schedules a new process. At entry, interrupts must be off and the running process's state must have been changed from running to some other state. This function finds another - thread to run and switches to it. */ + thread to run and switches to it. + + It's not safe to call printf() until schedule_tail() has + completed. */ static void schedule (void) {