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
......@@ -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