Merge branch 'userprog-merge' into 'master'

Implement complete stack initialization, process_wait, and all system calls correctly except exec

See merge request lab2425_autumn/pintos_22!34
This commit is contained in:
Demetriades, Themis
2024-11-11 22:56:28 +00:00
10 changed files with 530 additions and 154 deletions

View File

@@ -4,6 +4,9 @@
#include "filesys/off_t.h" #include "filesys/off_t.h"
#include <stdbool.h> #include <stdbool.h>
/* The maximum length of a file name in PintOS. */
#define FNAME_MAX_LEN 14
struct inode; struct inode;
/* Opening and closing files. */ /* Opening and closing files. */

View File

@@ -170,6 +170,9 @@ list_insert (struct list_elem *before, struct list_elem *elem)
{ {
ASSERT (is_interior (before) || is_tail (before)); ASSERT (is_interior (before) || is_tail (before));
ASSERT (elem != NULL); ASSERT (elem != NULL);
// Sanity checks to prevent (some) loop lists
ASSERT (before != elem);
ASSERT (before->prev != elem);
elem->prev = before->prev; elem->prev = before->prev;
elem->next = before; elem->next = before;

View File

@@ -166,7 +166,7 @@ mkdir (const char *dir)
} }
bool bool
readdir (int fd, char name[READDIR_MAX_LEN + 1]) readdir (int fd, char name[FNAME_MAX_LEN + 1])
{ {
return syscall2 (SYS_READDIR, fd, name); return syscall2 (SYS_READDIR, fd, name);
} }

View File

@@ -3,6 +3,7 @@
#include <stdbool.h> #include <stdbool.h>
#include <debug.h> #include <debug.h>
#include "../../filesys/file.h"
/* Process identifier. */ /* Process identifier. */
typedef int pid_t; typedef int pid_t;
@@ -12,9 +13,6 @@ typedef int pid_t;
typedef int mapid_t; typedef int mapid_t;
#define MAP_FAILED ((mapid_t) -1) #define MAP_FAILED ((mapid_t) -1)
/* Maximum characters in a filename written by readdir(). */
#define READDIR_MAX_LEN 14
/* Typical return values from main() and arguments to exit(). */ /* Typical return values from main() and arguments to exit(). */
#define EXIT_SUCCESS 0 /* Successful execution. */ #define EXIT_SUCCESS 0 /* Successful execution. */
#define EXIT_FAILURE 1 /* Unsuccessful execution. */ #define EXIT_FAILURE 1 /* Unsuccessful execution. */
@@ -41,7 +39,7 @@ void munmap (mapid_t);
/* Task 4 only. */ /* Task 4 only. */
bool chdir (const char *dir); bool chdir (const char *dir);
bool mkdir (const char *dir); bool mkdir (const char *dir);
bool readdir (int fd, char name[READDIR_MAX_LEN + 1]); bool readdir (int fd, char name[FNAME_MAX_LEN + 1]);
bool isdir (int fd); bool isdir (int fd);
int inumber (int fd); int inumber (int fd);

View File

@@ -341,6 +341,7 @@ lock_release (struct lock *lock)
released, transfer the remaining orphaned donors to its donor list. */ released, transfer the remaining orphaned donors to its donor list. */
if (max_donor != NULL) if (max_donor != NULL)
{ {
list_remove (&max_donor->donor_elem);
while (!list_empty (&orphan_list)) while (!list_empty (&orphan_list))
list_push_back (&max_donor->donors_list, list_pop_front (&orphan_list)); list_push_back (&max_donor->donors_list, list_pop_front (&orphan_list));
} }

View File

@@ -1,5 +1,6 @@
#include "threads/thread.h" #include "threads/thread.h"
#include <debug.h> #include <debug.h>
#include <hash.h>
#include <stddef.h> #include <stddef.h>
#include <random.h> #include <random.h>
#include <stdio.h> #include <stdio.h>
@@ -9,12 +10,14 @@
#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"
#include "threads/malloc.h"
#include "threads/palloc.h" #include "threads/palloc.h"
#include "threads/switch.h" #include "threads/switch.h"
#include "threads/synch.h" #include "threads/synch.h"
#include "threads/vaddr.h" #include "threads/vaddr.h"
#ifdef USERPROG #ifdef USERPROG
#include "userprog/process.h" #include "userprog/process.h"
#include "userprog/syscall.h"
#endif #endif
/* Random value for struct thread's `magic' member. /* Random value for struct thread's `magic' member.
@@ -68,6 +71,7 @@ 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_process_result (struct thread *t);
static void init_thread (struct thread *, const char *name, int nice, static void init_thread (struct thread *, const char *name, int nice,
int priority, fp32_t recent_cpu); int priority, fp32_t recent_cpu);
static bool is_thread (struct thread *) UNUSED; static bool is_thread (struct thread *) UNUSED;
@@ -110,6 +114,7 @@ thread_init (void)
initial_thread_recent_cpu); initial_thread_recent_cpu);
initial_thread->status = THREAD_RUNNING; initial_thread->status = THREAD_RUNNING;
initial_thread->tid = allocate_tid (); initial_thread->tid = allocate_tid ();
initial_thread->result = NULL; /* Main thread cannot be waited for. */
} }
/* Starts preemptive thread scheduling by enabling interrupts. /* Starts preemptive thread scheduling by enabling interrupts.
@@ -236,6 +241,11 @@ thread_create (const char *name, int priority,
struct thread *parent_thread = thread_current (); struct thread *parent_thread = thread_current ();
init_thread (t, name, parent_thread->nice, priority, parent_thread->recent_cpu); init_thread (t, name, parent_thread->nice, priority, parent_thread->recent_cpu);
tid = t->tid = allocate_tid (); tid = t->tid = allocate_tid ();
init_process_result (t);
#ifdef USERPROG
hash_init (&t->open_files, fd_hash, fd_less, NULL);
#endif
/* Prepare thread for first run by initializing its stack. /* Prepare thread for first run by initializing its stack.
Do this atomically so intermediate values for the 'stack' Do this atomically so intermediate values for the 'stack'
@@ -259,6 +269,10 @@ thread_create (const char *name, int priority,
intr_set_level (old_level); intr_set_level (old_level);
/* No need to synchronise child_results since it is only ever accessed by one
thread. By the nature of increasing TIDs, this list is ordered. */
list_push_back (&parent_thread->child_results, &t->result->elem);
/* Add to run queue. */ /* Add to run queue. */
thread_unblock (t); thread_unblock (t);
@@ -632,6 +646,18 @@ is_thread (struct thread *t)
return t != NULL && t->magic == THREAD_MAGIC; return t != NULL && t->magic == THREAD_MAGIC;
} }
/* Allocate and initialise a process result for given thread. */
static void
init_process_result (struct thread *t)
{
struct process_result *result = malloc (sizeof (struct process_result));
result->tid = t->tid;
result->exit_status = t->exit_status;
lock_init (&result->lock);
sema_init (&result->sema, 0);
t->result = result;
}
/* 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
@@ -660,6 +686,7 @@ init_thread (struct thread *t, const char *name, int nice, int priority,
t->priority = t->base_priority; t->priority = t->base_priority;
t->exit_status = -1; t->exit_status = -1;
list_init (&t->child_results);
old_level = intr_disable (); old_level = intr_disable ();
list_push_back (&all_list, &t->allelem); list_push_back (&all_list, &t->allelem);

View File

@@ -2,8 +2,11 @@
#define THREADS_THREAD_H #define THREADS_THREAD_H
#include <debug.h> #include <debug.h>
#include <hash.h>
#include <list.h> #include <list.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#include "threads/synch.h"
#include "threads/fixed-point.h" #include "threads/fixed-point.h"
/* States in a thread's life cycle. */ /* States in a thread's life cycle. */
@@ -29,6 +32,18 @@ typedef int tid_t;
#define NICE_DEFAULT 0 /* Default niceness. */ #define NICE_DEFAULT 0 /* Default niceness. */
#define NICE_MAX 20 /* Highest niceness. */ #define NICE_MAX 20 /* Highest niceness. */
/* A process result, synchronised between parent and child. */
struct process_result
{
tid_t tid; /* The tid of the child process. */
int exit_status; /* The exit status of the child process. Initially set
to -1, then to exit_status when child dies. */
struct lock lock; /* Lock the exit_status and sema. */
struct semaphore sema; /* Semaphore to signal the parent that the exit_status
has been set. */
struct list_elem elem; /* List element for the parent's children list. */
};
/* 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
@@ -108,6 +123,12 @@ struct thread
int nice; /* Nice value for this thread */ int nice; /* Nice value for this thread */
fp32_t recent_cpu; /* Amount of time this process received */ fp32_t recent_cpu; /* Amount of time this process received */
/* Process wait properties. */
struct process_result *result; /* Result of the process. */
struct list child_results; /* List of children's of this thread
process results. */
struct file *exec_file; /* Thread's currently running file */
/* 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. */
@@ -116,6 +137,7 @@ struct thread
#ifdef USERPROG #ifdef USERPROG
/* Owned by userprog/process.c. */ /* Owned by userprog/process.c. */
uint32_t *pagedir; /* Page directory. */ uint32_t *pagedir; /* Page directory. */
struct hash open_files; /* Hash Table of FD -> Struct File */
#endif #endif
/* Owned by thread.c. */ /* Owned by thread.c. */

View File

@@ -1,6 +1,7 @@
#include "userprog/process.h" #include "userprog/process.h"
#include <debug.h> #include <debug.h>
#include <inttypes.h> #include <inttypes.h>
#include <list.h>
#include <round.h> #include <round.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@@ -22,6 +23,10 @@
#include "threads/synch.h" #include "threads/synch.h"
#include "devices/timer.h" #include "devices/timer.h"
/* Defines the native number of bytes processed by the processor
(for the purposes of alignment). */
#define WORD_SIZE 4
/* Keeps track of the position of pointers to user program arguments /* Keeps track of the position of pointers to user program arguments
within a linked list. */ within a linked list. */
struct arg_elem struct arg_elem
@@ -30,155 +35,111 @@ struct arg_elem
struct list_elem elem; struct list_elem elem;
}; };
/* Holds the data required to be passed from a kernel thread to a thread
that executes process_start for the purpose of starting a user process. */
struct process_start_data
{
char *cmd; /* Pointer to a copy of the command used to execute the process.
Allocated a page that must be freed by process_start. */
char *cmd_saveptr; /* Value pointed to by 'saveptr' argument used by
successive calls to strtok_r to split 'cmd' into
tokens while maintaining state. */
char file_name[FNAME_MAX_LEN + 1]; /* Name of the file of the process to
be started. */
};
static thread_func start_process NO_RETURN; static thread_func start_process NO_RETURN;
static bool load (const char *cmdline, void (**eip) (void), void **esp); static bool load (const char *cmdline, void (**eip) (void), void **esp);
/* Starts a new thread running a user program loaded from /* Starts a new thread running a user program executed via
FILENAME. The new thread may be scheduled (and may even exit) CMD. The new thread may be scheduled (and may even exit)
before process_execute() returns. Returns the new process's before process_execute() returns. Returns the new process's
thread id, or TID_ERROR if the thread cannot be created. */ thread id, or TID_ERROR if the thread cannot be created. */
tid_t tid_t
process_execute (const char *file_name) process_execute (const char *cmd)
{ {
char *fn_copy; char *cmd_copy;
tid_t tid; tid_t tid;
/* Make a copy of FILE_NAME. struct process_start_data *data = malloc (sizeof (struct process_start_data));
if (data == NULL)
{
return TID_ERROR;
}
/* Make a copy of command.
Otherwise there's a race between the caller and load(). */ Otherwise there's a race between the caller and load(). */
fn_copy = palloc_get_page (0); cmd_copy = palloc_get_page (0);
if (fn_copy == NULL) if (cmd_copy == NULL)
return TID_ERROR; return TID_ERROR;
/* Imposing implicit limit that the command line arguments /* Imposing implicit limit that the command line arguments
including the user program name fit within a single page. */ including the user program name fit within a single page. */
strlcpy (fn_copy, file_name, PGSIZE); strlcpy (cmd_copy, cmd, PGSIZE);
/* Create a new thread to execute FILE_NAME. */ /* Retrieve first argument of command, which is the file name
tid = thread_create (file_name, PRI_DEFAULT, start_process, fn_copy); of the process. */
char *file_name = strtok_r (cmd_copy, " ", &data->cmd_saveptr);
/* Create a new thread to execute the command, by initializing
it running the function 'start_process' with the appropriate
arguments. For details of arguments, see 'start_process'. */
data->cmd = cmd_copy;
strlcpy (data->file_name, file_name, FNAME_MAX_LEN + 1);
tid = thread_create (file_name, PRI_DEFAULT, start_process, data);
if (tid == TID_ERROR) if (tid == TID_ERROR)
palloc_free_page (fn_copy); palloc_free_page (cmd_copy);
return tid; return tid;
} }
static void * static bool install_page (void *upage, void *kpage, bool writable);
push_to_stack (void **esp, void *data, size_t data_size) static bool process_init_stack (char *cmd_saveptr, void **esp, char *file_name);
{ static void *push_to_stack (void **esp, void *data, size_t data_size);
*esp -= data_size; #define push_var_to_stack(esp, var) (push_to_stack (esp, &var, sizeof (var)))
memcpy (*esp, data, data_size);
return *esp;
}
/* A thread function that loads a user process and starts it /* Make the current thread execute 'cmd', passing in a copy of the
running. */ command string used for processing, the saveptr used by strtok_r
(in order to further tokenize the same command and retrieve its
arguments), as well as the name of the file being executed. This
involves loading the specified file and starting it running. */
static void static void
start_process (void *file_name_) start_process (void *proc_start_data)
{ {
struct intr_frame if_; struct intr_frame if_;
bool success; bool success;
/* Retrieve first argument of command, which is the file name struct process_start_data *data = proc_start_data;
of the process. */
char *saveptr;
char *arg = strtok_r (file_name_, " ", &saveptr);
char file_name[15];
strlcpy (file_name, arg, 15);
/* TODO: Move naming of thread to process_execute, so start
tokenizing there. */
strlcpy (thread_current ()->name, file_name, 15);
/* Initialize interrupt frame and load executable. */ /* Initialize interrupt frame and load executable. */
memset (&if_, 0, sizeof if_); memset (&if_, 0, sizeof if_);
if_.gs = if_.fs = if_.es = if_.ds = if_.ss = SEL_UDSEG; if_.gs = if_.fs = if_.es = if_.ds = if_.ss = SEL_UDSEG;
if_.cs = SEL_UCSEG; if_.cs = SEL_UCSEG;
if_.eflags = FLAG_IF | FLAG_MBS; if_.eflags = FLAG_IF | FLAG_MBS;
success = load (file_name, &if_.eip, &if_.esp); success = load (data->file_name, &if_.eip, &if_.esp);
/* Load command line argument *data* to user process stack.
This can't cause overflow due to enforcing that the size of
command line input must fit in a page. Also keep track
of pointers to the argument data within a linked list. */
struct list arg_list;
list_init (&arg_list);
int arg_count = 0;
while (arg != NULL)
{
push_to_stack (&if_.esp, arg, (strlen (arg) + 1) * sizeof (char));
struct arg_elem *arg_elem = malloc (sizeof (struct arg_elem));
if (arg_elem == NULL)
{
printf("ERROR: Couldn't allocate argument pointer memory for %s!\n",
file_name);
palloc_free_page (file_name_);
if (success) process_exit ();
thread_exit ();
}
arg_elem->arg = if_.esp;
list_push_front (&arg_list, &arg_elem->elem);
arg_count++;
arg = strtok_r (NULL, " ", &saveptr);
}
/* Calculate the remaining number of bytes that need to be written
to the user process stack in order to check for possible overflow. */
size_t align_size = ((unsigned int) if_.esp % 4) * sizeof (uint8_t);
size_t argv_data_size = (arg_count + 1) * sizeof (char *);
size_t argv_size = sizeof (char **);
size_t argc_size = sizeof (int);
size_t return_addr_size = sizeof (void *);
size_t remaining_size = align_size + argv_data_size + argv_size + argc_size
+ return_addr_size;
/* If pushing the rest of the data required for the stack would cause
overflow, allocate an extra page. */
if (PHYS_BASE - if_.esp + remaining_size > PGSIZE)
{
/* TODO: Allocate an extra page for the rest of the process stack. */
}
/* Align stack pointer to word size before pushing argv elements for
performance. */
if_.esp -= align_size;
/* Push a null pointer sentinel inside argv. */
if_.esp -= sizeof (char *);
*(char *) if_.esp = 0;
/* Push pointer to the process file name to the stack. */
char **argv;
/* Push pointers to process arguments from argument linked list */
struct list_elem *e = list_begin (&arg_list);
struct list_elem *tail = list_tail (&arg_list);
while (e != tail)
{
struct arg_elem *arg_elem = list_entry (e, struct arg_elem, elem);
argv = push_to_stack (&if_.esp, &arg_elem->arg, sizeof (arg_elem->arg));
e = list_next (e);
free (arg_elem);
}
/* Push pointer to the start of argv array. */
push_to_stack (&if_.esp, &argv, sizeof(argv));
/* Push the number of arguments to the stack. */
push_to_stack (&if_.esp, &arg_count, sizeof (arg_count));
/* Push fake return address (null pointer). */
if_.esp -= sizeof (char *);
*(char *) if_.esp = 0;
/* If load failed, quit. */ /* If load failed, quit. */
palloc_free_page (file_name_);
if (!success) if (!success)
thread_exit (); {
palloc_free_page (data->cmd);
goto fail;
}
/* Initialize user process stack and free page used to store the
command that executed the process. */
success = process_init_stack (data->cmd_saveptr, &if_.esp, data->file_name);
palloc_free_page (data->cmd);
/* If stack initialization failed, free resources and quit. */
if (!success)
{
process_exit ();
goto fail;
}
struct file *exec_file = filesys_open (file_name);
thread_current ()->exec_file = exec_file;
file_deny_write (exec_file);
/* Start the user process by simulating a return from an /* Start the user process by simulating a return from an
interrupt, implemented by intr_exit (in interrupt, implemented by intr_exit (in
@@ -188,6 +149,109 @@ start_process (void *file_name_)
and jump to it. */ and jump to it. */
asm volatile ("movl %0, %%esp; jmp intr_exit" : : "g" (&if_) : "memory"); asm volatile ("movl %0, %%esp; jmp intr_exit" : : "g" (&if_) : "memory");
NOT_REACHED (); NOT_REACHED ();
/* If starting the process failed, free its common resources and exit. */
fail:
free (data);
thread_exit ();
}
/* Helper function that initializes the stack of a newly created
user process. Returns true if successful, false otherwise. */
static bool
process_init_stack (char *cmd_saveptr, void **esp, char *file_name)
{
/* Load command line argument *data* to user process stack.
This can't cause overflow due to enforcing that the size of
command line input must fit in a page. Also keep track
of pointers to the argument data within a linked list. */
struct list arg_list;
list_init (&arg_list);
char *arg = file_name;
int arg_count = 0;
while (arg != NULL)
{
push_to_stack (esp, arg, (strlen (arg) + 1) * sizeof (char));
struct arg_elem *arg_elem = malloc (sizeof (struct arg_elem));
if (arg_elem == NULL)
{
printf("ERROR: Couldn't allocate argument pointer memory for %s!\n",
thread_current ()->name);
return false;
}
arg_elem->arg = *esp;
list_push_front (&arg_list, &arg_elem->elem);
arg_count++;
arg = strtok_r (NULL, " ", &cmd_saveptr);
}
/* Calculate the remaining number of bytes that need to be written
to the user process stack in order to check for possible overflow. */
size_t align_size = ((unsigned int) *esp % WORD_SIZE) * sizeof (uint8_t);
size_t argv_data_size = (arg_count + 1) * sizeof (char *);
size_t argv_size = sizeof (char **);
size_t argc_size = sizeof (int);
size_t return_addr_size = sizeof (void *);
size_t remaining_size = align_size + argv_data_size + argv_size + argc_size
+ return_addr_size;
/* If pushing the rest of the data required for the stack would cause
overflow, allocate an extra page that is contiguous within the
virtual address space (below the current address range). */
if (PHYS_BASE - *esp + remaining_size > PGSIZE)
{
uint8_t *kpage = palloc_get_page (PAL_USER | PAL_ZERO);
if (!install_page (((uint8_t *) PHYS_BASE) - PGSIZE * 2, kpage, true))
return false;
}
/* Align stack pointer to word size before pushing argv elements for
performance. */
*esp -= align_size;
/* Push a null pointer sentinel inside argv. */
char *null_sentinel = NULL;
push_var_to_stack (esp, null_sentinel);
/* Push pointers to process arguments from argument linked list */
struct list_elem *e = list_begin (&arg_list);
struct list_elem *tail = list_tail (&arg_list);
while (e != tail)
{
struct arg_elem *arg_elem = list_entry (e, struct arg_elem, elem);
push_var_to_stack(esp, arg_elem->arg);
e = list_next (e);
free (arg_elem);
}
/* Push pointer to the start of argv array. */
char **argv = *esp;
push_var_to_stack(esp, argv);
/* Push the number of arguments to the stack. */
push_var_to_stack(esp, arg_count);
/* Push fake return address (null pointer). */
push_var_to_stack (esp, null_sentinel);
return true;
}
/* Helper function that pushes the first 'data_size' bytes stored
in the address '*data' into the stack given a pointer to the
stack pointer '**esp'. */
static void *
push_to_stack (void **esp, void *data, size_t data_size)
{
*esp -= data_size;
memcpy (*esp, data, data_size);
return *esp;
} }
/* Waits for thread TID to die and returns its exit status. /* Waits for thread TID to die and returns its exit status.
@@ -202,12 +266,39 @@ start_process (void *file_name_)
int int
process_wait (tid_t child_tid UNUSED) process_wait (tid_t child_tid UNUSED)
{ {
/* As a temporary wait, waiting will just put the thread to sleep for one struct process_result *child_result = NULL;
second (TIMER_FREQ = 100 ticks ~ 1 second). */ struct list_elem *e;
/* TODO: Implement process_wait () correctly. Remove the next line. */ struct thread *cur = thread_current ();
timer_sleep (TIMER_FREQ); for (e = list_begin (&cur->child_results);
e != list_end (&cur->child_results); e = list_next (e))
return 0; /* TODO: Change this too */ {
struct process_result *result
= list_entry (e, struct process_result, elem);
if (result->tid == child_tid)
{
child_result = result;
break;
}
/* List is ordered, allowing us to break early. */
else if (result->tid > child_tid)
break;
}
if (child_result == NULL)
return -1;
/* Wait for child to die. */
sema_down (&child_result->sema);
/* We need lock release in process_exit, so we need to acquire (and possibly
wait) for it here to ensure we don't free the lock memory before it is
released in process_exit. */
lock_acquire (&child_result->lock);
/* To prevent waiting for child twice, remove it from the list.
No need to use lock since this is the only thread with access to
the struct process_result now. */
list_remove (&child_result->elem);
int exit_status = child_result->exit_status;
lock_release (&child_result->lock);
free (child_result);
return exit_status;
} }
/* Free the current process's resources. */ /* Free the current process's resources. */
@@ -218,6 +309,49 @@ process_exit (void)
uint32_t *pd; uint32_t *pd;
printf ("%s: exit(%d)\n", cur->name, cur->exit_status); printf ("%s: exit(%d)\n", cur->name, cur->exit_status);
file_close (cur->exec_file);
/* Update process result. */
if (cur->result != NULL)
{
lock_acquire (&cur->result->lock);
cur->result->exit_status = cur->exit_status;
/* Parent has died, child has to free the struct process_result * */
if (sema_try_down (&cur->result->sema))
{
lock_release (&cur->result->lock);
free (cur->result);
}
/* Parent is still alive and will be the one to free the
struct process_result *, and may be waiting so call sema_up */
else
{
sema_up (&cur->result->sema);
lock_release (&cur->result->lock);
}
}
/* Free child process results or signal parent's death. */
struct list_elem *e;
for (e = list_begin (&cur->child_results);
e != list_end (&cur->child_results); e = list_next (e))
{
struct process_result *result
= list_entry (e, struct process_result, elem);
lock_acquire (&result->lock);
/* Child has died (and was not waited for). Free the result. */
if (sema_try_down (&result->sema))
{
lock_release (&result->lock);
free (result);
}
/* Child is still alive, signal via sema that parent has died. */
else
{
sema_up (&result->sema);
lock_release (&result->lock);
}
}
/* Destroy the current process's page directory and switch back /* Destroy the current process's page directory and switch back
to the kernel-only page directory. */ to the kernel-only page directory. */
@@ -439,8 +573,6 @@ load (const char *file_name, void (**eip) (void), void **esp)
/* load() helpers. */ /* load() helpers. */
static bool install_page (void *upage, void *kpage, bool writable);
/* Checks whether PHDR describes a valid, loadable segment in /* Checks whether PHDR describes a valid, loadable segment in
FILE and returns true if so, false otherwise. */ FILE and returns true if so, false otherwise. */
static bool static bool

View File

@@ -1,13 +1,28 @@
#include "userprog/syscall.h" #include "userprog/syscall.h"
#include "devices/shutdown.h" #include "devices/shutdown.h"
#include "devices/input.h" #include "devices/input.h"
#include "filesys/file.h"
#include "filesys/filesys.h"
#include "threads/vaddr.h" #include "threads/vaddr.h"
#include "threads/interrupt.h" #include "threads/interrupt.h"
#include "threads/malloc.h"
#include "threads/thread.h" #include "threads/thread.h"
#include "threads/synch.h"
#include "userprog/process.h" #include "userprog/process.h"
#include "userprog/pagedir.h"
#include <stdio.h> #include <stdio.h>
#include <syscall-nr.h> #include <syscall-nr.h>
static struct lock filesys_lock;
static unsigned fd_counter = MIN_USER_FD;
struct open_file
{
int fd; /* File Descriptor / Identifier */
struct file *file; /* Pointer to the associated file */
struct hash_elem elem; /* elem for a hash table */
};
static void syscall_handler (struct intr_frame *); static void syscall_handler (struct intr_frame *);
/* A syscall_function is a function that receives up to 3 arguments, the /* A syscall_function is a function that receives up to 3 arguments, the
@@ -30,6 +45,7 @@ static void syscall_seek (int fd, unsigned position);
static unsigned syscall_tell (int fd); static unsigned syscall_tell (int fd);
static void syscall_close (int fd); static void syscall_close (int fd);
static struct open_file *fd_get_file (int fd);
static void *validate_user_pointer (const void *ptr, size_t size); static void *validate_user_pointer (const void *ptr, size_t size);
/* A struct defining a syscall_function pointer along with its arity. */ /* A struct defining a syscall_function pointer along with its arity. */
@@ -63,12 +79,19 @@ static const syscall_arguments syscall_lookup[] =
static const int LOOKUP_SIZE static const int LOOKUP_SIZE
= sizeof (syscall_lookup) / sizeof (syscall_arguments); = sizeof (syscall_lookup) / sizeof (syscall_arguments);
/* Initialises the syscall handling system, as well as a global lock to
synchronise all file access between processes. */
void void
syscall_init (void) syscall_init (void)
{ {
intr_register_int (0x30, 3, INTR_ON, syscall_handler, "syscall"); intr_register_int (0x30, 3, INTR_ON, syscall_handler, "syscall");
lock_init (&filesys_lock);
} }
/* Function that takes a interrupt frame containing a syscall and its args.
Validates the arguments and pointers before calling the relevant
high-level system call function, storing its output (if any) in f->eax */
static void static void
syscall_handler (struct intr_frame *f) syscall_handler (struct intr_frame *f)
{ {
@@ -94,6 +117,8 @@ syscall_handler (struct intr_frame *f)
f->eax = syscall.function (args[0], args[1], args[2]); f->eax = syscall.function (args[0], args[1], args[2]);
} }
/* Called upon a "halt" syscall, resulting in a complete shutdown of the
process, via shutdown_power_off (); */
static void static void
syscall_halt (void) syscall_halt (void)
{ {
@@ -109,53 +134,120 @@ syscall_exit (int status)
thread_exit (); thread_exit ();
} }
/* Executes a given command with the relevant args, by calling process_execute.
Acquires the filesystem lock as process_execute accesses the file system.
Returns PID for the process that is running the CMD_LINE
*/
static pid_t static pid_t
syscall_exec (const char *cmd_line UNUSED) syscall_exec (const char *cmd_line)
{ {
//TODO validate_user_pointer (cmd_line, 1);
return 0;
lock_acquire (&filesys_lock);
pid_t pid = process_execute(cmd_line);
lock_release (&filesys_lock);
return pid;
} }
/* Handles the syscall of wait. Effectively a wrapper for process_wait as the
necessary validation and such all happens in process_wait anyway. */
static int static int
syscall_wait (pid_t pid) syscall_wait (pid_t pid)
{ {
return process_wait (pid); return process_wait (pid);
} }
/* Handles the syscall for file creation. First validates the user file
pointer. Acquires the file system lock to prevent synchronisation issues,
and then uses FILESYS_CREATE to create the file, returning the same status */
static bool static bool
syscall_create (const char *file UNUSED, unsigned initial_size UNUSED) syscall_create (const char *file UNUSED, unsigned initial_size UNUSED)
{ {
//TODO validate_user_pointer (file, 1);
return 0;
lock_acquire (&filesys_lock);
bool status = filesys_create (file, initial_size);
lock_release (&filesys_lock);
return status;
} }
/* Handles the syscall for file removal. First validates the user file pointer.
Acquires the file system lock to prevent synchronisation issues, and then
uses FILESYS_REMOVE to remove the file, returning the same success status */
static bool static bool
syscall_remove (const char *file UNUSED) syscall_remove (const char *file)
{ {
//TODO validate_user_pointer (file, 1);
return 0;
lock_acquire (&filesys_lock);
bool status = filesys_remove (file);
lock_release (&filesys_lock);
return status;
} }
/* Handles the syscall for opening a file connection. First, validates the file
pointer. Then it acquires a lock for the file system, in order to open the
connection without synchronisation issues. It then maps a new fd to this file
in the hash table before returning the fd. */
static int static int
syscall_open (const char *file UNUSED) syscall_open (const char *file)
{ {
//TODO validate_user_pointer (file, 1);
return 0;
lock_acquire (&filesys_lock);
struct file *ptr = filesys_open (file);
lock_release (&filesys_lock);
if (ptr == NULL)
return -1;
/* Allocate space for a struct representing a mapping from an FD to a struct
file. */
struct open_file *file_info
= (struct open_file*) malloc (sizeof (struct open_file));
if (file_info == NULL)
return -1;
/* Populate the above struct, with a unique FD and the current open file */
file_info->fd = fd_counter++;
file_info->file = ptr;
/* Add the new FD->file mapping to the hashtable for the current thread */
hash_insert (&thread_current ()->open_files, &file_info->elem);
/* Return the new FD */
return file_info->fd;
} }
/* Handles the syscall for getting a file's size. Converts a provided FD into
the asssociated file struct. Acquire the lock for the filesystem and use
FILE_LENGTH to calculate the length for return. */
static int static int
syscall_filesize (int fd UNUSED) syscall_filesize (int fd)
{ {
//TODO struct open_file *file_info = fd_get_file (fd);
return 0; if (file_info == NULL)
return -1;
lock_acquire (&filesys_lock);
int bytes = file_length (file_info->file);
lock_release (&filesys_lock);
return bytes;
} }
/* Handles the syscall for reading SIZE bytes from a file referenced by FD.
If the FD references the console, use input_getc (), otherwise convert the
FD to its associated file struct, acquire the filesystem lock, read up to
SIZE bytes and then return the number of bytes read.*/
static int static int
syscall_read (int fd, void *buffer, unsigned size) syscall_read (int fd, void *buffer, unsigned size)
{ {
/* Only console (fd = 0) or other files, not including STDOUT, (fd > 1) are /* Only console (fd = 0) or other files, not including STDOUT, (fd > 1) are
allowed. */ allowed. */
if (fd < 0 && fd != STDOUT_FILENO) if (fd < 0 || fd == STDOUT_FILENO)
return -1; return -1;
validate_user_pointer (buffer, size); validate_user_pointer (buffer, size);
@@ -172,10 +264,21 @@ syscall_read (int fd, void *buffer, unsigned size)
else else
{ {
/* Reading from a file. */ /* Reading from a file. */
return 0; // TODO: Implement Write to Files struct open_file *file_info = fd_get_file (fd);
if (file_info == NULL)
return -1;
lock_acquire (&filesys_lock);
int bytes_written = file_read (file_info->file, buffer, size);
lock_release (&filesys_lock);
return bytes_written;
} }
} }
/* Handles the syscall for writing SIZE bytes to a file referenced by FD.
If the FD references the console, use put_buf (), otherwise convert the
FD to its associated file struct, acquire the filesystem lock, write up to
SIZE bytes and then return the number of bytes written.*/
static int static int
syscall_write (int fd, const void *buffer, unsigned size) syscall_write (int fd, const void *buffer, unsigned size)
{ {
@@ -195,27 +298,106 @@ syscall_write (int fd, const void *buffer, unsigned size)
else else
{ {
/* Writing to a file. */ /* Writing to a file. */
return 0; // TODO: Implement Write to Files struct open_file *file_info = fd_get_file (fd);
if (file_info == NULL)
return 0;
lock_acquire (&filesys_lock);
int bytes = file_write (file_info->file, buffer, size);
lock_release (&filesys_lock);
return bytes;
} }
} }
/* Handles the syscall for seeking to POSITION bytes in a file referenced by
FD. Converts the FD to its associated file struct, acquires the filesystem
lock and then uses file_seek to adjust the cursor to a specific position in
the file.*/
static void static void
syscall_seek (int fd UNUSED, unsigned position UNUSED) syscall_seek (int fd, unsigned position)
{ {
//TODO struct open_file *file_info = fd_get_file (fd);
if (file_info != NULL)
{
lock_acquire (&filesys_lock);
file_seek (file_info->file, position);
lock_release (&filesys_lock);
}
} }
/* Handles the syscall for returning the next byte in a file referenced by
FD. Converts the FD to its associated file struct, acquires the filesystem
lock and then uses file_tell to read the next byte.*/
static unsigned static unsigned
syscall_tell (int fd UNUSED) syscall_tell (int fd)
{ {
//TODO struct open_file *file_info = fd_get_file (fd);
return 0; if (file_info == NULL)
return 0;
lock_acquire (&filesys_lock);
unsigned pos = file_tell (file_info->file);
lock_release (&filesys_lock);
return pos;
} }
/* Handles the syscall for closing a connection to a file. Converts the FD to
its associated file struct. If it exists, it removes it from the hash table,
acquires the filesystem lock, and uses file_close to close the connection.*/
static void static void
syscall_close (int fd UNUSED) syscall_close (int fd)
{ {
//TODO struct open_file *file_info = fd_get_file (fd);
if (file_info != NULL)
{
hash_delete (&thread_current ()->open_files, &file_info->elem);
lock_acquire (&filesys_lock);
file_close (file_info->file);
lock_release (&filesys_lock);
free (file_info);
}
}
/* Hashing function needed for the open_file table. Returns a hash for an entry,
based on its FD. */
unsigned
fd_hash (const struct hash_elem *element, void *aux UNUSED)
{
return hash_int (hash_entry (element, struct open_file, elem)->fd);
}
/* Comparator function for the open_file table. Compares two entries based on
the FDs. */
bool
fd_less (const struct hash_elem *a_, const struct hash_elem *b_,
void *aux UNUSED)
{
struct open_file *a = hash_entry (a_, struct open_file, elem);
struct open_file *b = hash_entry (b_, struct open_file, elem);
return a->fd < b->fd;
}
/* Gets a file from its descriptor (FD number). If there is no file with the fd
FD it returns NULL. */
static struct open_file *
fd_get_file (int fd)
{
/* We have to set up a fake open_file in order to be able to search the hash
table. See hash.h. */
struct open_file fake_file_info;
fake_file_info.fd = fd;
struct hash_elem *e
= hash_find (&thread_current ()->open_files, &fake_file_info.elem);
if (e == NULL)
return NULL;
return hash_entry (e, struct open_file, elem);
} }
/* Validates if a block of memory starting at PTR and of size SIZE bytes is /* Validates if a block of memory starting at PTR and of size SIZE bytes is
@@ -227,8 +409,9 @@ validate_user_pointer (const void *ptr, size_t size)
{ {
if (size > 0 && (ptr == NULL || if (size > 0 && (ptr == NULL ||
!is_user_vaddr (ptr) || !is_user_vaddr (ptr) ||
!is_user_vaddr (ptr + size - 1))) !is_user_vaddr (ptr + size - 1) ||
pagedir_get_page (thread_current()->pagedir, ptr) == NULL))
thread_exit (); thread_exit ();
return ptr; return (void *) ptr;
} }

View File

@@ -1,8 +1,15 @@
#ifndef USERPROG_SYSCALL_H #ifndef USERPROG_SYSCALL_H
#define USERPROG_SYSCALL_H #define USERPROG_SYSCALL_H
#include <hash.h>
#define MIN_USER_FD 2
typedef int pid_t; typedef int pid_t;
void syscall_init (void); void syscall_init (void);
unsigned fd_hash (const struct hash_elem *element, void *aux);
bool fd_less (const struct hash_elem *a, const struct hash_elem *b, void *aux);
#endif /* userprog/syscall.h */ #endif /* userprog/syscall.h */