Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • y2-rhymansaib/pintos_student
1 result
Select Git revision
Show changes
......@@ -11,7 +11,6 @@
#include "threads/switch.h"
#include "threads/synch.h"
#include "threads/vaddr.h"
#ifdef USERPROG
#include "userprog/process.h"
#endif
......@@ -25,6 +24,9 @@
that are ready to run but not actually running. */
static struct list ready_list;
/* List of processes in sleep (wait) state (i.e. wait queue). */
static struct list wait_list;
/* List of all processes. Processes are added to this list
when they are first scheduled and removed when they exit. */
static struct list all_list;
......@@ -65,15 +67,20 @@ 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 void init_thread (struct thread *, const char *name, int priority);
static bool is_thread (struct thread *) UNUSED;
static void *alloc_frame (struct thread *, size_t size);
static void schedule (void);
void thread_schedule_tail (struct thread *prev);
static tid_t allocate_tid (void);
void thread_awake (int64_t current_tick);
/* Helper (Auxiliary) functions */
static bool comparator_greater_thread_priority
(const struct list_elem *, const struct list_elem *, void *aux);
/* Initializes the threading system by transforming the code
that's currently running into a thread. This can't work in
general and it is possible in this case only because loader.S
......@@ -94,15 +101,15 @@ thread_init (void)
lock_init (&tid_lock);
list_init (&ready_list);
list_init (&wait_list);
list_init (&all_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 ();
initial_thread->sleep_endtick = 0; // a dummy value
}
/* Starts preemptive thread scheduling by enabling interrupts.
......@@ -112,9 +119,6 @@ thread_start (void)
{
/* Create the idle thread. */
struct semaphore idle_started;
//init_info (initial_thread, initial_thread->tid);
sema_init (&idle_started, 0);
thread_create ("idle", PRI_MIN, idle, &idle_started);
......@@ -126,9 +130,12 @@ thread_start (void)
}
/* Called by the timer interrupt handler at each timer tick.
Thus, this function runs in an external interrupt context. */
Thus, this function runs in an external interrupt context.
The parameter 'tick' is the current tick count held by
the timer device. */
void
thread_tick (void)
thread_tick (int64_t tick)
{
struct thread *t = thread_current ();
......@@ -142,11 +149,40 @@ thread_tick (void)
else
kernel_ticks++;
/* Wake any thread whose ticks_end has been expired. */
thread_awake(tick);
/* Enforce preemption. */
if (++thread_ticks >= TIME_SLICE)
intr_yield_on_return ();
}
/* Wake up all sleeping threads whose ticks_end has been expired,
removing from the wait queue and pushing it into the ready queue.
This function must be called with interrupts turned off. */
void
thread_awake (int64_t current_tick) {
struct list_elem *e;
// interrupt level check
ASSERT (intr_get_level () == INTR_OFF);
// enumerate all threads in wait queue
for (e = list_begin (&wait_list); e != list_end (&wait_list); e = list_next (e))
{
struct thread *t = list_entry (e, struct thread, waitelem);
if (t->sleep_endtick <= current_tick) {
/* sleep is expired. awake up t */
t->sleep_endtick = 0;
list_remove (&t->waitelem);
// Add to run queue.
thread_unblock (t);
}
}
}
/* Prints thread statistics. */
void
thread_print_stats (void)
......@@ -209,9 +245,36 @@ thread_create (const char *name, int priority,
/* Add to run queue. */
thread_unblock (t);
/* If the newly created thread has a higher priority than
* the currently running thread, then there should be an
* immediate context switching, for thread priority scheduling. */
if (priority > thread_current()->priority) {
// current thread releases off its running
thread_yield();
}
return tid;
}
/* Make the current thread T to sleep, until the timer ticks
reaches 'ticks_end'.
It subsequently calls thread_block(), making T sleep actually.
This function must be called with interrupts turned off. */
void
thread_sleep_until (int64_t ticks_end)
{
struct thread *t = thread_current();
t->sleep_endtick = ticks_end;
// put T into the wait queue
list_push_back (&wait_list, &t->waitelem);
// make the current thread block (sleeped)
thread_block();
}
/* Puts the current thread to sleep. It will not be scheduled
again until awoken by thread_unblock().
......@@ -245,8 +308,17 @@ thread_unblock (struct thread *t)
old_level = intr_disable ();
ASSERT (t->status == THREAD_BLOCKED);
list_push_back (&ready_list, &t->elem);
// t will turn into ready-to-run state : inserting into ready_list
// maintaining in the non-decreasing order of thread priority
list_insert_ordered (&ready_list, &t->elem, comparator_greater_thread_priority, NULL);
t->status = THREAD_READY;
// ensure preemption : compare priorities of current thread and t (to be unblocked),
if (thread_current() != idle_thread && thread_current()->priority < t->priority )
thread_yield();
intr_set_level (old_level);
}
......@@ -294,6 +366,15 @@ thread_exit (void)
process_exit ();
#endif
struct thread *curr = thread_current();
// release all locks
struct list_elem *e;
for (e = list_begin (&curr->locks); e != list_end (&curr->locks); e = list_next (e)) {
struct lock *lock = list_entry(e, struct lock, lockelem);
lock_release(lock);
}
/* Remove thread from all threads list, set our status to dying,
and schedule another process. That process will destroy us
when it calls thread_schedule_tail(). */
......@@ -315,8 +396,10 @@ thread_yield (void)
ASSERT (!intr_context ());
old_level = intr_disable ();
if (cur != idle_thread)
list_push_back (&ready_list, &cur->elem);
if (cur != idle_thread) {
// t will turn into ready-to-run state : inserting into ready_list
list_insert_ordered (&ready_list, &cur->elem, comparator_greater_thread_priority, NULL);
}
cur->status = THREAD_READY;
schedule ();
intr_set_level (old_level);
......@@ -339,13 +422,53 @@ thread_foreach (thread_action_func *func, void *aux)
}
}
/* Sets the current thread's priority to NEW_PRIORITY. */
/* Sets the current thread's priority to NEW_PRIORITY when a donation
was not performed. */
void
thread_set_priority (int new_priority)
{
thread_current ()->priority = new_priority;
// if the current thread has no donation, then it is normal priority change request.
struct thread *t_current = thread_current();
if (t_current->priority == t_current->original_priority) {
t_current->priority = new_priority;
t_current->original_priority = new_priority;
}
// otherwise, it has a donation: the original priority only should have changed
else {
t_current->original_priority = new_priority;
}
// if current thread gets its priority decreased, then yield
// (foremost entry in ready_list shall have the highest priority)
if (!list_empty (&ready_list)) {
struct thread *next = list_entry(list_begin(&ready_list), struct thread, elem);
if (next != NULL && next->priority > new_priority) {
thread_yield();
}
}
}
/* Let the thread [target] be donated the priority. */
void
thread_priority_donate(struct thread *target, int new_priority)
{
// donation : change only current priority
target->priority = new_priority;
// if current thread gets its priority decreased, then yield
// (foremost entry in ready_list shall have the highest priority)
if (target == thread_current() && !list_empty (&ready_list)) {
struct thread *next = list_entry(list_begin(&ready_list), struct thread, elem);
if (next != NULL && next->priority > new_priority) {
thread_yield();
}
}
}
/* Returns the current thread's priority. */
int
thread_get_priority (void)
......@@ -470,15 +593,24 @@ init_thread (struct thread *t, const char *name, int priority)
strlcpy (t->name, name, sizeof t->name);
t->stack = (uint8_t *) t + PGSIZE;
t->priority = priority;
t->original_priority = priority;
t->waiting_lock = NULL;
list_init (&t->locks);
t->sleep_endtick = 0;
t->magic = THREAD_MAGIC;
//t->is_kernel = is_kernel;
old_level = intr_disable ();
list_push_back (&all_list, &t->allelem);
intr_set_level (old_level);
}
#ifdef USERPROG
// init process-related informations.
t->pcb = NULL;
list_init(&t->child_list);
list_init(&t->file_descriptors);
t->executing_file = NULL;
#endif
}
/* Allocates a SIZE-byte frame at the top of thread T's stack and
returns a pointer to the frame's base. */
......@@ -589,7 +721,25 @@ allocate_tid (void)
return tid;
}
/* Helper function: implementations */
// A comparator function for thread priority, w.r.t ready_list element.
// returns true iff (thread a)'s priority > (thread b)'s priority.
static bool
comparator_greater_thread_priority (
const struct list_elem *a,
const struct list_elem *b, void *aux UNUSED)
{
struct thread *ta, *tb;
ASSERT (a != NULL);
ASSERT (b != NULL);
ta = list_entry (a, struct thread, elem);
tb = list_entry (b, struct thread, elem);
return ta->priority > tb->priority;
}
/* Offset of `stack' member within `struct thread'.
Used by switch.S, which can't figure it out on its own. */
uint32_t thread_stack_ofs = offsetof (struct thread, stack);
......@@ -88,14 +88,30 @@ struct thread
char name[16]; /* Name (for debugging purposes). */
uint8_t *stack; /* Saved stack pointer. */
int priority; /* Priority. */
int original_priority; /* Priority, before donation */
struct list_elem allelem; /* List element for all threads list. */
struct list_elem waitelem; /* List element, stored in the wait_list queue */
int64_t sleep_endtick; /* The tick after which the thread should awake (if the thread is in sleep) */
/* Shared between thread.c and synch.c. */
struct list_elem elem; /* List element. */
struct list_elem elem; /* List element, stored in the ready_list queue */
// needed for priority donations
struct lock *waiting_lock; /* The lock object on which this thread is waiting (or NULL if not locked) */
struct list locks; /* List of locks the thread holds (for multiple donations) */
#ifdef USERPROG
/* Owned by userprog/process.c. */
uint32_t *pagedir; /* Page directory. */
struct process_control_block *pcb; /* Process Control Block */
struct list child_list; /* List of children processes of this thread,
each elem is defined by pcb#elem */
struct list file_descriptors; /* List of file_descriptors the thread contains */
struct file *executing_file; /* The executable file of associated process. */
#endif
/* Owned by thread.c. */
......@@ -110,7 +126,7 @@ extern bool thread_mlfqs;
void thread_init (void);
void thread_start (void);
void thread_tick (void);
void thread_tick (int64_t tick);
void thread_print_stats (void);
typedef void thread_func (void *aux);
......@@ -119,6 +135,8 @@ tid_t thread_create (const char *name, int priority, thread_func *, void *);
void thread_block (void);
void thread_unblock (struct thread *);
void thread_sleep_until (int64_t wake_tick);
struct thread *thread_current (void);
tid_t thread_tid (void);
const char *thread_name (void);
......@@ -132,6 +150,7 @@ void thread_foreach (thread_action_func *, void *);
int thread_get_priority (void);
void thread_set_priority (int);
void thread_priority_donate(struct thread *, int priority);
int thread_get_nice (void);
void thread_set_nice (int);
......
......@@ -2,6 +2,7 @@
#include <inttypes.h>
#include <stdio.h>
#include "userprog/gdt.h"
#include "userprog/syscall.h"
#include "threads/interrupt.h"
#include "threads/thread.h"
......@@ -89,7 +90,7 @@ kill (struct intr_frame *f)
printf ("%s: dying due to interrupt %#04x (%s).\n",
thread_name (), f->vec_no, intr_name (f->vec_no));
intr_dump_frame (f);
thread_exit ();
sys_exit (-1); // terminate. no more wait, parent
case SEL_KCSEG:
/* Kernel's code segment, which indicates a kernel bug.
......@@ -104,21 +105,10 @@ kill (struct intr_frame *f)
kernel. */
printf ("Interrupt %#04x (%s) in unknown segment %04x\n",
f->vec_no, intr_name (f->vec_no), f->cs);
thread_exit ();
sys_exit (-1); // terminate. no more wait, parent
}
}
/* Page fault handler. This is a skeleton that must be filled in
to implement virtual memory. Some solutions to project 2 may
also require modifying this code.
At entry, the address that faulted is in CR2 (Control Register
2) and information about the fault, formatted as described in
the PF_* macros in exception.h, is in F's error_code member. The
example code here shows how to parse that information. You
can find more information about both of these in the
description of "Interrupt 14--Page Fault Exception (#PF)" in
[IA32-v3a] section 5.15 "Exception and Interrupt Reference". */
static void
page_fault (struct intr_frame *f)
{
......@@ -148,6 +138,14 @@ page_fault (struct intr_frame *f)
write = (f->error_code & PF_W) != 0;
user = (f->error_code & PF_U) != 0;
/* (3.1.5) a page fault in the kernel merely sets eax to 0xffffffff
* and copies its former value into eip */
if(!user) { // kernel mode
f->eip = (void *) f->eax;
f->eax = 0xffffffff;
return;
}
/* To implement virtual memory, delete the rest of the function
body, and replace it with code that brings in the page to
which fault_addr refers. */
......
rm -f build/filesys.dsk
cd build
pintos-mkdisk filesys.dsk --filesys-size=2
pintos --qemu -- -f -q
pintos --qemu -p ../../examples/my -a my -- -q
pintos --qemu -- -q run 'my'
cd ..
#include "userprog/process.h"
#include "userprog/syscall.h"
#include <debug.h>
#include <inttypes.h>
#include <round.h>
......@@ -18,55 +19,153 @@
#include "threads/thread.h"
#include "threads/vaddr.h"
#ifdef DEBUG
#define _DEBUG_PRINTF(...) printf(__VA_ARGS__)
#else
#define _DEBUG_PRINTF(...) /* do nothing */
#endif
static thread_func start_process NO_RETURN;
static bool load (const char *cmdline, void (**eip) (void), void **esp);
static void push_arguments (const char *[], int cnt, void **esp);
/* Starts a new thread running a user program loaded from
FILENAME. The new thread may be scheduled (and may even exit)
`cmdline`. The new thread may be scheduled (and may even exit)
before process_execute() returns. Returns the new process's
thread id, or TID_ERROR if the thread cannot be created. */
tid_t
process_execute (const char *file_name)
pid_t
process_execute (const char *cmdline)
{
char *fn_copy;
char *cmdline_copy = NULL, *file_name = NULL;
char *save_ptr = NULL;
struct process_control_block *pcb = NULL;
tid_t tid;
/* Make a copy of FILE_NAME.
/* Make a copy of CMD_LINE.
Otherwise there's a race between the caller and load(). */
fn_copy = palloc_get_page (0);
if (fn_copy == NULL)
return TID_ERROR;
strlcpy (fn_copy, file_name, PGSIZE);
cmdline_copy = palloc_get_page (0);
if (cmdline_copy == NULL) {
goto execute_failed;
}
strlcpy (cmdline_copy, cmdline, PGSIZE);
// Extract file_name from cmdline. Should make a copy.
file_name = palloc_get_page (0);
if (file_name == NULL) {
goto execute_failed;
}
strlcpy (file_name, cmdline, PGSIZE);
file_name = strtok_r(file_name, " ", &save_ptr);
/* Create a new thread to execute FILE_NAME. */
tid = thread_create (file_name, PRI_DEFAULT, start_process, fn_copy);
if (tid == TID_ERROR)
palloc_free_page (fn_copy);
return tid;
// Create a PCB, along with file_name, and pass it into thread_create
// so that a newly created thread can hold the PCB of process to be executed.
pcb = palloc_get_page(0);
if (pcb == NULL) {
goto execute_failed;
}
// pid is not set yet. Later, in start_process(), it will be determined.
// so we have to postpone afterward actions (such as putting 'pcb'
// alongwith (determined) 'pid' into 'child_list'), using context switching.
pcb->pid = PID_INITIALIZING;
pcb->cmdline = cmdline_copy;
pcb->waiting = false;
pcb->exited = false;
pcb->orphan = false;
pcb->exitcode = -1; // undefined
sema_init(&pcb->sema_initialization, 0);
sema_init(&pcb->sema_wait, 0);
// create thread!
tid = thread_create (file_name, PRI_DEFAULT, start_process, pcb);
if (tid == TID_ERROR) {
goto execute_failed;
}
// wait until initialization inside start_process() is complete.
sema_down(&pcb->sema_initialization);
if(cmdline_copy) {
palloc_free_page (cmdline_copy);
}
// process successfully created, maintain child process list
if(pcb->pid >= 0) {
list_push_back (&(thread_current()->child_list), &(pcb->elem));
}
palloc_free_page (file_name);
return pcb->pid;
execute_failed:
// release allocated memory and return
if(cmdline_copy) palloc_free_page (cmdline_copy);
if(file_name) palloc_free_page (file_name);
if(pcb) palloc_free_page (pcb);
return PID_ERROR;
}
/* A thread function that loads a user process and starts it
running. */
static void
start_process (void *file_name_)
start_process (void *pcb_)
{
char *file_name = file_name_;
struct intr_frame if_;
bool success;
struct thread *t = thread_current();
struct process_control_block *pcb = pcb_;
char *file_name = (char*) pcb->cmdline;
bool success = false;
// cmdline handling
const char **cmdline_tokens = (const char**) palloc_get_page(0);
if (cmdline_tokens == NULL) {
printf("[Error] Kernel Error: Not enough memory\n");
goto finish_step; // pid being -1, release lock, clean resources
}
char* token;
char* save_ptr;
int cnt = 0;
for (token = strtok_r(file_name, " ", &save_ptr); token != NULL;
token = strtok_r(NULL, " ", &save_ptr))
{
cmdline_tokens[cnt++] = token;
}
/* Initialize interrupt frame and load executable. */
struct intr_frame if_;
memset (&if_, 0, sizeof if_);
if_.gs = if_.fs = if_.es = if_.ds = if_.ss = SEL_UDSEG;
if_.cs = SEL_UCSEG;
if_.eflags = FLAG_IF | FLAG_MBS;
success = load (file_name, &if_.eip, &if_.esp);
if (success) {
push_arguments (cmdline_tokens, cnt, &if_.esp);
}
palloc_free_page (cmdline_tokens);
finish_step:
/* Assign PCB */
// we maintain an one-to-one mapping between pid and tid, with identity function.
// pid is determined, so interact with process_execute() for maintaining child_list
pcb->pid = success ? (pid_t)(t->tid) : PID_ERROR;
t->pcb = pcb;
// wake up sleeping in process_execute()
sema_up(&pcb->sema_initialization);
/* If load failed, quit. */
palloc_free_page (file_name);
if (!success)
thread_exit ();
sys_exit (-1);
/* Start the user process by simulating a return from an
interrupt, implemented by intr_exit (in
......@@ -88,14 +187,63 @@ start_process (void *file_name_)
This function will be implemented in problem 2-2. For now, it
does nothing. */
int
process_wait (tid_t child_tid UNUSED)
process_wait (tid_t child_tid)
{
// FIXME: @bgaster --- quick hack to make sure processes execute!
for(;;) ;
struct thread *t = thread_current ();
struct list *child_list = &(t->child_list);
// lookup the process with tid equals 'child_tid' from 'child_list'
struct process_control_block *child_pcb = NULL;
struct list_elem *it = NULL;
if (!list_empty(child_list)) {
for (it = list_front(child_list); it != list_end(child_list); it = list_next(it)) {
struct process_control_block *pcb = list_entry(
it, struct process_control_block, elem);
if(pcb->pid == child_tid) { // OK, the direct child found
child_pcb = pcb;
break;
}
}
}
// if child process is not found, return -1 immediately
if (child_pcb == NULL) {
_DEBUG_PRINTF("[DEBUG] wait(): child not found, pid = %d\n", child_tid);
return -1;
}
if (child_pcb->waiting) {
// already waiting (the parent already called wait on child's pid)
_DEBUG_PRINTF("[DEBUG] wait(): child found, pid = %d, but it is already waiting\n", child_tid);
return -1; // a process may wait for any fixed child at most once
}
else {
child_pcb->waiting = true;
}
// wait(block) until child terminates
// see process_exit() for signaling this semaphore
if (! child_pcb->exited) {
sema_down(& (child_pcb->sema_wait));
}
ASSERT (child_pcb->exited == true);
// remove from child_list
ASSERT (it != NULL);
list_remove (it);
// return the exit code of the child process
int retcode = child_pcb->exitcode;
// Now the pcb object of the child process can be finally freed.
// (in this context, the child process is guaranteed to have been exited)
palloc_free_page(child_pcb);
return retcode;
}
/* Free the current process's resources. */
void
process_exit (void)
......@@ -103,6 +251,48 @@ process_exit (void)
struct thread *cur = thread_current ();
uint32_t *pd;
/* Resources should be cleaned up */
// 1. file descriptors
struct list *fdlist = &cur->file_descriptors;
while (!list_empty(fdlist)) {
struct list_elem *e = list_pop_front (fdlist);
struct file_desc *desc = list_entry(e, struct file_desc, elem);
file_close(desc->file);
palloc_free_page(desc); // see sys_open()
}
// 2. clean up pcb object of all children processes
struct list *child_list = &cur->child_list;
while (!list_empty(child_list)) {
struct list_elem *e = list_pop_front (child_list);
struct process_control_block *pcb;
pcb = list_entry(e, struct process_control_block, elem);
if (pcb->exited == true) {
// pcb can freed when it is already terminated
palloc_free_page (pcb);
} else {
// the child process becomes an orphan.
// do not free pcb yet, postpone until the child terminates
pcb->orphan = true;
}
}
/* Release file for the executable */
if(cur->executing_file) {
file_allow_write(cur->executing_file);
file_close(cur->executing_file);
}
// Unblock the waiting parent process, if any, from wait().
// now its resource (pcb on page, etc.) can be freed.
sema_up (&cur->pcb->sema_wait);
// Destroy the pcb object by itself, if it is orphan.
// see (part 2) of above.
if (cur->pcb->orphan == true) {
palloc_free_page (& cur->pcb);
}
/* Destroy the current process's page directory and switch back
to the kernel-only page directory. */
pd = cur->pagedir;
......@@ -228,7 +418,6 @@ load (const char *file_name, void (**eip) (void), void **esp)
/* Open executable file. */
file = filesys_open (file_name);
if (file == NULL)
{
printf ("load: %s: open failed\n", file_name);
......@@ -314,11 +503,16 @@ load (const char *file_name, void (**eip) (void), void **esp)
/* Start address. */
*eip = (void (*) (void)) ehdr.e_entry;
/* Deny writes to executables. */
file_deny_write (file);
thread_current()->executing_file = file;
success = true;
done:
/* We arrive here whether the load is successful or not. */
file_close (file);
// do not close file here, postpone until it terminates
return success;
}
......@@ -430,6 +624,53 @@ load_segment (struct file *file, off_t ofs, uint8_t *upage,
return true;
}
/*
* Push arguments into the stack region of user program
* (specified by esp), according to the calling convention.
*/
static void
push_arguments (const char* cmdline_tokens[], int argc, void **esp)
{
ASSERT(argc >= 0);
int i, len = 0;
void* argv_addr[argc];
for (i = 0; i < argc; i++) {
len = strlen(cmdline_tokens[i]) + 1;
*esp -= len;
memcpy(*esp, cmdline_tokens[i], len);
argv_addr[i] = *esp;
}
// word align
*esp = (void*)((unsigned int)(*esp) & 0xfffffffc);
// last null
*esp -= 4;
*((uint32_t*) *esp) = 0;
// setting **esp with argvs
for (i = argc - 1; i >= 0; i--) {
*esp -= 4;
*((void**) *esp) = argv_addr[i];
}
// setting **argv (addr of stack, esp)
*esp -= 4;
*((void**) *esp) = (*esp + 4);
// setting argc
*esp -= 4;
*((int*) *esp) = argc;
// setting ret addr
*esp -= 4;
*((int*) *esp) = 0;
}
/* Create a minimal stack by mapping a zeroed page at the top of
user virtual memory. */
static bool
......@@ -442,9 +683,9 @@ setup_stack (void **esp)
if (kpage != NULL)
{
success = install_page (((uint8_t *) PHYS_BASE) - PGSIZE, kpage, true);
if (success) {
if (success)
*esp = PHYS_BASE;
} else
else
palloc_free_page (kpage);
}
return success;
......@@ -469,5 +710,3 @@ install_page (void *upage, void *kpage, bool writable)
return (pagedir_get_page (t->pagedir, upage) == NULL
&& pagedir_set_page (t->pagedir, upage, kpage, writable));
}
//--------------------------------------------------------------------
......@@ -2,10 +2,45 @@
#define USERPROG_PROCESS_H
#include "threads/thread.h"
#include "threads/synch.h"
tid_t process_execute (const char *file_name);
typedef int pid_t;
#define PID_ERROR ((pid_t) -1)
#define PID_INITIALIZING ((pid_t) -2)
pid_t process_execute (const char *cmdline);
int process_wait (tid_t);
void process_exit (void);
void process_activate (void);
/* PCB : see initialization at process_execute(). */
struct process_control_block {
pid_t pid; /* The pid of process */
const char* cmdline; /* The command line of this process being executed */
struct list_elem elem; /* element for thread.child_list */
bool waiting; /* indicates whether parent process is waiting on this. */
bool exited; /* indicates whether the process is done (exited). */
bool orphan; /* indicates whether the parent process has terminated before. */
int32_t exitcode; /* the exit code passed from exit(), when exited = true */
/* Synchronization */
struct semaphore sema_initialization; /* the semaphore used between start_process() and process_execute() */
struct semaphore sema_wait; /* the semaphore used for wait() : parent blocks until child exits */
};
/* File descriptor */
struct file_desc {
int id;
struct list_elem elem;
struct file* file;
};
#endif /* userprog/process.h */
#include "devices/shutdown.h"
#include "devices/input.h"
#include "userprog/syscall.h"
#include "userprog/process.h"
#include "filesys/filesys.h"
#include "filesys/file.h"
#include "threads/palloc.h"
#include <stdio.h>
#include <syscall-nr.h>
#include "threads/interrupt.h"
#include "threads/thread.h"
#include "threads/vaddr.h"
#include "threads/synch.h"
#include "lib/kernel/list.h"
static void syscall_handler (struct intr_frame *);
static void check_user (const uint8_t *uaddr);
static int32_t get_user (const uint8_t *uaddr);
static bool put_user (uint8_t *udst, uint8_t byte);
static int memread_user (void *src, void *des, size_t bytes);
static struct file_desc* find_file_desc(struct thread *, int fd);
unsigned sys_tell(int fd);
struct lock filesys_lock;
void
syscall_init (void)
{
lock_init (&filesys_lock);
intr_register_int (0x30, 3, INTR_ON, syscall_handler, "syscall");
}
static void fail_invalid_access(void) {
if (lock_held_by_current_thread(&filesys_lock))
lock_release (&filesys_lock);
sys_exit (-1);
NOT_REACHED();
}
static void
syscall_handler (struct intr_frame *f UNUSED)
syscall_handler (struct intr_frame *f)
{
printf ("system call!\n");
int syscall_number;
ASSERT( sizeof(syscall_number) == 4 );
memread_user(f->esp, &syscall_number, sizeof(syscall_number));
switch (syscall_number) {
case SYS_TELL:
{
int fd;
unsigned return_code;
memread_user(f->esp + 4, &fd, sizeof(fd));
return_code = sys_tell(fd);
f->eax = (uint32_t) return_code;
break;
}
default:
printf("System call %d is unimplemented!\n", syscall_number);
sys_exit(-1);
break;
}
}
void sys_exit(int status) {
printf("%s: exit(%d)\n", thread_current()->name, status);
struct process_control_block *pcb = thread_current()->pcb;
if(pcb != NULL) {
pcb->exited = true;
pcb->exitcode = status;
}
thread_exit();
}
unsigned sys_tell(int fd) {
lock_acquire (&filesys_lock);
struct file_desc* file_d = find_file_desc(thread_current(), fd);
unsigned ret;
if(file_d && file_d->file) {
ret = file_tell(file_d->file);
}
else
ret = -1;
lock_release (&filesys_lock);
return ret;
}
static void
check_user (const uint8_t *uaddr) {
if(get_user (uaddr) == -1)
fail_invalid_access();
}
static int32_t
get_user (const uint8_t *uaddr) {
if (! ((void*)uaddr < PHYS_BASE)) {
return -1;
}
int result;
asm ("movl $1f, %0; movzbl %1, %0; 1:"
: "=&a" (result) : "m" (*uaddr));
return result;
}
static bool
put_user (uint8_t *udst, uint8_t byte) {
if (! ((void*)udst < PHYS_BASE)) {
return false;
}
int error_code;
asm ("movl $1f, %0; movb %b2, %1; 1:"
: "=&a" (error_code), "=m" (*udst) : "q" (byte));
return error_code != -1;
}
static int
memread_user (void *src, void *dst, size_t bytes)
{
int32_t value;
size_t i;
for(i=0; i<bytes; i++) {
value = get_user(src + i);
if(value == -1)
fail_invalid_access();
*(char*)(dst + i) = value & 0xff;
}
return (int)bytes;
}
static struct file_desc*
find_file_desc(struct thread *t, int fd)
{
ASSERT (t != NULL);
if (fd < 3) {
return NULL;
}
struct list_elem *e;
if (! list_empty(&t->file_descriptors)) {
for(e = list_begin(&t->file_descriptors);
e != list_end(&t->file_descriptors); e = list_next(e))
{
struct file_desc *desc = list_entry(e, struct file_desc, elem);
if(desc->id == fd) {
return desc;
}
}
}
return NULL;
}
......@@ -3,4 +3,6 @@
void syscall_init (void);
#endif /* userprog/syscall.h */
void sys_exit (int);
#endif
......@@ -359,7 +359,7 @@ sub cyl_sectors {
# Makes sure that the loader is a reasonable size.
sub read_loader {
my ($name) = @_;
$name = find_file ("loader.bin") if !defined $name;
$name = find_file ("/home/dev/uwe_os/pintos_student/src/threads/build/loader.bin") if !defined $name;
die "Cannot find loader\n" if !defined $name;
my ($handle);
......
File mode changed from 100755 to 100644
......@@ -256,7 +256,7 @@ sub set_disk {
sub find_disks {
# Find kernel, if we don't already have one.
if (!exists $parts{KERNEL}) {
my $name = find_file ('kernel.bin');
my $name = find_file ('/home/dev/uwe_os/pintos_student/src/threads/build/kernel.bin');
die "Cannot find kernel\n" if !defined $name;
do_set_part ('KERNEL', 'file', $name);
}
......@@ -929,15 +929,15 @@ sub exec_setitimer {
exit (1);
}
sub SIGVTALRM {
#sub SIGVTALRM {
use Config;
my $i = 0;
foreach my $name (split(' ', $Config{sig_name})) {
return $i if $name eq 'VTALRM';
$i++;
}
return 0;
}
# return 0;
#}
# find_in_path ($program)
#
......
#! /bin/sh
# Path to GDB macros file. Customize for your site.
GDBMACROS=/usr/class/cs140/pintos/pintos/src/misc/gdb-macros
GDBMACROS=/home/dev/uwe_os/pintos_student/src/misc/gdb-macros
# Choose correct GDB.
if command -v i386-elf-gdb >/dev/null 2>&1; then
......
File mode changed from 100755 to 100644