diff options
Diffstat (limited to 'src/userprog')
| -rw-r--r-- | src/userprog/Make.vars | 2 | ||||
| -rw-r--r-- | src/userprog/process.c | 177 | ||||
| -rwxr-xr-x[-rw-r--r--] | src/userprog/start_pfs.sh | 4 | ||||
| -rwxr-xr-x[-rw-r--r--] | src/userprog/start_recursor.sh | 10 | ||||
| -rw-r--r-- | src/userprog/syscall.c | 334 |
5 files changed, 501 insertions, 26 deletions
diff --git a/src/userprog/Make.vars b/src/userprog/Make.vars index eebac65..22aec79 100644 --- a/src/userprog/Make.vars +++ b/src/userprog/Make.vars @@ -2,6 +2,6 @@ os.dsk: DEFINES = -DUSERPROG -DFILESYS KERNEL_SUBDIRS = threads devices lib lib/kernel userprog filesys -TEST_SUBDIRS = tests/userprog #tests/userprog/no-vm tests/filesys/base +TEST_SUBDIRS = tests/userprog tests/filesys/base GRADING_FILE = $(SRCDIR)/tests/userprog/Grading SIMULATOR = --qemu diff --git a/src/userprog/process.c b/src/userprog/process.c index b3e16bb..6170b53 100644 --- a/src/userprog/process.c +++ b/src/userprog/process.c @@ -14,10 +14,24 @@ #include "threads/flags.h" #include "threads/init.h" #include "threads/interrupt.h" +#include "threads/malloc.h" #include "threads/palloc.h" +#include "threads/synch.h" #include "threads/thread.h" #include "threads/vaddr.h" +struct start_process_args + { + struct semaphore sema; + + // parent -> child + char *file_name; + + // child -> parent + bool success; + struct parent_child *child; + }; + static thread_func start_process NO_RETURN; static bool load (const char *cmdline, void (**eip) (void), void **esp); @@ -28,42 +42,75 @@ static bool load (const char *cmdline, void (**eip) (void), void **esp); tid_t process_execute (const char *file_name) { - char *fn_copy; + struct start_process_args args; // stack allocated since we know we wait for thread_create to finish reading tid_t tid; + sema_init (&args.sema, 0); + /* Make a copy of FILE_NAME. Otherwise there's a race between the caller and load(). */ - fn_copy = palloc_get_page (0); - if (fn_copy == NULL) + args.file_name = palloc_get_page (0); + if (args.file_name == NULL) return TID_ERROR; - strlcpy (fn_copy, file_name, PGSIZE); - - /* 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); + strlcpy (args.file_name, file_name, PGSIZE); + + /* Create a new thread to execute FILE_NAME. + If thread_create fails we free and return immediately since we know + that the thread didn't start. + Otherwise, the child thread can be scheduled freely so we explicitly + wait for the child to signal that it has read from and written to + the args. Only then can we free resources and return the tid. */ + tid = thread_create (file_name, PRI_DEFAULT, start_process, &args); + if (tid != TID_ERROR) { + sema_down (&args.sema); + + if (args.success) { + list_push_back (&thread_current ()->children, &args.child->elem); + } else { + tid = -1; + } + } + palloc_free_page (args.file_name); return tid; } /* A thread function that loads a user process and starts it running. */ static void -start_process (void *file_name_) +start_process (void *args_) { - char *file_name = file_name_; + struct start_process_args *args = args_; struct intr_frame if_; + struct thread *t; bool success; + t = thread_current (); + /* Initialize interrupt frame and load executable. */ 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); + success = load (args->file_name, &if_.eip, &if_.esp); + args->success = success; + t->load_success = success; + + if (success) { + t->parent = malloc (sizeof (struct parent_child)); + + sema_init (&t->parent->exit_sema, 0); + lock_init (&t->parent->l); + + t->parent->child_tid = t->tid; + t->parent->alive_count = 2; + + args->child = t->parent; + } + + sema_up (&args->sema); /* If load failed, quit. */ - palloc_free_page (file_name); - if (!success) + if (!success) thread_exit (); /* Start the user process by simulating a return from an @@ -86,11 +133,39 @@ 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) { + struct thread *t = thread_current (); + struct list_elem *e; + for (e = list_begin (&t->children); e != list_end (&t->children); + e = list_next (e)) + { + struct parent_child *pc = list_entry (e, struct parent_child, elem); + if (child_tid == pc->child_tid) { + sema_down (&pc->exit_sema); + int exit_status = pc->exit_status; + pc->exit_status = -1; + sema_up (&pc->exit_sema); // a bit of a hack + // the child is killed so we can read again if we want to + return exit_status; + } + } return -1; } +static void +free_pc (struct parent_child *pc) +{ + lock_acquire (&pc->l); + + pc->alive_count--; + if (pc->alive_count == 0) { + free (pc); + } else { + lock_release (&pc->l); + } +} + /* Free the current process's resources. */ void process_exit (void) @@ -98,6 +173,20 @@ process_exit (void) struct thread *cur = thread_current (); uint32_t *pd; + if (cur->load_success) { + sema_up (&cur->parent->exit_sema); + free_pc (cur->parent); + printf("%s: exit(%d)\n", cur->name, cur->parent->exit_status); + + struct list_elem *e; + struct parent_child *child; + while (!list_empty (&cur->children)) { + e = list_pop_front (&cur->children); + child = list_entry (e, struct parent_child, elem); + free_pc (child); + } + } + /* Destroy the current process's page directory and switch back to the kernel-only page directory. */ pd = cur->pagedir; @@ -226,10 +315,66 @@ load (const char *file_name, void (**eip) (void), void **esp) goto done; } + size_t cmd_len, word_alignment; + char *esp_cmd, **esp_argv_first, **esp_argv_entry, ***esp_argv; + int *esp_argc, argc = 0; + + /* Copy passed command to start of user stack. */ + cmd_len = strlen (file_name); + esp_cmd = *esp; + esp_cmd -= cmd_len + 1; // +1 makes room for '\0' + for (i = 0; i < cmd_len; i++) { + esp_cmd[i] = file_name[i]; + } + esp_cmd[cmd_len] = '\0'; + + /* Tokenize passed command in-place. */ + // esp_cmd still points to first character of passed command + char *token, *save_ptr; + for (token = strtok_r (esp_cmd, " ", &save_ptr); token != NULL; + token = strtok_r (NULL, " ", &save_ptr)) + { + argc++; + } + + file_name = esp_cmd; + strlcpy (thread_current ()->name, file_name, 16); + + /* argv entries are pointers so they need to be aligned to the pointer size. */ + size_t psize = sizeof (char *); + word_alignment = (cmd_len + psize) / psize; + + /* Write argv entries. */ + esp_argv_entry = *esp; + esp_argv_entry -= word_alignment + argc + 1; // +1 makes room for argv[argc] = NULL + esp_argv_first = esp_argv_entry; // save for later + + // esp_cmd points to first entry so write directly + *esp_argv_entry = esp_cmd; + esp_argv_entry++; + + for (i = 0; i < argc - 1; i++) { + // step until a \0 is found + while (*(++esp_cmd) != '\0') {} + // step over any ' ' + while (*(++esp_cmd) == ' ') {} + *esp_argv_entry = esp_cmd; // point to next character + esp_argv_entry++; + } + + esp_argv = esp_argv_first - 1; + + *esp_argv = esp_argv_first; + + esp_argc = esp_argv - 1; + *esp_argc = argc; + + *esp = esp_argc - 1; // return address + /* Uncomment the following line to print some debug information. This will be useful when you debug the program stack.*/ -/*#define STACK_DEBUG*/ +// #define STACK_DEBUG #ifdef STACK_DEBUG printf("*esp is %p\nstack contents:\n", *esp); diff --git a/src/userprog/start_pfs.sh b/src/userprog/start_pfs.sh index 6ae76ad..26b9eb9 100644..100755 --- a/src/userprog/start_pfs.sh +++ b/src/userprog/start_pfs.sh @@ -1,4 +1,4 @@ -make -j4 +make -j12 cd build pintos-mkdisk fs.dsk 800 dd if=/dev/urandom of=random bs=1 count=100 @@ -7,4 +7,4 @@ pintos --qemu -v -p random -a random -- -q pintos --qemu -v -p ../../examples/pfs -a pfs -- -q pintos --qemu -v -p ../../examples/pfs_writer -a pfs_writer -- -q pintos --qemu -v -p ../../examples/pfs_reader -a pfs_reader -- -q -pintos --qemu -v -- run pfs +pintos --qemu -v -- -q run pfs diff --git a/src/userprog/start_recursor.sh b/src/userprog/start_recursor.sh index 22a69ac..6f5e99b 100644..100755 --- a/src/userprog/start_recursor.sh +++ b/src/userprog/start_recursor.sh @@ -1,6 +1,6 @@ -make -j4 -cd build +make -j12 +cd build pintos-mkdisk fs.dsk 400 -pintos --qemu -v -- -f -q -pintos --qemu -v -p ../../examples/recursor_ng -a recursor_ng -- -q -pintos --qemu -v -m 128 -- run "recursor_ng pintosmaster 6 1" +pintos -v --qemu -- -f -q +pintos -v --qemu -p ../../examples/recursor_ng -a recursor_ng -- -q +pintos -v --qemu -m 128 -- run "recursor_ng pintosmaster 6 1" diff --git a/src/userprog/syscall.c b/src/userprog/syscall.c index 370c89b..c1308ca 100644 --- a/src/userprog/syscall.c +++ b/src/userprog/syscall.c @@ -4,6 +4,17 @@ #include "threads/interrupt.h" #include "threads/thread.h" +#include "devices/input.h" +#include "filesys/file.h" +#include "filesys/filesys.h" +#include "filesys/off_t.h" +#include "lib/pid_t.h" +#include "userprog/pagedir.h" +#include "userprog/process.h" +#include "threads/init.h" +#include "threads/malloc.h" +#include "threads/vaddr.h" + static void syscall_handler (struct intr_frame *); void @@ -13,8 +24,327 @@ syscall_init (void) } static void -syscall_handler (struct intr_frame *f UNUSED) +halt (void) +{ + power_off(); +} + +static bool +create (const char *filename, off_t initial_size) +{ + return filesys_create (filename, initial_size); +} + +static bool +remove (const char *filename) +{ + return filesys_remove (filename); +} + +static int +open (const char *filename) +{ + struct thread *t = thread_current (); + + if (!t->fds) { + t->fds = (struct file **) calloc (MAX_FDS, sizeof (struct file *)); + } + + struct file *file = filesys_open (filename); + if (!file) { + return -1; + } + + for (int i = 0; i < MAX_FDS; i++) { + struct file **fd = t->fds + i; + if (*fd == NULL) { + *fd = file; + return i + 2; // 0 and 1 are reserved for stdin and stdout + } + } + free (file); + return -1; +} + +static struct file ** +get_fd (struct thread *thread, int fd_i) +{ + if (!thread->fds) { + return NULL; + } + + return thread->fds + fd_i - 2; // -2 since 0 and 1 are reserved and not present in the array +} + +static void +exit (int status) { - printf ("system call!\n"); + struct thread *thread = thread_current (); + + if (thread->fds) { + for (int i = 0; i < MAX_FDS; i++) { + struct file **fd = thread->fds + i; + if (fd && *fd) { + file_close (*fd); + *fd = NULL; + } + } + free (thread->fds); + } + + lock_acquire (&thread->parent->l); + thread->parent->exit_status = status; + lock_release (&thread->parent->l); + thread_exit (); } + +static int +wait (tid_t child_tid) +{ + return process_wait (child_tid); +} + +static int +read (int fd_i, void *buf, unsigned size) +{ + struct thread *thread = thread_current (); + + if (fd_i == 0) { + // stdin + unsigned i; + for (i = 0; i < size; i++) { + ((char *)buf)[i] = input_getc (); + } + return i; + } else if (fd_i == 1) { + // can't read from stdout + return -1; + } + + struct file **fd = get_fd (thread, fd_i); + if (fd && *fd) { + return file_read (*fd, buf, size); + } else { + return -1; + } +} + +static int +write (int fd_i, const void *buf, unsigned size) +{ + struct thread *thread = thread_current (); + + if (fd_i == 1) { + // stdout + putbuf ((const char *)buf, size); + return size; + } else if (fd_i == 0) { + // can't write to stdout + return -1; + } + + struct file **fd = get_fd (thread, fd_i); + if (fd && *fd) { + return file_write (*fd, buf, size); + } else { + return -1; + } +} + +static int +filesize (int fd_i) +{ + struct thread *thread = thread_current (); + struct file **fd = get_fd (thread, fd_i); + if (fd && *fd) { + return file_length (*fd); + } + return -1; +} + +static void +seek (int fd_i, unsigned position) +{ + struct thread *thread = thread_current (); + struct file **fd = get_fd (thread, fd_i); + if (fd && *fd) { + file_seek (*fd, position); + } +} + +static unsigned +tell (int fd_i) +{ + struct thread *thread = thread_current (); + struct file **fd = get_fd (thread, fd_i); + if (fd && *fd) { + return file_tell (*fd); + } + return 0; +} + + +static void +close (int fd_i) +{ + struct thread *thread = thread_current (); + + struct file **fd = get_fd (thread, fd_i); + if (fd && *fd) { + file_close (*fd); + *fd = NULL; + } +} + +static pid_t +exec (const char *filename) +{ + return process_execute (filename); +} + +static bool +ptr_is_valid (const void *ptr) +{ + // uintptr_t ptr = _ptr; + if (!is_user_vaddr (ptr)) + return false; + + struct thread *t = thread_current (); + + if (pagedir_get_page (t->pagedir, ptr) == NULL) + return false; + + return true; +} + +// cast argument N from f->esp to TYPE without dereferencing +#define INTR_ESP(N, TYPE) (TYPE *)(f->esp+(4*(N))) + +#define CHECK_PTR_AND_MAYBE_EXIT(PTR) \ + do { \ + if (!ptr_is_valid (PTR)) { \ + exit (-1); \ + return; \ + } \ + } while (0) + + +static void +syscall_handler (struct intr_frame *f UNUSED) +{ + // check esp + int *syscall_number = INTR_ESP (0, int); + + CHECK_PTR_AND_MAYBE_EXIT (syscall_number); + + char **filename; + int *status, *fd_i; + off_t *initial_size; + tid_t *child_tid; + unsigned *size, *position; + void **buf; + + switch (*syscall_number) { + case 0: + // halt + halt (); + break; + case 1: + // exit + status = INTR_ESP (1, int); + CHECK_PTR_AND_MAYBE_EXIT (status); + exit (*status); + break; + case 2: + // exec + filename = INTR_ESP (1, char *); + CHECK_PTR_AND_MAYBE_EXIT (filename); + CHECK_PTR_AND_MAYBE_EXIT (*filename); + f->eax = exec (*filename); + break; + case 3: + // wait + child_tid = INTR_ESP (1, tid_t); + CHECK_PTR_AND_MAYBE_EXIT (child_tid); + f->eax = wait (*child_tid); + break; + case 4: + // create + filename = INTR_ESP (1, char *); + initial_size = INTR_ESP (2, off_t); + CHECK_PTR_AND_MAYBE_EXIT (filename); + CHECK_PTR_AND_MAYBE_EXIT (*filename); + CHECK_PTR_AND_MAYBE_EXIT (initial_size); + f->eax = create (*filename, *initial_size); + break; + case 5: + // remove + filename = INTR_ESP (1, char *); + CHECK_PTR_AND_MAYBE_EXIT (filename); + CHECK_PTR_AND_MAYBE_EXIT (*filename); + f->eax = remove (*filename); + break; + case 6: + // open + filename = INTR_ESP (1, char *); + CHECK_PTR_AND_MAYBE_EXIT (filename); + CHECK_PTR_AND_MAYBE_EXIT (*filename); + f->eax = open (*filename); + break; + case 7: + // filesize + fd_i = INTR_ESP (1, int); + CHECK_PTR_AND_MAYBE_EXIT (fd_i); + f->eax = filesize (*fd_i); + break; + case 8: + // read + fd_i = INTR_ESP (1, int); + buf = INTR_ESP (2, void *); + size = INTR_ESP (3, unsigned); + CHECK_PTR_AND_MAYBE_EXIT (fd_i); + CHECK_PTR_AND_MAYBE_EXIT (buf); + CHECK_PTR_AND_MAYBE_EXIT (*buf); + CHECK_PTR_AND_MAYBE_EXIT (size); + CHECK_PTR_AND_MAYBE_EXIT (*buf + *size); + f->eax = read (*fd_i, *buf, *size); + break; + case 9: + // write + fd_i = INTR_ESP (1, int); + buf = INTR_ESP (2, void *); + size = INTR_ESP (3, unsigned); + CHECK_PTR_AND_MAYBE_EXIT (fd_i); + CHECK_PTR_AND_MAYBE_EXIT (buf); + CHECK_PTR_AND_MAYBE_EXIT (*buf); + CHECK_PTR_AND_MAYBE_EXIT (size); + CHECK_PTR_AND_MAYBE_EXIT (*buf + *size); + f->eax = write (*fd_i, *buf, *size); + break; + case 10: + // seek + fd_i = INTR_ESP (1, int); + position = INTR_ESP (2, unsigned); + CHECK_PTR_AND_MAYBE_EXIT (fd_i); + CHECK_PTR_AND_MAYBE_EXIT (position); + seek (*fd_i, *position); + break; + case 11: + // tell + fd_i = INTR_ESP (1, int); + CHECK_PTR_AND_MAYBE_EXIT (fd_i); + f->eax = tell (*fd_i); + break; + case 12: + // close + fd_i = INTR_ESP (1, int); + close (*fd_i); + break; + default: + printf ("kernel: unknown syscall '%d'\n", *syscall_number); + break; + } +} + +#undef INTR_ESP |
