CSCC69: Operating Systems
Assignment 1
Some of the slides were borrowed from csc369 course, U of T, St George
• Locks
– Provide mutual exclusion
– 2 operations: acquire() and release()
• Semaphores
– Generalize locks with an integer count variable and a thread
– 2 operations: wait() and signal()
– If the integer count is negative, threads wait in a queue until
another thread signals the semaphore
• Monitors
– An abstraction that encapsulates shared data and operations on
it in such a way that only a single process at a time may be
executing “in” the monitor
More on Monitors
• Programmer defines the scope of the monitor
– ie: which data is “monitored”
• Local data can be accessed only by the monitor’s
procedures (not by any external procedures
• Other processes that attempt to enter the
monitor are blocked. They must first acquire the
lock before becoming active in the monitor.
• Only a single process executes inside the monitor.
Assignment 1 – Task 2
• The thread system provides interrupts, control
functions, and semaphores.
• You will be extending the thread system of
OS161 to provide additional, higher-level
functions found in most thread libraries and
will use that functionality to implement the
process management system calls.
• OS/161's threads are “processes”.
• Each thread has a pidinfo block
– pid of this thread, pid of parent, exit status,…
• All of the blocks are stored in a table
– Access to this table must be protected since it is a
shared resource
– Global lock? Individual locks?
– pid.c in src/kern/thread serves as a monitor to do
exactly this
Assignment 1- Task 2
• The starter code includes a simple thread id management
system (see pid.c).
• The system allocates an identifier a (pid) whenever a new
thread is created.
• Some of the functions that allow programmers to access
and use pids have been written.
• You should think of pid.c as implementing a monitor that
controls access to the shared pid data structures.
• The externally-callable functions in pid.c are the monitor's
entry points.
• All access to the pid data structures (including querying or
modifying any fields) must be done through a function
provided by pid.c.
Task 2
• Read the specification and manual carefully
• Think before you implement to achieve the
correct solution.
• Add other variable/helper functions as you
• Look at pid.c/pid.h and thread.c/thread.h
• src/kern/arch/mips/locore/trap.c contains
code for newly admitted processes.
Task 2
• int thread_join(pid_t pid, int *status)
thread_join suspends the execution of the calling thread until the thread
identified by pid terminates by calling thread_exit
• Errors:
– ESRCH: No thread could be found corresponding to that specified by pid.
– EINVAL: The thread has been detached.
– EINVAL: The caller of thread_join is not the parent of the thread specified by
– EINVAL: The pid argument refers to the calling thread.
• The man pages in the OS/161 distribution contain a description of the
error return values that you must return.
• If there are conditions that can happen that are not listed in the man
page, consider adding one.
– Note that if you add an error code to kern/include/kern/errno.h, you need to
add a corresponding error message to the file user/lib/libc/string/strerror.c.
System calls for accessing pid - user level interface
A pid, is a unique number that identifies a process.
getpid() returns the pid of the current process
waitpid() causes current process to wait on another process
to exit, and get their exit status
• Use the kernel threading functionalities you implement
• The implementation of getpid() should be straightforward,
assuming you have already taken care of allocating thread
identifiers when a new thread is created
• Your waitpid() system call should map nicely to the
thread_join function in the kernel thread system.
• fork() is the mechanism for creating new
• It should make a copy of the invoking process and
make sure that the parent and child processes
each observe the correct return value (that is, 0
for the child and the newly created pid for the
• The core of the fork() system call will use the
thread_fork() function to create a new kernel
System Calls
• User level applications are isolated from the OS in a
different protection domain
• System calls are a way of requesting the services of the
– Method calls defined in user-level libraries “trap” from
user-space to the OS in kernel-space
• Once control traps to the kernel, a kernel-level system
call handler is invoked to service the system call and
return the appropriate results to user-space
• Once the system call is complete, control is given back
to the user-level application
Trap Frame
• The trap frame is used to pass information about
the exception, storing the current processor state
for later execution
• The trap handler determines which syscall is
being requested by looking at the corresponding
code in the trap frame struct (tf->tf_a0)
• Once the syscall finishes executing, the return
value or error code is put back into the trap
frame (tf->tf_v0)
• Read the code: taking a look at the whole call
stack to get a better idea
OS161 System Call Example: time
Creating a New System Call in OS161
• Hint: take a look at what has been done for sys___time system call.
You can follow the same pattern.
• Define the new system call code in src/kern/include/kern/syscall.h
• Define the prototypes for the new syscall
– Kernel space: src/kern/include/syscall.h
– User space: src/user/include
• Write the source code for it in src/kern/syscall/proc_syscalls.c
• If necessary, define any new error codes in
• Complete the cases in the handler switch statement in
• Test your system calls with programs in user/testbin
• Rebuild both kernel and user level program
_exit System Call
• exit only take on integer argument.
• All we need to do is find our struct process
entry using curthread->t_pid.
• Then indicate that “I’ve exited” and fill the
exitcode. The only thing to note that the
exitcode must be made using the MACROs in
_exit - terminate process
Standard C Library (libc, -lc)
#include <unistd.h>
_exit(int exitcode);
Cause the current process to exit. The exit code exitcode is reported back to other
process(es) via the waitpid() call. The process id of the exiting process should not
be reused until all processes interested in collecting the exit code with waitpid
have done so. (What "interested" means is intentionally left vague; you should
design this.)
Return Values
_exit does not return.

similar documents