CS 134

Phase 1: Basic File and Process System Calls

Overall Design

Openfile:

We first design openfile to be a struct that contains a pointer to a vnode (representaion of an open file in the OS), a file offset to know where the file is stored, flags for whether a file is read only or not, a lock to protect concurrent access to the file, and a counter variable for the number of references to that open file. The open files need corresponding create, destory increment and decrement functions to handle updating or destroying the state, and incrementing the counter for the number of things accessing that open file.

Filetable:

We designed the file table to contain a list of open files. We added corresponding init function to intialize the state, destroy to free memory, and add which finds the first available file descriptor. We also defined a constant to determine the size of the array of files as the maximum possible number of files that can be open at one instance. Note: each process needs access to the file table, so we add a pointer to the file table in proc.

System Calls

open

  • SYS_open is 45, has 2 arguments: filename and flags (defined in unistd.h)
  • Returns -1 on error and always uses the lowest available file descriptor
  • Implement in file_syscalls.c
  • Copy file name from user space to kernel using copyinstr
  • Use the vfs_open to get the vnode
  • Allocate a file descriptor (helper function)
  • Iterator through the available file descriptors, and assign the filetable at that fd
  • Return the file descriptor

read

  • SYS_read is 50, has 3 arguments: filehandle, buf, size (where size is the length of the buffer)
  • Validate file descriptor (ensure that it is between 0 and the max capacity of the file table)
  • Check if file is open so you can read it
  • Create new uio for reading uio_uinit
  • Acquire the lock
  • Use VOP_READ
  • Update file offset
  • Release the lock
  • Return the number of bytes read

write

  • sys_write has 3 arguments: filehandle, buf, size (where size is the length of the buffer)
  • Validate file is open (by checking that the count is not 0)
  • Create new uio
  • Acquire the lock
  • Update file offset
  • Use VOP_WRITE
  • Release the lock
  • Return the number of bytes written

lseek

  • Make sure current offset in correct place
  • Check if offset + pos > end of file (if so return end)
  • Acquire the lock
  • Update the offset from pos += code
  • Release lock
  • Return new offset position

close

  • Validate file is open
  • Acquire the lock
  • Use vfs_close
  • Decrement the counter
  • Destroy the openfile and clear spot in filetable if count is 0
  • Release the lock
  • Get return value (return 0 on success, error)

dup2

  • Takes in old and new fd
  • Check if old has value
  • Check if new fd is open and available
  • Create new pointer to what the filehandle points too (in new handle spot in file table)

chdir

  • Allocate memory for filepath on the kernel stack
  • Copy filepath from user stack to kernel stack using copyinstr
  • Take the path from the kernel stack and pass it into vfs_chdir

__getcwd

  • Create a uio to hold the buffer that holds the file path
  • Then call vfs_getcwd passing in the uio

fork (preliminary without PID support)

  • Create a new entry in the process table
  • Duplicate filetable into space created using as_copy
  • Duplicate file descriptors
  • Copy trapframe
  • thread_fork(process name, proc, trrapframe as function) to create new struct proc
  • Call enter_forked_process(trapframe)
  • return 1 to the parent (rather than the child's PID)

_exit (preliminary without PID support)

  • Filetable_decrement for all
  • Remove entry from process table
  • Call proc_exit?
  • Free memory for the process table

Phase 2: Advanced Process-Related System Calls

Implementing Process IDs

  • We will have a process table that contains pid entries
  • Each pid entry contains a:
    • boolean is_free value to determine if the slot is free
    • unsigned short generation that is used to create new PIDs to ensure we aren't recycling new PIDs too quickly
    • struct proc info storing the process object

System Calls

getpid

  • PID is in the process table in pid.h
  • Here we will return the PID of the calling process from the process table

Revised fork

  • Now, the parent process must return the PID of the new process, and the child process stil returns 0

execv

  • Take new desired function from user space into kernel space
  • Create a new executable for the new program using as_create
  • Replace the existing address space with a new one for the new executable we created
  • Run it
  • Then move the output from the kernel space back into the user space

waitpid

  • Calling process is put to "sleep" by putting it in a while loop until the child process exits
  • waitpid returns the exit status of the child
  • The proc_info object is destroyed
  • Free the process table entry

_exit

  • Terminates the calling process using thread_exit and proc_remthread (because we only have a single-threaded program)
  • Records an exit status in proc_info

(When logged in, completion status appears here.)