diff options
Diffstat (limited to 'src/userprog/process.c')
| -rw-r--r-- | src/userprog/process.c | 177 |
1 files changed, 161 insertions, 16 deletions
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); |
